【代码优化】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;
import cn.hutool.core.util.ObjUtil;
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.util.object.BeanUtils;
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.AiImageListReqVO;
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.service.image.AiImageService;
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.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - Ai 绘画")
@Tag(name = "管理后台 - AI 绘画")
@RestController
@RequestMapping("/ai/image")
@Slf4j
public class AiImageController {
@Resource
private AiImageService aiImageService;
private AiImageService imageService;
@Operation(summary = "获取【我的】绘图分页")
@GetMapping("/my-page")
public CommonResult<PageResult<AiImagePageMyRespVO>> getImagePageMy(@Validated AiImageListReqVO req) {
// 转换 resp
PageResult<AiImageDO> pageResult = aiImageService.getImagePageMy(getLoginUserId(), req);
// 转换 PageResult<AiImageListRespVO> 返回
PageResult<AiImagePageMyRespVO> result = new PageResult<>();
result.setTotal(pageResult.getTotal());
result.setList(BeanUtils.toBean(pageResult.getList(), AiImagePageMyRespVO.class));
return success(result);
public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated PageParam pageReqVO) {
PageResult<AiImageDO> pageResult = imageService.getImagePageMy(getLoginUserId(), pageReqVO);
return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
}
// TODO @fan类似 /my-page 的建议
@Operation(summary = "获取【我的】绘图记录", description = "...")
@Operation(summary = "获取【我的】绘图记录")
@GetMapping("/get-my")
public CommonResult<AiImagePageMyRespVO> getMy(@RequestParam("id") Long id) {
// 获取 image 信息
AiImageDO imageDO = aiImageService.getMy(id);
// resp 并返回
return CommonResult.success(BeanUtils.toBean(imageDO, AiImagePageMyRespVO.class));
public CommonResult<AiImageRespVO> getImageMy(@RequestParam("id") Long id) {
AiImageDO image = imageService.getImage(id);
if (image == null || ObjUtil.notEqual(getLoginUserId(), image.getUserId())) {
return success(null);
}
return success(BeanUtils.toBean(image, AiImageRespVO.class));
}
// TODO @fan建议把 dallDrawingmidjourney 融合成一个 draw 接口异步绘制然后返回一个 id 给前端前端通过 get 接口轮询直到获取到生成成功
@ -58,14 +54,15 @@ public class AiImageController {
@Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!")
@PostMapping("/dall")
public CommonResult<Long> dall(@Validated @RequestBody AiImageDallReqVO req) {
return success(aiImageService.dall(getLoginUserId(), req));
return success(imageService.dall(getLoginUserId(), req));
}
@Operation(summary = "删除【我的】绘画记录")
@DeleteMapping("/delete-id-my")
@DeleteMapping("/delete-my")
@Parameter(name = "id", required = true, description = "绘画编号", example = "1024")
public CommonResult<Boolean> deleteIdMy(@RequestParam("id") Long id) {
return success(aiImageService.deleteIdMy(id, getLoginUserId()));
public CommonResult<Boolean> deleteImageMy(@RequestParam("id") Long id) {
imageService.deleteImageMy(id, getLoginUserId());
return success(true);
}
// ================ midjourney 接口
@ -73,12 +70,14 @@ public class AiImageController {
@Operation(summary = "midjourney-imagine 绘画", description = "...")
@PostMapping("/midjourney/imagine")
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 - 回调通知")
@RequestMapping("/midjourney-notify")
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;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Map;
// TODO @fan可以考虑复用 AiImageDallRespVO统一成 AIImageRespVO
/**
* midjourney req
*
* @author fansili
* @time 2024/4/28 17:42
* @since 1.0
*/
// TODO @芋艿完善 swagger 注解
@Data
@Accessors(chain = true)
public class AiImagePageMyRespVO extends PageParam {
public class AiImageRespVO {
@Schema(description = "id编号", example = "1")
private Long id;

View File

@ -1,5 +1,7 @@
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.query.LambdaQueryWrapperX;
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));
}
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;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
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.AiImageListReqVO;
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;
/**
* ai 作图
* AI 绘图 Service 接口
*
* @author fansili
* @time 2024/4/25 15:50
* @since 1.0
*/
public interface AiImageService {
/**
* ai绘画 - 列表
* 获取我的绘图分页
*
* @param loginUserId
* @param req
* @return
* @param userId 用户编号
* @param pageReqVO 分页条件
* @return 绘图分页
*/
PageResult<AiImageDO> getImagePageMy(Long loginUserId, AiImageListReqVO req);
PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO);
/**
* - image 信息
* 得绘图记录
*
* @param id
* @return
* @param id 绘图编号
* @return 绘图记录
*/
AiImageDO getMy(Long id);
AiImageDO getImage(Long id);
/**
* ai绘画 - dall2/dall3 绘画
@ -52,19 +49,12 @@ public interface AiImageService {
Long midjourneyImagine(Long loginUserId, AiImageMidjourneyImagineReqVO req);
/**
* midjourney 操作(u1u2放大换一批...)
* 删除我的绘画记录
*
* @param req
* @param id 绘画编号
* @param userId 用户编号
*/
void midjourneyOperate(AiImageMidjourneyOperateReqVO req);
/**
* 删除 - image 记录
*
* @param id
* @param loginUserId
*/
Boolean deleteIdMy(Long id, Long loginUserId);
void deleteImageMy(Long id, Long userId);
/**
* 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.util.StrUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpUtil;
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.OpenAiImageStyleEnum;
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.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.ErrorCodeConstants;
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.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.MidjourneySubmitRespVO;
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.AiImageMidjourneyOperateReqVO;
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.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.module.ai.ErrorCodeConstants.AI_IMAGE_NOT_EXISTS;
/**
* AI 绘画(接入 dall2/dall3midjourney)
* AI 绘画 Service 实现类
*
* @author fansili
* @time 2024/4/25 15:51
* @since 1.0
*/
@Service
@Slf4j
@ -59,10 +57,13 @@ public class AiImageServiceImpl implements AiImageService {
@Resource
private AiImageMapper imageMapper;
@Resource
private FileApi fileApi;
@Resource
private OpenAiImageClient openAiImageClient;
@Autowired
private MidjourneyProxyClient midjourneyProxyClient;
@ -70,16 +71,12 @@ public class AiImageServiceImpl implements AiImageService {
private String midjourneyNotifyUrl;
@Override
public PageResult<AiImageDO> getImagePageMy(Long loginUserId, AiImageListReqVO req) {
// 查询当前用户下所有的绘画记录
return imageMapper.selectPage(req,
new LambdaQueryWrapperX<AiImageDO>()
.eq(AiImageDO::getUserId, loginUserId)
.orderByDesc(AiImageDO::getId));
public PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO) {
return imageMapper.selectPage(userId, pageReqVO);
}
@Override
public AiImageDO getMy(Long id) {
public AiImageDO getImage(Long id) {
return imageMapper.selectById(id);
}
@ -95,7 +92,7 @@ public class AiImageServiceImpl implements AiImageService {
.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
imageMapper.insert(aiImageDO);
// 异步执行
doDall(aiImageDO, req);
getSelf().doDall(aiImageDO, req);
// 转换 AiImageDallDrawingRespVO
return aiImageDO.getId();
}
@ -185,42 +182,15 @@ public class AiImageServiceImpl implements AiImageService {
return aiImageDO.getId();
}
@Transactional(rollbackFor = Exception.class)
@Override
public void midjourneyOperate(AiImageMidjourneyOperateReqVO req) {
// // 校验是否存在
// AiImageDO aiImageDO = validateExists(req.getId());
// // 获取 midjourneyOperations
// List<AiImageMidjourneyOperationsVO> midjourneyOperations = getMidjourneyOperations(aiImageDO);
// // 校验 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);
public void deleteImageMy(Long id, Long userId) {
// 1. 校验是否存在
AiImageDO image = validateImageExists(id);
if (ObjUtil.notEqual(image.getUserId(), userId)) {
throw exception(AI_IMAGE_NOT_EXISTS);
}
// 删除记录
return imageMapper.deleteById(id) > 0;
imageMapper.deleteById(id);
}
@Override
@ -255,11 +225,21 @@ public class AiImageServiceImpl implements AiImageService {
return true;
}
private AiImageDO validateExists(Long id) {
AiImageDO aiImageDO = imageMapper.selectById(id);
if (aiImageDO == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_IMAGINE_FAIL);
private AiImageDO validateImageExists(Long id) {
AiImageDO image = imageMapper.selectById(id);
if (image == null) {
throw exception(AI_IMAGE_NOT_EXISTS);
}
return aiImageDO;
return image;
}
/**
* 获得自身的代理对象解决 AOP 生效问题
*
* @return 自己
*/
private AiImageServiceImpl getSelf() {
return SpringUtil.getBean(getClass());
}
}