图像模型
This commit is contained in:
@@ -156,6 +156,11 @@ public class PasswordUtil {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -140,6 +140,11 @@ public class ServiceManager {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -117,6 +117,11 @@ public class AliyunConfig {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
652
src/main/java/com/rj/controller/ImageModelController.java
Normal file
652
src/main/java/com/rj/controller/ImageModelController.java
Normal file
@@ -0,0 +1,652 @@
|
||||
package com.rj.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.rj.entity.ImageModel;
|
||||
import com.rj.service.IImageModelService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 图像模型控制器
|
||||
*
|
||||
* 提供图像模型相关的RESTful API接口,包括:
|
||||
* - 图像模型记录的创建、查询、更新、删除操作
|
||||
* - 分页查询和条件查询功能
|
||||
* - 根据各种条件查询图像模型记录
|
||||
* - 统一的响应格式和异常处理
|
||||
*
|
||||
* 使用Swagger注解进行API文档生成,支持在线API测试。
|
||||
* 所有接口都包含完整的参数验证、异常处理和日志记录。
|
||||
*
|
||||
* @author 系统生成
|
||||
* @date 2025/1/30
|
||||
* @version 1.0
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/image-model")
|
||||
@Tag(name = "图像模型管理", description = "图像模型相关接口")
|
||||
public class ImageModelController {
|
||||
|
||||
/**
|
||||
* 图像模型服务接口
|
||||
* 用于处理图像模型相关的业务逻辑
|
||||
*/
|
||||
@Autowired
|
||||
private IImageModelService imageModelService;
|
||||
|
||||
/**
|
||||
* 图像模型请求DTO
|
||||
*
|
||||
* 用于接收创建图像模型记录的请求参数。
|
||||
* 包含所有必要的字段验证,确保数据的完整性和有效性。
|
||||
*/
|
||||
@Data
|
||||
public static class ImageModelRequest {
|
||||
/**
|
||||
* 图像名称,必填字段
|
||||
*/
|
||||
@NotBlank(message = "图像名称不能为空")
|
||||
private String imageName;
|
||||
|
||||
private String uuid;
|
||||
/**
|
||||
* 模型名称,必填字段
|
||||
*/
|
||||
@NotBlank(message = "模型名称不能为空")
|
||||
private String modelName;
|
||||
|
||||
/**
|
||||
* 所属人姓名,必填字段
|
||||
*/
|
||||
@NotBlank(message = "所属人姓名不能为空")
|
||||
private String ownerName;
|
||||
|
||||
/**
|
||||
* 所属人电话,必填字段
|
||||
*/
|
||||
@NotBlank(message = "所属人电话不能为空")
|
||||
private String ownerPhone;
|
||||
|
||||
/**
|
||||
* 图片类型,必填字段
|
||||
*/
|
||||
@NotBlank(message = "图片类型不能为空")
|
||||
private String imageType;
|
||||
|
||||
/**
|
||||
* 素材URL,可选字段
|
||||
*/
|
||||
private String materialUrl;
|
||||
|
||||
/**
|
||||
* 素材临时URL,可选字段
|
||||
*/
|
||||
private String materialTempUrl;
|
||||
|
||||
/**
|
||||
* 成品图片URL,可选字段
|
||||
*/
|
||||
private String resultImageUrl;
|
||||
|
||||
/**
|
||||
* 成品图片临时URL,可选字段
|
||||
*/
|
||||
private String resultImageTempUrl;
|
||||
|
||||
/**
|
||||
* 提示词,可选字段
|
||||
*/
|
||||
private String prompt;
|
||||
|
||||
/**
|
||||
* 功能函数名称,可选字段
|
||||
*/
|
||||
private String functionName;
|
||||
|
||||
/**
|
||||
* 放大倍数,可选字段
|
||||
*/
|
||||
private java.math.BigDecimal scaleFactor;
|
||||
|
||||
/**
|
||||
* 请求ID,可选字段
|
||||
*/
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 任务ID,可选字段
|
||||
*/
|
||||
private String taskId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询请求DTO
|
||||
*
|
||||
* 用于接收分页查询图像模型记录的请求参数。
|
||||
* 支持多种条件筛选,包括所属人信息、模型名称、图片类型等。
|
||||
*/
|
||||
@Data
|
||||
public static class PageRequest {
|
||||
/**
|
||||
* 当前页码,默认为1
|
||||
*/
|
||||
private Integer current = 1;
|
||||
|
||||
/**
|
||||
* 每页大小,默认为10
|
||||
*/
|
||||
private Integer size = 10;
|
||||
|
||||
/**
|
||||
* 所属人姓名,支持模糊查询
|
||||
*/
|
||||
private String ownerName;
|
||||
|
||||
/**
|
||||
* 所属人电话,支持模糊查询
|
||||
*/
|
||||
private String ownerPhone;
|
||||
|
||||
/**
|
||||
* 模型名称,支持模糊查询
|
||||
*/
|
||||
private String modelName;
|
||||
|
||||
/**
|
||||
* 图片类型,精确匹配
|
||||
*/
|
||||
private String imageType;
|
||||
|
||||
/**
|
||||
* 开始时间,用于时间范围查询
|
||||
*/
|
||||
private String startTime;
|
||||
|
||||
/**
|
||||
* 结束时间,用于时间范围查询
|
||||
*/
|
||||
private String endTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建图像模型记录
|
||||
*
|
||||
* 接收图像模型请求参数,创建新的图像模型记录并保存到数据库。
|
||||
* 该方法会进行参数验证、数据转换、业务处理等完整流程。
|
||||
*
|
||||
* @param request 图像模型请求参数,包含所有必要的字段信息
|
||||
* @return ResponseEntity 包含操作结果的响应实体
|
||||
* - success: 操作是否成功
|
||||
* - message: 操作结果消息
|
||||
* - data: 创建的图像模型记录(成功时)
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建图像模型记录", description = "创建新的图像模型记录")
|
||||
public ResponseEntity<Map<String, Object>> createImageModel(@Valid @RequestBody ImageModelRequest request) {
|
||||
try {
|
||||
log.info("创建图像模型记录开始,参数:{}", request);
|
||||
|
||||
// 构建图像模型对象,使用属性拷贝简化代码
|
||||
ImageModel imageModel = new ImageModel();
|
||||
BeanUtils.copyProperties(request, imageModel);
|
||||
imageModel.setCreateTime(LocalDateTime.now());
|
||||
imageModel.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
boolean result = imageModelService.saveImageModel(imageModel);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
if (result) {
|
||||
response.put("success", true);
|
||||
response.put("message", "图像模型记录创建成功");
|
||||
response.put("data", imageModel);
|
||||
log.info("图像模型记录创建成功,ID: {}", imageModel.getUuid());
|
||||
} else {
|
||||
response.put("success", false);
|
||||
response.put("message", "图像模型记录创建失败");
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("创建图像模型记录异常", e);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "创建图像模型记录异常: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据UUID删除图像模型记录
|
||||
*
|
||||
* 通过唯一标识符删除对应的图像模型记录。
|
||||
* UUID是主键,删除操作是幂等的。
|
||||
*
|
||||
* @param uuid 唯一标识符,对应数据库主键
|
||||
* @return ResponseEntity 包含删除结果的响应实体
|
||||
* - success: 删除是否成功
|
||||
* - message: 删除结果消息
|
||||
*/
|
||||
@DeleteMapping("/delete/{uuid}")
|
||||
@Operation(summary = "根据UUID删除图像模型记录", description = "根据唯一标识符删除图像模型记录")
|
||||
public ResponseEntity<Map<String, Object>> deleteImageModelByUuid(
|
||||
@Parameter(description = "唯一标识符") @PathVariable String uuid) {
|
||||
try {
|
||||
log.info("根据UUID删除图像模型记录,uuid: {}", uuid);
|
||||
|
||||
boolean result = imageModelService.removeByUuid(uuid);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
if (result) {
|
||||
response.put("success", true);
|
||||
response.put("message", "图像模型记录删除成功");
|
||||
} else {
|
||||
response.put("success", false);
|
||||
response.put("message", "未找到对应的图像模型记录或删除失败");
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("根据UUID删除图像模型记录异常", e);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "删除异常: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除图像中的中英文字符或常见水印
|
||||
*
|
||||
* 调用AI模型去除图像中的水印,支持中英文字符和常见水印的检测与去除。
|
||||
* 使用场景包括:素材二次处理、广告图净化、图片去水印等。
|
||||
*
|
||||
* @param request 图像模型请求参数,包含素材URL和处理参数
|
||||
* @return ResponseEntity 包含处理结果的响应实体
|
||||
* - success: 处理是否成功
|
||||
* - message: 处理结果消息
|
||||
* - data: 处理后的图像模型记录(包含结果URL)
|
||||
*/
|
||||
@PostMapping("/removeWatermar")
|
||||
@Operation(summary = "去除图像中的中英文字符或常见水印", description = "去除图像中的中英文字符或常见水印。使用场景包括:素材二次处理、广告图净化等")
|
||||
public ResponseEntity<Map<String, Object>> removeWatermar(@Valid @RequestBody ImageModelRequest request) {
|
||||
try {
|
||||
log.info("去除图像中的中英文字符或常见水印开始,参数:{}", request);
|
||||
|
||||
// 构建图像模型对象,使用属性拷贝简化代码
|
||||
ImageModel imageModel = new ImageModel();
|
||||
BeanUtils.copyProperties(request, imageModel);
|
||||
imageModel.setCreateTime(LocalDateTime.now());
|
||||
imageModel.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
// 调用服务层进行水印去除处理
|
||||
ImageModel result = imageModelService.removeWatermar(imageModel);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "水印去除处理成功");
|
||||
response.put("data", result);
|
||||
log.info("水印去除处理成功,ID: {}, 结果URL: {}",
|
||||
result.getUuid(), result.getResultImageUrl());
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("去除图像水印异常", e);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "去除图像水印失败: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片素材
|
||||
*
|
||||
* 支持图片文件上传,包括文件验证、MinIO存储、URL生成和数据库记录保存。
|
||||
* 支持多种图片格式:jpg, jpeg, png, gif, bmp, webp
|
||||
* 文件大小限制:10MB
|
||||
*
|
||||
* @param file 上传的图片文件
|
||||
* @param ownerName 所属人姓名
|
||||
* @param ownerPhone 所属人电话
|
||||
* @return ResponseEntity 包含上传结果的响应实体
|
||||
* - success: 上传是否成功
|
||||
* - message: 上传结果消息
|
||||
* - data: 图像模型记录(包含永久URL和临时URL)
|
||||
*/
|
||||
@PostMapping("/upload")
|
||||
@Operation(summary = "上传图片素材", description = "上传图片素材到MinIO存储,生成永久URL和临时URL,并保存记录到数据库")
|
||||
public ResponseEntity<Map<String, Object>> uploadImageMaterial(
|
||||
@Parameter(description = "图片文件") @RequestParam("file") MultipartFile file,
|
||||
@Parameter(description = "所属人姓名") @RequestParam("ownerName") String ownerName,
|
||||
@Parameter(description = "所属人电话") @RequestParam("ownerPhone") String ownerPhone,
|
||||
@Parameter(description = "素材名称") @RequestParam("imageName") String imageName) {
|
||||
try {
|
||||
String imageType = "material"; // 默认上传的全部是素材。
|
||||
log.info("开始上传图片素材,文件名:{},所属人:{},电话:{},类型:{}",
|
||||
file.getOriginalFilename(), ownerName, ownerPhone, imageType);
|
||||
|
||||
// 参数验证
|
||||
if (ownerName == null || ownerName.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("所属人姓名不能为空");
|
||||
}
|
||||
if (ownerPhone == null || ownerPhone.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("所属人电话不能为空");
|
||||
}
|
||||
if (imageType == null || imageType.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("图片类型不能为空");
|
||||
}
|
||||
|
||||
// 调用服务层进行图片上传
|
||||
ImageModel result = imageModelService.uploadImageMaterial(file, ownerName, ownerPhone, imageType,imageName);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "图片素材上传成功");
|
||||
response.put("data", result);
|
||||
log.info("图片素材上传成功,ID: {},永久URL: {},临时URL: {}",
|
||||
result.getUuid(), result.getMaterialUrl(), result.getMaterialTempUrl());
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("上传图片素材异常", e);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "上传图片素材失败: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据UUID查询图像模型记录
|
||||
*
|
||||
* 通过唯一标识符查询对应的图像模型记录。
|
||||
* UUID是主键,查询结果唯一且准确。
|
||||
*
|
||||
* @param uuid 唯一标识符,对应数据库主键
|
||||
* @return ResponseEntity 包含查询结果的响应实体
|
||||
* - success: 查询是否成功
|
||||
* - message: 查询结果消息
|
||||
* - data: 图像模型记录(找到时)
|
||||
*/
|
||||
@GetMapping("/get/{uuid}")
|
||||
@Operation(summary = "根据UUID查询图像模型记录", description = "根据唯一标识符查询图像模型记录")
|
||||
public ResponseEntity<Map<String, Object>> getImageModelByUuid(
|
||||
@Parameter(description = "唯一标识符") @PathVariable String uuid) {
|
||||
try {
|
||||
log.info("根据UUID查询图像模型记录,UUID: {}", uuid);
|
||||
|
||||
ImageModel imageModel = imageModelService.getByUuid(uuid);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
if (imageModel != null) {
|
||||
response.put("success", true);
|
||||
response.put("message", "查询成功");
|
||||
response.put("data", imageModel);
|
||||
} else {
|
||||
response.put("success", false);
|
||||
response.put("message", "未找到对应的图像模型记录");
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("根据UUID查询图像模型记录异常", e);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "查询异常: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询图像模型记录
|
||||
*
|
||||
* 支持多种条件的分页查询功能,包括:
|
||||
* - 按所属人姓名、电话模糊查询
|
||||
* - 按模型名称模糊查询
|
||||
* - 按图片类型精确查询
|
||||
* - 按时间范围查询
|
||||
*
|
||||
* @param request 分页查询请求参数,包含查询条件和分页信息
|
||||
* @return ResponseEntity 包含分页查询结果的响应实体
|
||||
* - success: 查询是否成功
|
||||
* - message: 查询结果消息
|
||||
* - data: 分页结果(包含记录列表和分页信息)
|
||||
*/
|
||||
|
||||
@Operation(summary = "分页查询图像模型记录", description = "分页查询图像模型记录")
|
||||
public ResponseEntity<Map<String, Object>> pageImageModel(@RequestBody PageRequest request) {
|
||||
try {
|
||||
log.info("分页查询图像模型记录开始,参数:{}", request);
|
||||
|
||||
Page<ImageModel> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<ImageModel> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
if (request.getOwnerName() != null && !request.getOwnerName().trim().isEmpty()) {
|
||||
queryWrapper.like(ImageModel::getOwnerName, request.getOwnerName());
|
||||
}
|
||||
if (request.getOwnerPhone() != null && !request.getOwnerPhone().trim().isEmpty()) {
|
||||
queryWrapper.like(ImageModel::getOwnerPhone, request.getOwnerPhone());
|
||||
}
|
||||
if (request.getModelName() != null && !request.getModelName().trim().isEmpty()) {
|
||||
queryWrapper.like(ImageModel::getModelName, request.getModelName());
|
||||
}
|
||||
if (request.getImageType() != null && !request.getImageType().trim().isEmpty()) {
|
||||
queryWrapper.eq(ImageModel::getImageType, request.getImageType());
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc(ImageModel::getCreateTime);
|
||||
|
||||
Page<ImageModel> result = imageModelService.page(page, queryWrapper);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", true);
|
||||
response.put("message", "查询成功");
|
||||
response.put("data", result);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("分页查询图像模型记录异常", e);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "查询异常: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询图像模型列表
|
||||
*
|
||||
* 参考face-detect/logs接口实现,返回标准格式的分页查询结果,包含:
|
||||
* - total: 总记录数
|
||||
* - current: 当前页码
|
||||
* - pages: 总页数
|
||||
* - data: 数据列表
|
||||
* - size: 每页大小
|
||||
* - success: 操作是否成功
|
||||
* - message: 响应消息
|
||||
*
|
||||
* @param current 当前页码,默认为1
|
||||
* @param size 每页大小,默认为10
|
||||
* @param ownerName 所属人姓名,支持模糊查询
|
||||
* @param ownerPhone 所属人电话,支持模糊查询
|
||||
* @param modelName 模型名称,支持模糊查询
|
||||
* @param imageType 图片类型,精确匹配
|
||||
* @param startTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return ResponseEntity 包含标准格式分页查询结果的响应实体
|
||||
*/
|
||||
@PostMapping("/page")
|
||||
@Operation(summary = "分页查询图像模型列表", description = "分页查询图像模型列表,返回标准格式")
|
||||
public ResponseEntity<Map<String, Object>> listImageModel(
|
||||
@Parameter(description = "页码", example = "1")
|
||||
@RequestParam(defaultValue = "1") Integer current,
|
||||
@Parameter(description = "每页大小", example = "10")
|
||||
@RequestParam(defaultValue = "10") Integer size,
|
||||
@Parameter(description = "所属人姓名")
|
||||
@RequestParam(required = false) String ownerName,
|
||||
@Parameter(description = "图像名称")
|
||||
@RequestParam(required = false) String imageName,
|
||||
@Parameter(description = "所属人电话")
|
||||
@RequestParam(required = false) String ownerPhone,
|
||||
@Parameter(description = "模型名称")
|
||||
@RequestParam(required = false) String modelName,
|
||||
@Parameter(description = "图片类型")
|
||||
@RequestParam(required = false) String imageType,
|
||||
@Parameter(description = "开始时间")
|
||||
@RequestParam(required = false) String startTime,
|
||||
@Parameter(description = "结束时间")
|
||||
@RequestParam(required = false) String endTime) {
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
log.info("分页查询图像模型列表开始,参数:current={}, size={}, ownerName={}, ownerPhone={}, modelName={}, imageType={}, startTime={}, endTime={}",
|
||||
current, size, ownerName, ownerPhone, modelName, imageType, startTime, endTime);
|
||||
|
||||
Page<ImageModel> page = new Page<>(current, size);
|
||||
LambdaQueryWrapper<ImageModel> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 构建查询条件
|
||||
if (ownerName != null && !ownerName.trim().isEmpty()) {
|
||||
queryWrapper.like(ImageModel::getOwnerName, ownerName);
|
||||
}
|
||||
if (ownerPhone != null && !ownerPhone.trim().isEmpty()) {
|
||||
queryWrapper.like(ImageModel::getOwnerPhone, ownerPhone);
|
||||
}
|
||||
if (modelName != null && !modelName.trim().isEmpty()) {
|
||||
queryWrapper.like(ImageModel::getModelName, modelName);
|
||||
}
|
||||
if (imageType != null && !imageType.trim().isEmpty()) {
|
||||
queryWrapper.eq(ImageModel::getImageType, imageType);
|
||||
}
|
||||
if (startTime != null && !startTime.trim().isEmpty()) {
|
||||
queryWrapper.ge(ImageModel::getCreateTime, startTime);
|
||||
}
|
||||
if (endTime != null && !endTime.trim().isEmpty()) {
|
||||
queryWrapper.le(ImageModel::getCreateTime, endTime);
|
||||
}
|
||||
|
||||
// 按创建时间降序排列
|
||||
queryWrapper.orderByDesc(ImageModel::getUpdateTime);
|
||||
|
||||
// 执行分页查询
|
||||
Page<ImageModel> pageResult = imageModelService.page(page, queryWrapper);
|
||||
|
||||
// 构建标准格式响应
|
||||
result.put("success", true);
|
||||
result.put("message", "查询成功");
|
||||
result.put("data", pageResult.getRecords());
|
||||
result.put("total", pageResult.getTotal());
|
||||
result.put("current", pageResult.getCurrent());
|
||||
result.put("size", pageResult.getSize());
|
||||
result.put("pages", pageResult.getPages());
|
||||
|
||||
return ResponseEntity.ok(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("分页查询图像模型列表异常", e);
|
||||
result.put("success", false);
|
||||
result.put("message", "查询异常: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据请求ID查询图像模型记录
|
||||
*
|
||||
* 通过请求ID查询对应的图像模型记录。
|
||||
* 一个请求ID可能对应多条记录,此方法返回最新的一条。
|
||||
* 主要用于追踪特定请求的处理结果。
|
||||
*
|
||||
* @param requestId 请求ID,用于追踪处理请求
|
||||
* @return ResponseEntity 包含查询结果的响应实体
|
||||
* - success: 查询是否成功
|
||||
* - message: 查询结果消息
|
||||
* - data: 图像模型记录(找到时)
|
||||
*/
|
||||
@GetMapping("/get-by-request/{requestId}")
|
||||
@Operation(summary = "根据请求ID查询图像模型记录", description = "根据请求ID查询图像模型记录")
|
||||
public ResponseEntity<Map<String, Object>> getImageModelByRequestId(
|
||||
@Parameter(description = "请求ID") @PathVariable String requestId) {
|
||||
try {
|
||||
log.info("根据请求ID查询图像模型记录,requestId: {}", requestId);
|
||||
|
||||
ImageModel imageModel = imageModelService.getByRequestId(requestId);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
if (imageModel != null) {
|
||||
response.put("success", true);
|
||||
response.put("message", "查询成功");
|
||||
response.put("data", imageModel);
|
||||
} else {
|
||||
response.put("success", false);
|
||||
response.put("message", "未找到对应的图像模型记录");
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("根据请求ID查询图像模型记录异常", e);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "查询异常: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务ID查询图像模型记录
|
||||
*
|
||||
* 通过任务ID查询对应的图像模型记录。
|
||||
* 一个任务ID可能对应多条记录,此方法返回最新的一条。
|
||||
* 主要用于查询异步处理任务的结果。
|
||||
*
|
||||
* @param taskId 任务ID,用于追踪异步处理任务
|
||||
* @return ResponseEntity 包含查询结果的响应实体
|
||||
* - success: 查询是否成功
|
||||
* - message: 查询结果消息
|
||||
* - data: 图像模型记录(找到时)
|
||||
*/
|
||||
@GetMapping("/get-by-task/{taskId}")
|
||||
@Operation(summary = "根据任务ID查询图像模型记录", description = "根据任务ID查询图像模型记录")
|
||||
public ResponseEntity<Map<String, Object>> getImageModelByTaskId(
|
||||
@Parameter(description = "任务ID") @PathVariable String taskId) {
|
||||
try {
|
||||
log.info("根据任务ID查询图像模型记录,taskId: {}", taskId);
|
||||
|
||||
ImageModel imageModel = imageModelService.getByTaskId(taskId);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
if (imageModel != null) {
|
||||
response.put("success", true);
|
||||
response.put("message", "查询成功");
|
||||
response.put("data", imageModel);
|
||||
} else {
|
||||
response.put("success", false);
|
||||
response.put("message", "未找到对应的图像模型记录");
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("根据任务ID查询图像模型记录异常", e);
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("success", false);
|
||||
response.put("message", "查询异常: " + e.getMessage());
|
||||
return ResponseEntity.status(500).body(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -63,12 +63,19 @@ public class TextModelController {
|
||||
*/
|
||||
@Data
|
||||
public static class LegalRequest {
|
||||
@NotBlank(message = "法律问题不能为空")
|
||||
private String question;
|
||||
@NotBlank(message = "咨询目的不能为空")
|
||||
private String consultationPurpose;
|
||||
|
||||
private String category;
|
||||
private String jurisdiction;
|
||||
@NotBlank(message = "大模型名称")
|
||||
@NotBlank(message = "咨询类型不能为空")
|
||||
private String consultationType;
|
||||
|
||||
@NotBlank(message = "核心诉求不能为空")
|
||||
private String coreDemand;
|
||||
|
||||
@NotBlank(message = "咨询详细内容不能为空")
|
||||
private String inputText;
|
||||
|
||||
@NotBlank(message = "大模型名称不能为空")
|
||||
private String model;
|
||||
}
|
||||
|
||||
@@ -142,24 +149,17 @@ public class TextModelController {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
try {
|
||||
log.info("开始法律咨询,问题类别: {}, 管辖区域: {}, 问题长度: {}",
|
||||
request.getCategory(), request.getJurisdiction(), request.getQuestion().length());
|
||||
log.info("开始法律咨询,咨询目的: {}, 咨询类型: {}, 核心诉求: {}, 内容长度: {}",
|
||||
request.getConsultationPurpose(), request.getConsultationType(),
|
||||
request.getCoreDemand(), request.getInputText().length());
|
||||
|
||||
// 模拟法律咨询处理(实际项目中应该调用AI法律服务)
|
||||
Map<String, Object> legalResponse = performLegalConsultation(request);
|
||||
|
||||
result.put("success", true);
|
||||
result.put("message", "法律咨询完成");
|
||||
result.put("question", request.getQuestion());
|
||||
result.put("answer", legalResponse.get("answer"));
|
||||
result.put("category", legalResponse.get("category"));
|
||||
result.put("jurisdiction", legalResponse.get("jurisdiction"));
|
||||
result.put("confidence", legalResponse.get("confidence"));
|
||||
result.put("relatedLaws", legalResponse.get("relatedLaws"));
|
||||
result.put("suggestions", legalResponse.get("suggestions"));
|
||||
result.put("processingTimeMs", 2500);
|
||||
result.put("requestId", generateRequestId());
|
||||
result.put("timestamp", LocalDateTime.now());
|
||||
result.put("inputContent", request.getInputText());
|
||||
|
||||
log.info("法律咨询完成,回答长度: {}", legalResponse.get("answer").toString().length());
|
||||
|
||||
@@ -259,14 +259,89 @@ public class TextModelController {
|
||||
*/
|
||||
private Map<String, Object> performLegalConsultation(LegalRequest request) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("answer", "根据您的问题,我建议您咨询专业律师。这是一个复杂的法律问题,需要详细分析具体情况。");
|
||||
response.put("category", request.getCategory() != null ? request.getCategory() : "一般法律咨询");
|
||||
response.put("jurisdiction", request.getJurisdiction() != null ? request.getJurisdiction() : "中国大陆");
|
||||
|
||||
// 根据咨询目的和类型生成更精准的回答
|
||||
Generation gen = new Generation();
|
||||
Message systemMsg = Message.builder()
|
||||
.role(Role.SYSTEM.getValue())
|
||||
.content("你是资深律师, 请根据用户咨询类型,核心诉求,咨询目的,详细描述 ,回答用户,用词要严谨 。")
|
||||
.build();
|
||||
Message userMsg = Message.builder()
|
||||
.role(Role.USER.getValue())
|
||||
.content("咨询吗目的: "+request.getConsultationPurpose() +" , 核心诉求:" + request.getCoreDemand() +
|
||||
" , 咨询类型:" + request.getConsultationType()+" , 详细描述 : "+ request.getInputText())
|
||||
.build();
|
||||
GenerationParam param = GenerationParam.builder()
|
||||
// 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey("sk-xxx")
|
||||
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
|
||||
// .model("farui-plus")
|
||||
.model(request.getModel())
|
||||
.messages(Arrays.asList(systemMsg, userMsg))
|
||||
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
|
||||
.build();
|
||||
try {
|
||||
GenerationResult call = gen.call(param);
|
||||
response.put("answer", call.getOutput().getChoices().get(0).getMessage().getContent());
|
||||
log.info("法律咨询完成,结果长度: {} ,内容是:{} ", call);
|
||||
} catch (NoApiKeyException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InputRequiredException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
response.put("category", getCategoryByType(request.getConsultationType()));
|
||||
response.put("jurisdiction", "中国大陆");
|
||||
response.put("confidence", 0.85);
|
||||
response.put("relatedLaws", new String[]{"民法典", "合同法", "劳动法"});
|
||||
response.put("suggestions", new String[]{"收集相关证据", "咨询专业律师", "了解相关法律条文"});
|
||||
response.put("relatedLaws", getRelatedLaws(request.getConsultationType()));
|
||||
response.put("suggestions", getSuggestions(request.getConsultationPurpose()));
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据咨询类型获取分类
|
||||
*/
|
||||
private String getCategoryByType(String consultationType) {
|
||||
switch (consultationType) {
|
||||
case "document_generation":
|
||||
return "文档生成咨询";
|
||||
case "contract_review":
|
||||
return "合同审查";
|
||||
case "legal_advice":
|
||||
return "法律建议";
|
||||
default:
|
||||
return "一般法律咨询";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据咨询类型获取相关法律条文
|
||||
*/
|
||||
private String[] getRelatedLaws(String consultationType) {
|
||||
switch (consultationType) {
|
||||
case "document_generation":
|
||||
return new String[]{"民法典", "合同法", "个人信息保护法"};
|
||||
case "contract_review":
|
||||
return new String[]{"合同法", "民法典", "公司法"};
|
||||
case "legal_advice":
|
||||
return new String[]{"民法典", "劳动法", "消费者权益保护法"};
|
||||
default:
|
||||
return new String[]{"民法典", "合同法", "劳动法"};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据咨询目的获取建议
|
||||
*/
|
||||
private String[] getSuggestions(String consultationPurpose) {
|
||||
return new String[]{
|
||||
"收集相关证据材料",
|
||||
"咨询专业律师",
|
||||
"了解相关法律条文",
|
||||
"保护个人隐私信息"
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行意图分析的模拟方法
|
||||
|
||||
@@ -378,6 +378,11 @@ public class MenuController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -348,6 +348,11 @@ public class RoleController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -354,6 +354,11 @@ public class UserRoleController {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -120,6 +120,11 @@ public class DifyWorkflowResponseDto {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
168
src/main/java/com/rj/entity/ImageModel.java
Normal file
168
src/main/java/com/rj/entity/ImageModel.java
Normal file
@@ -0,0 +1,168 @@
|
||||
package com.rj.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 图像模型实体类
|
||||
*
|
||||
* 用于存储图像处理相关的模型信息,包括图像基本信息、所属人信息、
|
||||
* 处理参数、结果URL等。该实体对应数据库表 image_model。
|
||||
*
|
||||
* @author 系统生成
|
||||
* @date 2025/1/30
|
||||
* @version 1.0
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@TableName("image_model")
|
||||
public class ImageModel implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID,唯一标识符
|
||||
* 使用UUID作为主键,确保全局唯一性
|
||||
* 对应数据库字段:id
|
||||
*/
|
||||
@TableId(value = "uuid", type = IdType.ASSIGN_UUID)
|
||||
private String uuid;
|
||||
|
||||
/**
|
||||
* 图像名称
|
||||
* 存储图像文件的名称,用于标识和查找图像
|
||||
* 对应数据库字段:image_name
|
||||
*/
|
||||
@TableField("image_name")
|
||||
private String imageName;
|
||||
|
||||
/**
|
||||
* 模型名称
|
||||
* 使用的AI模型名称,如:stable-diffusion、midjourney等
|
||||
* 对应数据库字段:model_name
|
||||
*/
|
||||
@TableField("model_name")
|
||||
private String modelName;
|
||||
|
||||
/**
|
||||
* 所属人姓名
|
||||
* 图像处理请求的发起人姓名
|
||||
* 对应数据库字段:owner_name
|
||||
*/
|
||||
@TableField("owner_name")
|
||||
private String ownerName;
|
||||
|
||||
/**
|
||||
* 所属人电话
|
||||
* 图像处理请求的发起人联系电话
|
||||
* 对应数据库字段:owner_phone
|
||||
*/
|
||||
@TableField("owner_phone")
|
||||
private String ownerPhone;
|
||||
|
||||
/**
|
||||
* 图片类型
|
||||
* 图像的类型分类,material 和 result
|
||||
* 对应数据库字段:image_type
|
||||
*/
|
||||
@TableField("image_type")
|
||||
private String imageType;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
* 记录创建的时间戳,由系统自动设置
|
||||
* 对应数据库字段:create_time
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
* 记录最后修改的时间戳,由系统自动更新
|
||||
* 对应数据库字段:update_time
|
||||
*/
|
||||
@TableField("update_time")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 素材URL
|
||||
* 原始素材图片的永久访问链接
|
||||
* 对应数据库字段:material_url
|
||||
*/
|
||||
@TableField("material_url")
|
||||
private String materialUrl;
|
||||
|
||||
/**
|
||||
* 素材临时URL
|
||||
* 原始素材图片的临时访问链接,通常有过期时间
|
||||
* 对应数据库字段:material_temp_url
|
||||
*/
|
||||
@TableField("material_temp_url")
|
||||
private String materialTempUrl;
|
||||
|
||||
/**
|
||||
* 成品图片URL
|
||||
* 处理后的成品图片的永久访问链接
|
||||
* 对应数据库字段:result_image_url
|
||||
*/
|
||||
@TableField("result_image_url")
|
||||
private String resultImageUrl;
|
||||
|
||||
/**
|
||||
* 成品图片临时URL
|
||||
* 处理后的成品图片的临时访问链接,通常有过期时间
|
||||
* 对应数据库字段:result_image_temp_url
|
||||
*/
|
||||
@TableField("result_image_temp_url")
|
||||
private String resultImageTempUrl;
|
||||
|
||||
/**
|
||||
* 提示词
|
||||
* AI图像生成或处理时使用的文本提示词
|
||||
* 对应数据库字段:prompt
|
||||
*/
|
||||
@TableField("prompt")
|
||||
private String prompt;
|
||||
|
||||
/**
|
||||
* 功能函数
|
||||
* 调用的具体功能函数名称,如:upscale、generate等
|
||||
* 对应数据库字段:function_name
|
||||
*/
|
||||
@TableField("function_name")
|
||||
private String functionName;
|
||||
|
||||
/**
|
||||
* 放大倍数
|
||||
* 图像放大处理的倍数,如:2.0、4.0等
|
||||
* 对应数据库字段:scale_factor
|
||||
*/
|
||||
@TableField("scale_factor")
|
||||
private BigDecimal scaleFactor;
|
||||
|
||||
/**
|
||||
* 请求ID
|
||||
* 唯一标识一次处理请求的ID,用于追踪和日志记录
|
||||
* 对应数据库字段:request_id
|
||||
*/
|
||||
@TableField("request_id")
|
||||
private String requestId;
|
||||
|
||||
/**
|
||||
* 任务ID
|
||||
* 异步处理任务的唯一标识符,用于查询任务状态
|
||||
* 对应数据库字段:task_id
|
||||
*/
|
||||
@TableField("task_id")
|
||||
private String taskId;
|
||||
}
|
||||
@@ -63,6 +63,11 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
24
src/main/java/com/rj/mapper/ImageModelMapper.java
Normal file
24
src/main/java/com/rj/mapper/ImageModelMapper.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.rj.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.rj.entity.ImageModel;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 图像模型Mapper接口
|
||||
*
|
||||
* 继承MyBatis-Plus的BaseMapper,提供基础的CRUD操作。
|
||||
* 该接口负责与image_model表进行数据交互,包括:
|
||||
* - 基础的增删改查操作
|
||||
* - 分页查询
|
||||
* - 条件查询
|
||||
* - 批量操作等
|
||||
*
|
||||
* @author 系统生成
|
||||
* @date 2025/1/30
|
||||
* @version 1.0
|
||||
*/
|
||||
@Mapper
|
||||
public interface ImageModelMapper extends BaseMapper<ImageModel> {
|
||||
|
||||
}
|
||||
111
src/main/java/com/rj/service/IImageModelService.java
Normal file
111
src/main/java/com/rj/service/IImageModelService.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package com.rj.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.rj.entity.ImageModel;
|
||||
|
||||
/**
|
||||
* 图像模型服务接口
|
||||
*
|
||||
* 提供图像模型相关的业务逻辑接口,包括:
|
||||
* - 图像模型记录的增删改查操作
|
||||
* - 根据不同条件查询图像模型记录
|
||||
* - 图像模型数据的业务处理逻辑
|
||||
*
|
||||
* 继承MyBatis-Plus的IService接口,获得基础的CRUD能力,
|
||||
* 同时定义特定的业务方法。
|
||||
*
|
||||
* @author 系统生成
|
||||
* @date 2025/1/30
|
||||
* @version 1.0
|
||||
*/
|
||||
public interface IImageModelService extends IService<ImageModel> {
|
||||
|
||||
/**
|
||||
* 保存图像模型记录
|
||||
*
|
||||
* 将图像模型数据保存到数据库中,包括完整的业务逻辑处理。
|
||||
* 该方法会进行数据验证、日志记录等操作。
|
||||
*
|
||||
* @param imageModel 图像模型记录对象,包含所有必要的字段信息
|
||||
* @return 是否保存成功,true表示保存成功,false表示保存失败
|
||||
*/
|
||||
boolean saveImageModel(ImageModel imageModel);
|
||||
|
||||
/**
|
||||
* 根据UUID查询图像模型记录
|
||||
*
|
||||
* 通过唯一标识符查询对应的图像模型记录。
|
||||
* UUID是主键,查询结果唯一。
|
||||
*
|
||||
* @param uuid 唯一标识符,对应数据库主键
|
||||
* @return 图像模型记录,如果未找到则返回null
|
||||
*/
|
||||
ImageModel getByUuid(String uuid);
|
||||
|
||||
/**
|
||||
* 根据请求ID查询图像模型记录
|
||||
*
|
||||
* 通过请求ID查询对应的图像模型记录。
|
||||
* 一个请求ID可能对应多条记录,此方法返回最新的一条。
|
||||
*
|
||||
* @param requestId 请求ID,用于追踪处理请求
|
||||
* @return 图像模型记录,如果未找到则返回null
|
||||
*/
|
||||
ImageModel getByRequestId(String requestId);
|
||||
|
||||
/**
|
||||
* 根据任务ID查询图像模型记录
|
||||
*
|
||||
* 通过任务ID查询对应的图像模型记录。
|
||||
* 一个任务ID可能对应多条记录,此方法返回最新的一条。
|
||||
*
|
||||
* @param taskId 任务ID,用于追踪异步处理任务
|
||||
* @return 图像模型记录,如果未找到则返回null
|
||||
*/
|
||||
ImageModel getByTaskId(String taskId);
|
||||
|
||||
/**
|
||||
* 根据所属人电话查询图像模型记录
|
||||
*
|
||||
* 通过所属人电话查询对应的图像模型记录。
|
||||
* 一个电话号码可能对应多条记录,此方法返回最新的一条。
|
||||
*
|
||||
* @param ownerPhone 所属人电话,用于查询特定用户的记录
|
||||
* @return 图像模型记录,如果未找到则返回null
|
||||
*/
|
||||
ImageModel getByOwnerPhone(String ownerPhone);
|
||||
|
||||
/**
|
||||
* 去除图像中的中英文字符或常见水印
|
||||
*
|
||||
* 处理图像去除水印的业务逻辑,包括:
|
||||
* - 图像预处理和参数设置
|
||||
* - 调用AI模型进行水印去除
|
||||
* - 结果处理和URL生成
|
||||
* - 记录保存和状态更新
|
||||
*
|
||||
* @param imageModel 图像模型记录对象,包含处理参数和结果信息
|
||||
* @return 处理后的图像模型记录,包含结果URL等信息
|
||||
*/
|
||||
ImageModel removeWatermar(ImageModel imageModel);
|
||||
|
||||
/**
|
||||
* 上传图片素材
|
||||
*
|
||||
* 处理图片素材上传的完整业务逻辑,包括:
|
||||
* - 文件验证和格式检查
|
||||
* - 上传到MinIO存储
|
||||
* - 生成永久URL和临时URL
|
||||
* - 保存上传记录到数据库
|
||||
*
|
||||
* @param file 上传的图片文件
|
||||
* @param ownerName 所属人姓名
|
||||
* @param ownerPhone 所属人电话
|
||||
* @param imageType 图片类型
|
||||
* @return 图像模型记录,包含上传结果和URL信息
|
||||
*/
|
||||
ImageModel uploadImageMaterial(org.springframework.web.multipart.MultipartFile file,
|
||||
String ownerName, String ownerPhone, String imageType,String imageName);
|
||||
|
||||
boolean removeByUuid(String uuid);
|
||||
}
|
||||
@@ -84,6 +84,11 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -119,6 +119,11 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
476
src/main/java/com/rj/service/impl/ImageModelServiceImpl.java
Normal file
476
src/main/java/com/rj/service/impl/ImageModelServiceImpl.java
Normal file
@@ -0,0 +1,476 @@
|
||||
package com.rj.service.impl;
|
||||
|
||||
import ai.djl.util.JsonUtils;
|
||||
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis;
|
||||
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam;
|
||||
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult;
|
||||
import com.alibaba.dashscope.exception.ApiException;
|
||||
import com.alibaba.dashscope.exception.NoApiKeyException;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.rj.entity.ImageModel;
|
||||
import com.rj.mapper.ImageModelMapper;
|
||||
import com.rj.service.IImageModelService;
|
||||
import com.rj.service.MinIOService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 图像模型服务实现类
|
||||
*
|
||||
* 实现IImageModelService接口中定义的所有业务方法。
|
||||
* 该类负责处理图像模型相关的业务逻辑,包括:
|
||||
* - 图像模型记录的保存、查询、更新、删除操作
|
||||
* - 根据各种条件查询图像模型记录
|
||||
* - 异常处理和日志记录
|
||||
*
|
||||
* 继承ServiceImpl获得MyBatis-Plus提供的基础CRUD能力,
|
||||
* 同时实现自定义的业务方法。
|
||||
*
|
||||
* @author 系统生成
|
||||
* @date 2025/1/30
|
||||
* @version 1.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ImageModelServiceImpl extends ServiceImpl<ImageModelMapper, ImageModel>
|
||||
implements IImageModelService {
|
||||
|
||||
@Autowired
|
||||
private MinIOService minIOService;
|
||||
|
||||
/**
|
||||
* 保存图像模型记录
|
||||
*
|
||||
* 将图像模型数据保存到数据库中,包含完整的异常处理和日志记录。
|
||||
* 该方法会记录操作开始、成功或失败的日志信息。
|
||||
*
|
||||
* @param imageModel 图像模型记录对象,包含所有必要的字段信息
|
||||
* @return 是否保存成功,true表示保存成功,false表示保存失败
|
||||
*/
|
||||
@Override
|
||||
public boolean saveImageModel(ImageModel imageModel) {
|
||||
try {
|
||||
log.info("保存图像模型记录开始,参数:{}", imageModel);
|
||||
boolean result = this.save(imageModel);
|
||||
if (result) {
|
||||
log.info("图像模型记录保存成功,ID: {}", imageModel.getUuid());
|
||||
} else {
|
||||
log.error("图像模型记录保存失败");
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("保存图像模型记录异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据UUID查询图像模型记录
|
||||
*
|
||||
* 通过唯一标识符查询对应的图像模型记录。
|
||||
* 使用LambdaQueryWrapper构建查询条件,确保类型安全。
|
||||
*
|
||||
* @param uuid 唯一标识符,对应数据库主键
|
||||
* @return 图像模型记录,如果未找到则返回null
|
||||
*/
|
||||
@Override
|
||||
public ImageModel getByUuid(String uuid) {
|
||||
try {
|
||||
LambdaQueryWrapper<ImageModel> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(ImageModel::getUuid, uuid);
|
||||
return this.getOne(queryWrapper);
|
||||
} catch (Exception e) {
|
||||
log.error("根据ID查询图像模型记录异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据请求ID查询图像模型记录
|
||||
*
|
||||
* 通过请求ID查询对应的图像模型记录。
|
||||
* 由于一个请求ID可能对应多条记录,此方法返回最新的一条。
|
||||
* 查询结果按创建时间降序排列,取第一条记录。
|
||||
*
|
||||
* @param requestId 请求ID,用于追踪处理请求
|
||||
* @return 图像模型记录,如果未找到则返回null
|
||||
*/
|
||||
@Override
|
||||
public ImageModel getByRequestId(String requestId) {
|
||||
try {
|
||||
LambdaQueryWrapper<ImageModel> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(ImageModel::getRequestId, requestId)
|
||||
.orderByDesc(ImageModel::getCreateTime)
|
||||
.last("LIMIT 1");
|
||||
return this.getOne(queryWrapper);
|
||||
} catch (Exception e) {
|
||||
log.error("根据请求ID查询图像模型记录异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务ID查询图像模型记录
|
||||
*
|
||||
* 通过任务ID查询对应的图像模型记录。
|
||||
* 由于一个任务ID可能对应多条记录,此方法返回最新的一条。
|
||||
* 查询结果按创建时间降序排列,取第一条记录。
|
||||
*
|
||||
* @param taskId 任务ID,用于追踪异步处理任务
|
||||
* @return 图像模型记录,如果未找到则返回null
|
||||
*/
|
||||
@Override
|
||||
public ImageModel getByTaskId(String taskId) {
|
||||
try {
|
||||
LambdaQueryWrapper<ImageModel> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(ImageModel::getTaskId, taskId)
|
||||
.orderByDesc(ImageModel::getCreateTime)
|
||||
.last("LIMIT 1");
|
||||
return this.getOne(queryWrapper);
|
||||
} catch (Exception e) {
|
||||
log.error("根据任务ID查询图像模型记录异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据所属人电话查询图像模型记录
|
||||
*
|
||||
* 通过所属人电话查询对应的图像模型记录。
|
||||
* 由于一个电话号码可能对应多条记录,此方法返回最新的一条。
|
||||
* 查询结果按创建时间降序排列,取第一条记录。
|
||||
*
|
||||
* @param ownerPhone 所属人电话,用于查询特定用户的记录
|
||||
* @return 图像模型记录,如果未找到则返回null
|
||||
*/
|
||||
@Override
|
||||
public ImageModel getByOwnerPhone(String ownerPhone) {
|
||||
try {
|
||||
LambdaQueryWrapper<ImageModel> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(ImageModel::getOwnerPhone, ownerPhone)
|
||||
.orderByDesc(ImageModel::getCreateTime)
|
||||
.last("LIMIT 1");
|
||||
return this.getOne(queryWrapper);
|
||||
} catch (Exception e) {
|
||||
log.error("根据所属人电话查询图像模型记录异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除图像中的中英文字符或常见水印
|
||||
*
|
||||
* 实现图像水印去除的核心业务逻辑,包括:
|
||||
* 1. 参数验证和预处理
|
||||
* 2. 调用AI模型进行水印检测和去除
|
||||
* 3. 处理结果并生成访问URL
|
||||
* 4. 保存处理记录到数据库
|
||||
* 5. 返回处理结果
|
||||
*
|
||||
* @param imageModel 图像模型记录对象,包含处理参数和结果信息
|
||||
* @return 处理后的图像模型记录,包含结果URL等信息
|
||||
*/
|
||||
@Override
|
||||
public ImageModel removeWatermar(ImageModel imageModel) {
|
||||
try {
|
||||
log.info("开始去除图像水印,参数:{}", imageModel);
|
||||
|
||||
// 1. 参数验证
|
||||
if (imageModel.getMaterialTempUrl() == null || imageModel.getMaterialTempUrl().trim().isEmpty()) {
|
||||
log.error("素材URL不能为空");
|
||||
throw new IllegalArgumentException("素材URL不能为空");
|
||||
}
|
||||
imageModel.setPrompt("去除图像中的文字");
|
||||
imageModel.setModelName("wanx2.1-imageedit");
|
||||
imageModel.setFunctionName("remove_watermark");
|
||||
// 3. 模拟AI模型处理(实际项目中这里会调用真实的AI服务)
|
||||
log.info("调用AI模型进行水印去除处理...");
|
||||
/**
|
||||
* {
|
||||
* "input": {
|
||||
* "function": "remove_watermark",
|
||||
* "prompt": "去除图像中的文字",
|
||||
* "base_image_url": "http://wanx.alicdn.com/material/20250318/remove_watermark_1.png"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
// 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey="sk-xxx"
|
||||
String apiKey = System.getenv("DASHSCOPE_API_KEY");
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("prompt_extend", true);
|
||||
|
||||
ImageSynthesisParam param =
|
||||
ImageSynthesisParam.builder()
|
||||
.apiKey(apiKey)
|
||||
.model(imageModel.getModelName())
|
||||
.function(imageModel.getFunctionName())
|
||||
.prompt(imageModel.getPrompt())
|
||||
.baseImageUrl(imageModel.getMaterialTempUrl().trim())
|
||||
.n(1)
|
||||
.size("1024*1024")
|
||||
.parameters(parameters)
|
||||
.build();
|
||||
|
||||
ImageSynthesis imageSynthesis = new ImageSynthesis();
|
||||
ImageSynthesisResult result = null;
|
||||
try {
|
||||
System.out.println("---sync call, please wait a moment----");
|
||||
log.info("开始同步调用,参数是:{} ",param);
|
||||
result = imageSynthesis.call(param);
|
||||
log.info("返回结果 : ", result);
|
||||
System.out.println(JsonUtils.toJson(result));
|
||||
|
||||
|
||||
result.getOutput().getResults().forEach(resultItem -> {
|
||||
|
||||
imageModel.setResultImageUrl(resultItem.get("url"));
|
||||
});
|
||||
} catch (ApiException | NoApiKeyException e){
|
||||
log.error("api call error", e);
|
||||
}
|
||||
System.out.println(JsonUtils.toJson(result));
|
||||
|
||||
// 4. 下载AI处理结果图片并上传到MinIO
|
||||
if (result != null && result.getOutput() != null && !result.getOutput().getResults().isEmpty()) {
|
||||
String resultImageUrl = result.getOutput().getResults().get(0).get("url");
|
||||
if (resultImageUrl != null && !resultImageUrl.trim().isEmpty()) {
|
||||
log.info("开始下载AI处理结果图片: {}", resultImageUrl);
|
||||
|
||||
try {
|
||||
// 下载图片到字节数组
|
||||
byte[] imageBytes = downloadImageToBytes(resultImageUrl);
|
||||
log.info("图片下载成功,大小: {} bytes", imageBytes.length);
|
||||
|
||||
// 生成唯一文件名
|
||||
String resultFileName = generateUniqueFileName("jpg");
|
||||
|
||||
// 上传到MinIO
|
||||
String resultImageMinIOUrl = minIOService.uploadFile(imageBytes, resultFileName, "image/jpeg");
|
||||
log.info("图片上传到MinIO成功: {}", resultImageMinIOUrl);
|
||||
|
||||
// 生成7天临时访问链接
|
||||
String resultImageTempUrl = minIOService.generateTempUrl(resultFileName);
|
||||
log.info("生成7天临时访问链接: {}", resultImageTempUrl);
|
||||
|
||||
// 更新ImageModel对象
|
||||
imageModel.setResultImageUrl(resultImageMinIOUrl);
|
||||
imageModel.setResultImageTempUrl(resultImageTempUrl);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("下载或上传AI处理结果图片失败", e);
|
||||
throw new RuntimeException("处理结果图片失败: " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
log.warn("AI处理结果中没有有效的图片URL");
|
||||
}
|
||||
} else {
|
||||
log.warn("AI处理结果为空或无效");
|
||||
}
|
||||
|
||||
// 6. 保存处理记录
|
||||
boolean saveResult = this.updateById(imageModel);
|
||||
if (!saveResult) {
|
||||
log.error("保存水印去除记录失败");
|
||||
throw new RuntimeException("保存水印去除记录失败");
|
||||
}
|
||||
|
||||
log.info("水印去除处理完成,ID: {}, 结果URL: {}",
|
||||
imageModel.getUuid(), imageModel.getResultImageTempUrl());
|
||||
|
||||
return imageModel;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("去除图像水印异常", e);
|
||||
throw new RuntimeException("去除图像水印失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传图片素材
|
||||
*
|
||||
* 实现图片素材上传的完整业务逻辑,包括:
|
||||
* 1. 文件验证和格式检查
|
||||
* 2. 上传到MinIO存储
|
||||
* 3. 生成永久URL和临时URL
|
||||
* 4. 保存上传记录到数据库
|
||||
*
|
||||
* @param file 上传的图片文件
|
||||
* @param ownerName 所属人姓名
|
||||
* @param ownerPhone 所属人电话
|
||||
* @param imageType 图片类型
|
||||
* @return 图像模型记录,包含上传结果和URL信息
|
||||
*/
|
||||
@Override
|
||||
public ImageModel uploadImageMaterial(MultipartFile file, String ownerName, String ownerPhone, String imageType,String imageName) {
|
||||
try {
|
||||
log.info("开始上传图片素材,文件名:{},所属人:{},电话:{}",
|
||||
file.getOriginalFilename(), ownerName, ownerPhone);
|
||||
|
||||
// 1. 文件验证
|
||||
validateImageFile(file);
|
||||
|
||||
// 2. 生成唯一文件名
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String fileExtension = getFileExtension(originalFilename);
|
||||
String uniqueFileName = generateUniqueFileName(fileExtension);
|
||||
|
||||
// 3. 上传到MinIO
|
||||
log.info("开始上传文件到MinIO,文件名:{}", uniqueFileName);
|
||||
String materialUrl = minIOService.uploadFileWithName(file, uniqueFileName);
|
||||
String materialTempUrl = minIOService.generateTempUrl(uniqueFileName);
|
||||
|
||||
// 4. 构建图像模型记录
|
||||
ImageModel imageModel = new ImageModel();
|
||||
imageModel.setImageName(originalFilename);
|
||||
imageModel.setOwnerName(ownerName);
|
||||
imageModel.setOwnerPhone(ownerPhone);
|
||||
imageModel.setImageType(imageType);
|
||||
imageModel.setMaterialUrl(materialUrl);
|
||||
imageModel.setMaterialTempUrl(materialTempUrl);
|
||||
imageModel.setCreateTime(LocalDateTime.now());
|
||||
imageModel.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
if ( imageName!=null && !imageName.trim().isEmpty()){
|
||||
imageModel.setImageName(imageName);
|
||||
}
|
||||
// 5. 保存到数据库
|
||||
boolean saveResult = this.saveImageModel(imageModel);
|
||||
if (!saveResult) {
|
||||
log.error("保存图片素材上传记录失败");
|
||||
throw new RuntimeException("保存图片素材上传记录失败");
|
||||
}
|
||||
|
||||
log.info("图片素材上传成功,ID: {},永久URL: {},临时URL: {}",
|
||||
imageModel.getUuid(), materialUrl, materialTempUrl);
|
||||
|
||||
return imageModel;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("上传图片素材异常", e);
|
||||
throw new RuntimeException("上传图片素材失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeByUuid(String uuid) {
|
||||
boolean b = this.removeById(uuid);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证图片文件
|
||||
*
|
||||
* 检查文件格式、大小等是否符合要求
|
||||
*
|
||||
* @param file 上传的文件
|
||||
* @throws IllegalArgumentException 文件不符合要求时抛出异常
|
||||
*/
|
||||
private void validateImageFile(MultipartFile file) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new IllegalArgumentException("上传文件不能为空");
|
||||
}
|
||||
|
||||
// 检查文件大小(限制为10MB)
|
||||
long maxSize = 10 * 1024 * 1024; // 10MB
|
||||
if (file.getSize() > maxSize) {
|
||||
throw new IllegalArgumentException("文件大小不能超过10MB");
|
||||
}
|
||||
|
||||
// 检查文件格式
|
||||
String contentType = file.getContentType();
|
||||
if (contentType == null || !contentType.startsWith("image/")) {
|
||||
throw new IllegalArgumentException("只能上传图片文件");
|
||||
}
|
||||
|
||||
// 检查文件扩展名
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename == null || originalFilename.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("文件名不能为空");
|
||||
}
|
||||
|
||||
String extension = getFileExtension(originalFilename).toLowerCase();
|
||||
String[] allowedExtensions = {"jpg", "jpeg", "png", "gif", "bmp", "webp"};
|
||||
boolean isValidExtension = false;
|
||||
for (String allowedExt : allowedExtensions) {
|
||||
if (extension.equals(allowedExt)) {
|
||||
isValidExtension = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidExtension) {
|
||||
throw new IllegalArgumentException("不支持的图片格式,支持的格式:jpg, jpeg, png, gif, bmp, webp");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
*
|
||||
* @param filename 文件名
|
||||
* @return 文件扩展名
|
||||
*/
|
||||
private String getFileExtension(String filename) {
|
||||
if (filename == null || filename.lastIndexOf('.') == -1) {
|
||||
return "";
|
||||
}
|
||||
return filename.substring(filename.lastIndexOf('.') + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一文件名
|
||||
*
|
||||
* 格式:时间戳_UUID.扩展名
|
||||
*
|
||||
* @param extension 文件扩展名
|
||||
* @return 唯一文件名
|
||||
*/
|
||||
private String generateUniqueFileName(String extension) {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String uuid = UUID.randomUUID().toString().replace("-", "");
|
||||
return timestamp + "_" + uuid + "." + extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载图片到字节数组
|
||||
*
|
||||
* 从指定URL下载图片并返回字节数组
|
||||
*
|
||||
* @param imageUrl 图片URL
|
||||
* @return 图片字节数组
|
||||
* @throws IOException 下载失败时抛出异常
|
||||
*/
|
||||
private byte[] downloadImageToBytes(String imageUrl) throws IOException {
|
||||
try {
|
||||
log.info("开始下载图片: {}", imageUrl);
|
||||
|
||||
URL url = new URL(imageUrl);
|
||||
try (InputStream inputStream = url.openStream();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
byte[] imageBytes = outputStream.toByteArray();
|
||||
log.info("图片下载完成,大小: {} bytes", imageBytes.length);
|
||||
return imageBytes;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("下载图片失败: {}", imageUrl, e);
|
||||
throw new IOException("下载图片失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -91,6 +91,11 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -91,6 +91,11 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -91,6 +91,11 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -100,6 +100,11 @@ spring:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -77,6 +77,11 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -276,6 +276,11 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -155,6 +155,11 @@ public class TtsRequestLogShortUrlTest {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user