【代码优化】AI:Image 图片的列表、详情等接口

This commit is contained in:
YunaiV 2024-05-31 21:47:48 +08:00
parent c1f2e49066
commit 458f5acdf0
7 changed files with 83 additions and 150 deletions

View File

@ -1,13 +1,14 @@
package cn.iocoder.yudao.module.ai.controller.admin.image; package cn.iocoder.yudao.module.ai.controller.admin.image;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageMyRespVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageRespVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
import cn.iocoder.yudao.module.ai.service.image.AiImageService; import cn.iocoder.yudao.module.ai.service.image.AiImageService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@ -21,35 +22,30 @@ import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - Ai 绘画") @Tag(name = "管理后台 - AI 绘画")
@RestController @RestController
@RequestMapping("/ai/image") @RequestMapping("/ai/image")
@Slf4j @Slf4j
public class AiImageController { public class AiImageController {
@Resource @Resource
private AiImageService aiImageService; private AiImageService imageService;
@Operation(summary = "获取【我的】绘图分页") @Operation(summary = "获取【我的】绘图分页")
@GetMapping("/my-page") @GetMapping("/my-page")
public CommonResult<PageResult<AiImagePageMyRespVO>> getImagePageMy(@Validated AiImageListReqVO req) { public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated PageParam pageReqVO) {
// 转换 resp PageResult<AiImageDO> pageResult = imageService.getImagePageMy(getLoginUserId(), pageReqVO);
PageResult<AiImageDO> pageResult = aiImageService.getImagePageMy(getLoginUserId(), req); return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
// 转换 PageResult<AiImageListRespVO> 返回
PageResult<AiImagePageMyRespVO> result = new PageResult<>();
result.setTotal(pageResult.getTotal());
result.setList(BeanUtils.toBean(pageResult.getList(), AiImagePageMyRespVO.class));
return success(result);
} }
// TODO @fan类似 /my-page 的建议 @Operation(summary = "获取【我的】绘图记录")
@Operation(summary = "获取【我的】绘图记录", description = "...")
@GetMapping("/get-my") @GetMapping("/get-my")
public CommonResult<AiImagePageMyRespVO> getMy(@RequestParam("id") Long id) { public CommonResult<AiImageRespVO> getImageMy(@RequestParam("id") Long id) {
// 获取 image 信息 AiImageDO image = imageService.getImage(id);
AiImageDO imageDO = aiImageService.getMy(id); if (image == null || ObjUtil.notEqual(getLoginUserId(), image.getUserId())) {
// resp 并返回 return success(null);
return CommonResult.success(BeanUtils.toBean(imageDO, AiImagePageMyRespVO.class)); }
return success(BeanUtils.toBean(image, AiImageRespVO.class));
} }
// TODO @fan建议把 dallDrawingmidjourney 融合成一个 draw 接口异步绘制然后返回一个 id 给前端前端通过 get 接口轮询直到获取到生成成功 // TODO @fan建议把 dallDrawingmidjourney 融合成一个 draw 接口异步绘制然后返回一个 id 给前端前端通过 get 接口轮询直到获取到生成成功
@ -58,14 +54,15 @@ public class AiImageController {
@Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!") @Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!")
@PostMapping("/dall") @PostMapping("/dall")
public CommonResult<Long> dall(@Validated @RequestBody AiImageDallReqVO req) { public CommonResult<Long> dall(@Validated @RequestBody AiImageDallReqVO req) {
return success(aiImageService.dall(getLoginUserId(), req)); return success(imageService.dall(getLoginUserId(), req));
} }
@Operation(summary = "删除【我的】绘画记录") @Operation(summary = "删除【我的】绘画记录")
@DeleteMapping("/delete-id-my") @DeleteMapping("/delete-my")
@Parameter(name = "id", required = true, description = "绘画编号", example = "1024") @Parameter(name = "id", required = true, description = "绘画编号", example = "1024")
public CommonResult<Boolean> deleteIdMy(@RequestParam("id") Long id) { public CommonResult<Boolean> deleteImageMy(@RequestParam("id") Long id) {
return success(aiImageService.deleteIdMy(id, getLoginUserId())); imageService.deleteImageMy(id, getLoginUserId());
return success(true);
} }
// ================ midjourney 接口 // ================ midjourney 接口
@ -73,12 +70,14 @@ public class AiImageController {
@Operation(summary = "midjourney-imagine 绘画", description = "...") @Operation(summary = "midjourney-imagine 绘画", description = "...")
@PostMapping("/midjourney/imagine") @PostMapping("/midjourney/imagine")
public CommonResult<Long> midjourneyImagine(@Validated @RequestBody AiImageMidjourneyImagineReqVO req) { public CommonResult<Long> midjourneyImagine(@Validated @RequestBody AiImageMidjourneyImagineReqVO req) {
return success(aiImageService.midjourneyImagine(getLoginUserId(), req)); return success(imageService.midjourneyImagine(getLoginUserId(), req));
} }
// TODO @fan可以考虑复用 AiImageDallRespVO统一成 AIImageRespVO
@Operation(summary = "midjourney proxy - 回调通知") @Operation(summary = "midjourney proxy - 回调通知")
@RequestMapping("/midjourney-notify") @RequestMapping("/midjourney-notify")
public CommonResult<Boolean> midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) { public CommonResult<Boolean> midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) {
return success(aiImageService.midjourneyNotify(getLoginUserId(), notifyReqVO)); return success(imageService.midjourneyNotify(getLoginUserId(), notifyReqVO));
} }
} }

View File

@ -1,18 +0,0 @@
package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* AI Image 我的图片列表 req
*
* @author fansili
* @time 2024/4/28 17:42
* @since 1.0
*/
@Data
@Accessors(chain = true)
public class AiImageListReqVO extends PageParam {
}

View File

@ -1,16 +0,0 @@
package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* midjourney
*
* @author fansili
* @time 2024/4/28 17:42
* @since 1.0
*/
@Data
@Accessors(chain = true)
public class AiImageMidjourneyRes {
}

View File

@ -1,23 +1,13 @@
package cn.iocoder.yudao.module.ai.controller.admin.image.vo; package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Map; import java.util.Map;
// TODO @fan可以考虑复用 AiImageDallRespVO统一成 AIImageRespVO // TODO @芋艿完善 swagger 注解
/**
* midjourney req
*
* @author fansili
* @time 2024/4/28 17:42
* @since 1.0
*/
@Data @Data
@Accessors(chain = true) public class AiImageRespVO {
public class AiImagePageMyRespVO extends PageParam {
@Schema(description = "id编号", example = "1") @Schema(description = "id编号", example = "1")
private Long id; private Long id;

View File

@ -1,5 +1,7 @@
package cn.iocoder.yudao.module.ai.dal.mysql.image; package cn.iocoder.yudao.module.ai.dal.mysql.image;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
@ -35,4 +37,10 @@ public interface AiImageMapper extends BaseMapperX<AiImageDO> {
return this.selectOne(new LambdaQueryWrapperX<AiImageDO>().eq(AiImageDO::getJobId, id)); return this.selectOne(new LambdaQueryWrapperX<AiImageDO>().eq(AiImageDO::getJobId, id));
} }
default PageResult<AiImageDO> selectPage(Long userId, PageParam pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<AiImageDO>()
.eq(AiImageDO::getUserId, userId)
.orderByDesc(AiImageDO::getId));
}
} }

View File

@ -1,38 +1,35 @@
package cn.iocoder.yudao.module.ai.service.image; package cn.iocoder.yudao.module.ai.service.image;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyOperateReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
/** /**
* ai 作图 * AI 绘图 Service 接口
* *
* @author fansili * @author fansili
* @time 2024/4/25 15:50
* @since 1.0
*/ */
public interface AiImageService { public interface AiImageService {
/** /**
* ai绘画 - 列表 * 获取我的绘图分页
* *
* @param loginUserId * @param userId 用户编号
* @param req * @param pageReqVO 分页条件
* @return * @return 绘图分页
*/ */
PageResult<AiImageDO> getImagePageMy(Long loginUserId, AiImageListReqVO req); PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO);
/** /**
* - image 信息 * 得绘图记录
* *
* @param id * @param id 绘图编号
* @return * @return 绘图记录
*/ */
AiImageDO getMy(Long id); AiImageDO getImage(Long id);
/** /**
* ai绘画 - dall2/dall3 绘画 * ai绘画 - dall2/dall3 绘画
@ -52,19 +49,12 @@ public interface AiImageService {
Long midjourneyImagine(Long loginUserId, AiImageMidjourneyImagineReqVO req); Long midjourneyImagine(Long loginUserId, AiImageMidjourneyImagineReqVO req);
/** /**
* midjourney 操作(u1u2放大换一批...) * 删除我的绘画记录
* *
* @param req * @param id 绘画编号
* @param userId 用户编号
*/ */
void midjourneyOperate(AiImageMidjourneyOperateReqVO req); void deleteImageMy(Long id, Long userId);
/**
* 删除 - image 记录
*
* @param id
* @param loginUserId
*/
Boolean deleteIdMy(Long id, Long loginUserId);
/** /**
* midjourney proxy - 回调通知 * midjourney proxy - 回调通知

View File

@ -2,17 +2,17 @@ package cn.iocoder.yudao.module.ai.service.image;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum; import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum;
import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum; import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum;
import cn.iocoder.yudao.framework.ai.core.exception.AiException; import cn.iocoder.yudao.framework.ai.core.exception.AiException;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.ai.AiCommonConstants; import cn.iocoder.yudao.module.ai.AiCommonConstants;
import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
import cn.iocoder.yudao.module.ai.client.MidjourneyProxyClient; import cn.iocoder.yudao.module.ai.client.MidjourneyProxyClient;
import cn.iocoder.yudao.module.ai.client.enums.MidjourneyModelEnum; import cn.iocoder.yudao.module.ai.client.enums.MidjourneyModelEnum;
import cn.iocoder.yudao.module.ai.client.enums.MidjourneySubmitCodeEnum; import cn.iocoder.yudao.module.ai.client.enums.MidjourneySubmitCodeEnum;
@ -21,9 +21,7 @@ import cn.iocoder.yudao.module.ai.client.vo.MidjourneyImagineReqVO;
import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
import cn.iocoder.yudao.module.ai.client.vo.MidjourneySubmitRespVO; import cn.iocoder.yudao.module.ai.client.vo.MidjourneySubmitRespVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyOperateReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
import cn.iocoder.yudao.module.ai.enums.AiImagePublicStatusEnum; import cn.iocoder.yudao.module.ai.enums.AiImagePublicStatusEnum;
@ -46,12 +44,12 @@ import org.springframework.transaction.annotation.Transactional;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.AI_IMAGE_NOT_EXISTS;
/** /**
* AI 绘画(接入 dall2/dall3midjourney) * AI 绘画 Service 实现类
* *
* @author fansili * @author fansili
* @time 2024/4/25 15:51
* @since 1.0
*/ */
@Service @Service
@Slf4j @Slf4j
@ -59,10 +57,13 @@ public class AiImageServiceImpl implements AiImageService {
@Resource @Resource
private AiImageMapper imageMapper; private AiImageMapper imageMapper;
@Resource @Resource
private FileApi fileApi; private FileApi fileApi;
@Resource @Resource
private OpenAiImageClient openAiImageClient; private OpenAiImageClient openAiImageClient;
@Autowired @Autowired
private MidjourneyProxyClient midjourneyProxyClient; private MidjourneyProxyClient midjourneyProxyClient;
@ -70,16 +71,12 @@ public class AiImageServiceImpl implements AiImageService {
private String midjourneyNotifyUrl; private String midjourneyNotifyUrl;
@Override @Override
public PageResult<AiImageDO> getImagePageMy(Long loginUserId, AiImageListReqVO req) { public PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO) {
// 查询当前用户下所有的绘画记录 return imageMapper.selectPage(userId, pageReqVO);
return imageMapper.selectPage(req,
new LambdaQueryWrapperX<AiImageDO>()
.eq(AiImageDO::getUserId, loginUserId)
.orderByDesc(AiImageDO::getId));
} }
@Override @Override
public AiImageDO getMy(Long id) { public AiImageDO getImage(Long id) {
return imageMapper.selectById(id); return imageMapper.selectById(id);
} }
@ -95,7 +92,7 @@ public class AiImageServiceImpl implements AiImageService {
.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
imageMapper.insert(aiImageDO); imageMapper.insert(aiImageDO);
// 异步执行 // 异步执行
doDall(aiImageDO, req); getSelf().doDall(aiImageDO, req);
// 转换 AiImageDallDrawingRespVO // 转换 AiImageDallDrawingRespVO
return aiImageDO.getId(); return aiImageDO.getId();
} }
@ -185,42 +182,15 @@ public class AiImageServiceImpl implements AiImageService {
return aiImageDO.getId(); return aiImageDO.getId();
} }
@Transactional(rollbackFor = Exception.class)
@Override @Override
public void midjourneyOperate(AiImageMidjourneyOperateReqVO req) { public void deleteImageMy(Long id, Long userId) {
// // 校验是否存在 // 1. 校验是否存在
// AiImageDO aiImageDO = validateExists(req.getId()); AiImageDO image = validateImageExists(id);
// // 获取 midjourneyOperations if (ObjUtil.notEqual(image.getUserId(), userId)) {
// List<AiImageMidjourneyOperationsVO> midjourneyOperations = getMidjourneyOperations(aiImageDO); throw exception(AI_IMAGE_NOT_EXISTS);
// // 校验 OperateId 是否存在
// AiImageMidjourneyOperationsVO midjourneyOperationsVO = validateMidjourneyOperationsExists(midjourneyOperations, req.getOperateId());
// // 校验 messageId
// validateMessageId(aiImageDO.getMjNonceId(), req.getMessageId());
// // 获取 mjOperationName
// String mjOperationName = midjourneyOperationsVO.getLabel();
// // 保存一个 image 任务记录
// // todo
//// doSave(aiImageDO.getPrompt(), aiImageDO.getSize(), aiImageDO.getModel(),
//// null, null, AiImageStatusEnum.SUBMIT, null,
//// req.getMessageId(), req.getOperateId(), mjOperationName);
// // 提交操作
// midjourneyInteractionsApi.reRoll(
// new ReRollReq()
// .setCustomId(req.getOperateId())
// .setMessageId(req.getMessageId())
// );
}
@Override
public Boolean deleteIdMy(Long id, Long userId) {
// 校验是否存在并获取 image
AiImageDO image = validateExists(id);
// 是否属于当前用户
if (!image.getUserId().equals(userId)) {
throw exception(ErrorCodeConstants.AI_IMAGE_NOT_EXISTS);
} }
// 删除记录 // 删除记录
return imageMapper.deleteById(id) > 0; imageMapper.deleteById(id);
} }
@Override @Override
@ -255,11 +225,21 @@ public class AiImageServiceImpl implements AiImageService {
return true; return true;
} }
private AiImageDO validateExists(Long id) { private AiImageDO validateImageExists(Long id) {
AiImageDO aiImageDO = imageMapper.selectById(id); AiImageDO image = imageMapper.selectById(id);
if (aiImageDO == null) { if (image == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_IMAGINE_FAIL); throw exception(AI_IMAGE_NOT_EXISTS);
} }
return aiImageDO; return image;
} }
/**
* 获得自身的代理对象解决 AOP 生效问题
*
* @return 自己
*/
private AiImageServiceImpl getSelf() {
return SpringUtil.getBean(getClass());
}
} }