diff --git a/.gitignore b/.gitignore index 18f34bd..c5abc96 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ docker/*/data/ docker/minio/config docker/xxljob/logs application-youlai.yml +.claude diff --git a/src/main/java/com/youlai/boot/mini/controller/AiGenerationController.java b/src/main/java/com/youlai/boot/mini/controller/AiGenerationController.java index 9b554a1..db2a621 100644 --- a/src/main/java/com/youlai/boot/mini/controller/AiGenerationController.java +++ b/src/main/java/com/youlai/boot/mini/controller/AiGenerationController.java @@ -159,4 +159,18 @@ public class AiGenerationController { aiGenerationService.setMiniProgramState(miniProgramState); return Result.success(); } + + //需要在三个回调接口中控制状态,更新其他状态; 更新状态时需要判断 当前状态已是成功或失败时不能再更新状态,当状态是失败时需要退还补偿积分; + // mini_point_rule积分规则表,每个回调回退的积分是对应的规则扣减积分的负数值;mini_point_record积分记录表;mini_point_account用户积分账户 + + + // 当外部服务挂掉,不回调我的接口时,需要根据时间段判断,如果超过一定时间未回调,则更新状态为失败,并退还积分; + + //TODO 现在 用户生成内容时,需要增加一个参数,同意或不同意公开生成的作品内容 + //TODO mini 中增加 查询 用户公开生成作品的接口,考虑设计一下查询规则 满足平台前中后期运营需求 + + //TODO admin 中 后台管理中增加 查询用户生成任务历史的接口 + //TODO admin 中 编辑用户生成作品历史接口 + //TODO admin 中 增加 手动查询生成任务接口,手动跳转生成失败任务,或做个接口给用户手动刷新查看,需要注意状态流转,只有超时未完成任务可出现刷新按钮 + } diff --git a/src/main/java/com/youlai/boot/mini/model/entity/MiniAiGenerationTask.java b/src/main/java/com/youlai/boot/mini/model/entity/MiniAiGenerationTask.java index 28af4d2..38d7dea 100644 --- a/src/main/java/com/youlai/boot/mini/model/entity/MiniAiGenerationTask.java +++ b/src/main/java/com/youlai/boot/mini/model/entity/MiniAiGenerationTask.java @@ -48,6 +48,10 @@ public class MiniAiGenerationTask implements Serializable { @Schema(description = "任务状态:0=生成中 1=成功 2=失败 3=超时 4=已取消") private Integer status; + @TableField("visibility") + @Schema(description = "可见范围:public-公开,private-仅自己可见,friends-好友可见") + private String visibility; + @TableField("result_resource_url") @Schema(description = "生成结果资源URL") private String resultResourceUrl; diff --git a/src/main/java/com/youlai/boot/mini/model/form/AiFourPanelGenerateForm.java b/src/main/java/com/youlai/boot/mini/model/form/AiFourPanelGenerateForm.java index d8cf303..899e794 100644 --- a/src/main/java/com/youlai/boot/mini/model/form/AiFourPanelGenerateForm.java +++ b/src/main/java/com/youlai/boot/mini/model/form/AiFourPanelGenerateForm.java @@ -51,4 +51,7 @@ public class AiFourPanelGenerateForm { @Schema(description = "故事梗概:AI会据此生成四格漫画脚本") private String description; + + @Schema(description = "可见范围: public公开 private仅自己", defaultValue = "private") + private String visibility; } diff --git a/src/main/java/com/youlai/boot/mini/model/form/AiSingleImageGenerateForm.java b/src/main/java/com/youlai/boot/mini/model/form/AiSingleImageGenerateForm.java index 2d09d56..bb9f866 100644 --- a/src/main/java/com/youlai/boot/mini/model/form/AiSingleImageGenerateForm.java +++ b/src/main/java/com/youlai/boot/mini/model/form/AiSingleImageGenerateForm.java @@ -51,4 +51,7 @@ public class AiSingleImageGenerateForm { @Schema(description = "场景描述:用户自定义生成场景") private String description; + + @Schema(description = "可见范围: public公开 private仅自己", defaultValue = "private") + private String visibility; } diff --git a/src/main/java/com/youlai/boot/mini/model/form/AiVideoGenerateForm.java b/src/main/java/com/youlai/boot/mini/model/form/AiVideoGenerateForm.java index 5ad08ec..1673eda 100644 --- a/src/main/java/com/youlai/boot/mini/model/form/AiVideoGenerateForm.java +++ b/src/main/java/com/youlai/boot/mini/model/form/AiVideoGenerateForm.java @@ -24,4 +24,7 @@ public class AiVideoGenerateForm { @Schema(description = "视频时长,单位秒,最大15秒", defaultValue = "5") private Integer duration = 5; + + @Schema(description = "可见范围: public公开 private仅自己", defaultValue = "private") + private String visibility; } diff --git a/src/main/java/com/youlai/boot/mini/service/impl/AiGenerationServiceImpl.java b/src/main/java/com/youlai/boot/mini/service/impl/AiGenerationServiceImpl.java index 7e4bb17..2430fa7 100644 --- a/src/main/java/com/youlai/boot/mini/service/impl/AiGenerationServiceImpl.java +++ b/src/main/java/com/youlai/boot/mini/service/impl/AiGenerationServiceImpl.java @@ -75,6 +75,8 @@ public class AiGenerationServiceImpl implements AiGenerationService { private static final String OSS_IMAGE_DIR = "ai/image/"; private static final String OSS_VIDEO_DIR = "ai/video/"; private static final String OSS_THUMBNAIL_DIR = "ai/thumbnail/"; + private static final String OSS_GENERATE_IMAGE_DIR = "ai/generate/image"; + private static final String OSS_GENERATE_VIDEO_DIR = "ai/generate/video"; //AI单图生成服务地址 @Value("${ai.generate.single-image-server-url:http://127.0.0.1:8001/api/v1/photo-to-comic}") @@ -255,6 +257,7 @@ public class AiGenerationServiceImpl implements AiGenerationService { MiniAiGenerationTask task = new MiniAiGenerationTask(); task.setUuid(taskUuid) .setMiniUserId(userId) + .setVisibility(form.getVisibility()) .setType("img_single") // 单图 .setGenerateParams(JSONUtil.toJsonStr(form)) .setPointsConsumed(Math.abs(deductPoint)) @@ -343,6 +346,7 @@ public class AiGenerationServiceImpl implements AiGenerationService { MiniAiGenerationTask task = new MiniAiGenerationTask(); task.setUuid(taskUuid) .setMiniUserId(userId) + .setVisibility(form.getVisibility()) .setType("img_grid_4") // 四宫格漫画 .setGenerateParams(JSONUtil.toJsonStr(form)) .setPointsConsumed(Math.abs(deductPoint)) @@ -427,6 +431,7 @@ public class AiGenerationServiceImpl implements AiGenerationService { // 创建生成任务 MiniAiGenerationTask task = new MiniAiGenerationTask(); task.setUuid(taskUuid) + .setVisibility(form.getVisibility()) .setMiniUserId(userId) .setType("video") .setGenerateParams(JSONUtil.toJsonStr(form)) @@ -440,7 +445,7 @@ public class AiGenerationServiceImpl implements AiGenerationService { // 组装第三方接口参数 Map aiRequest = new HashMap<>(); - aiRequest.put("model", form.getModel() == null ? aiDefaultVideoModel : form.getModel());//TODO写到上面 + aiRequest.put("model", form.getModel() == null ? aiDefaultVideoModel : form.getModel()); aiRequest.put("content", form.getContent()); aiRequest.put("resolution", form.getResolution()); aiRequest.put("duration", form.getDuration()); @@ -524,12 +529,8 @@ public class AiGenerationServiceImpl implements AiGenerationService { // 如果生成成功,下载外部视频到OSS if (status == 1 && vo.getContent() != null && vo.getContent().getVideoUrl() != null) { String externalVideoUrl = vo.getContent().getVideoUrl(); - // 调用下载方法,存储到视频目录 - String ossUrl = downloadExternalUrlToOss(externalVideoUrl, OSS_VIDEO_DIR); - task.setResultResourceUrl(ossUrl); - aiGenerationTaskMapper.updateById(task); + String ossUrl = downloadExternalUrlToOss(externalVideoUrl, OSS_GENERATE_VIDEO_DIR); - // 保存视频媒体记录 MiniAiTaskMedia media = new MiniAiTaskMedia(); String mediaUuid = UUID.randomUUID().toString().replace("-", ""); media.setUuid(mediaUuid) @@ -598,44 +599,41 @@ public class AiGenerationServiceImpl implements AiGenerationService { task.setUpdateTimestamp(System.currentTimeMillis()); aiGenerationTaskMapper.updateById(task); - // 如果生成成功,下载外部URL到OSS + // 如果生成成功,下载外部URL到OSS,存储所有结果图片 if (status == 1 && form.getResult() != null && !form.getResult().isEmpty()) { - String externalImageUrl = form.getResult().get(0).getUrl(); - // 调用下载方法,存储到图片目录 - String ossUrl = downloadExternalUrlToOss(externalImageUrl); - task.setResultResourceUrl(ossUrl); - aiGenerationTaskMapper.updateById(task); - - // 保存生成的媒体记录 - MiniAiTaskMedia media = new MiniAiTaskMedia(); - String mediaUuid = UUID.randomUUID().toString().replace("-", ""); - AiCallbackImage image = form.getResult().get(0); - // 解析尺寸 - Integer width = null; - Integer height = null; - if (image.getSize() != null && image.getSize().contains("x")) { - String[] sizeArr = image.getSize().split("x"); - try { - width = Integer.parseInt(sizeArr[0]); - height = Integer.parseInt(sizeArr[1]); - } catch (Exception e) { - log.warn("解析四宫格图片尺寸失败,size:{}", image.getSize(), e); + long now = System.currentTimeMillis(); + + for (AiCallbackImage image : form.getResult()) { + String ossUrl = downloadExternalUrlToOss(image.getUrl(), OSS_GENERATE_IMAGE_DIR); + + // 解析尺寸 + Integer width = null; + Integer height = null; + if (image.getSize() != null && image.getSize().contains("x")) { + String[] sizeArr = image.getSize().split("x"); + try { + width = Integer.parseInt(sizeArr[0]); + height = Integer.parseInt(sizeArr[1]); + } catch (Exception e) { + log.warn("解析四宫格图片尺寸失败,size:{}", image.getSize(), e); + } } - } - media.setUuid(mediaUuid) - .setTaskId(task.getId()) - .setMiniUserId(task.getMiniUserId()) - .setFileSource("ai_generate") - .setMediaType("image") - .setSourceUrl(ossUrl) - .setWidth(width) - .setHeight(height) - .setCreateBy(task.getCreateBy()) - .setCreateTimestamp(System.currentTimeMillis()) - .setCreateTime(new Date()); - - aiTaskMediaMapper.insert(media); + MiniAiTaskMedia media = new MiniAiTaskMedia(); + String mediaUuid = UUID.randomUUID().toString().replace("-", ""); + media.setUuid(mediaUuid) + .setTaskId(task.getId()) + .setMiniUserId(task.getMiniUserId()) + .setFileSource("ai_generate") + .setMediaType("image") + .setSourceUrl(ossUrl) + .setWidth(width) + .setHeight(height) + .setCreateBy(task.getCreateBy()) + .setCreateTimestamp(now) + .setCreateTime(new Date(now)); + aiTaskMediaMapper.insert(media); + } // 更新用户上传的参考文件:关联当前任务ID并软删除 LambdaUpdateWrapper updateMediaWrapper = new LambdaUpdateWrapper<>(); @@ -662,6 +660,7 @@ public class AiGenerationServiceImpl implements AiGenerationService { } @Override + @Transactional(rollbackFor = Exception.class) public boolean handleTaskCallback(AiSingleImageCallbackForm form) { log.info("处理单图生成任务回调,任务UUID:{}", form.getUuid()); try { @@ -684,45 +683,45 @@ public class AiGenerationServiceImpl implements AiGenerationService { return false; } - // 如果生成成功,下载外部URL到OSS - String ossUrl = null; + // 如果生成成功,下载外部URL到OSS,存储所有结果图片 if (status == 1 && form.getResult() != null && !form.getResult().isEmpty()) { - String externalResultUrl = form.getResult().get(0).getUrl(); - ossUrl = downloadExternalUrlToOss(externalResultUrl); - - // 保存生成的媒体记录 - MiniAiTaskMedia media = new MiniAiTaskMedia(); - String mediaUuid = UUID.randomUUID().toString().replace("-", ""); - AiCallbackImage image = form.getResult().get(0); - // 解析尺寸 - Integer width = null; - Integer height = null; - if (image.getSize() != null && image.getSize().contains("x")) { - String[] sizeArr = image.getSize().split("x"); - try { - width = Integer.parseInt(sizeArr[0]); - height = Integer.parseInt(sizeArr[1]); - } catch (Exception e) { - log.warn("解析单图图片尺寸失败,size:{}", image.getSize(), e); + long now = System.currentTimeMillis(); + + for (AiCallbackImage image : form.getResult()) { + String ossUrl = downloadExternalUrlToOss(image.getUrl(), OSS_GENERATE_IMAGE_DIR); + + // 解析尺寸 + Integer width = null; + Integer height = null; + if (image.getSize() != null && image.getSize().contains("x")) { + String[] sizeArr = image.getSize().split("x"); + try { + width = Integer.parseInt(sizeArr[0]); + height = Integer.parseInt(sizeArr[1]); + } catch (Exception e) { + log.warn("解析单图图片尺寸失败,size:{}", image.getSize(), e); + } } - } - - media.setUuid(mediaUuid) - .setMiniUserId(task.getMiniUserId()) - .setTaskId(task.getId()) - .setFileSource("ai_generated") - .setMediaType("image") - .setSourceUrl(ossUrl) - .setWidth(width) - .setHeight(height) - .setCreateBy(task.getCreateBy()) - .setCreateTime(new Date()) - .setCreateTimestamp(System.currentTimeMillis()) - .setUpdateTime(new Date()) - .setUpdateTimestamp(System.currentTimeMillis()) - .setDeleted(false); - aiTaskMediaMapper.insert(media); + MiniAiTaskMedia media = new MiniAiTaskMedia(); + String mediaUuid = UUID.randomUUID().toString().replace("-", ""); + media.setUuid(mediaUuid) + .setMiniUserId(task.getMiniUserId()) + .setTaskId(task.getId()) + .setFileSource("ai_generated") + .setMediaType("image") + .setSourceUrl(ossUrl) + .setWidth(width) + .setHeight(height) + .setCreateBy(task.getCreateBy()) + .setCreateTime(new Date(now)) + .setCreateTimestamp(now) + .setUpdateTime(new Date(now)) + .setUpdateTimestamp(now) + .setDeleted(false); + + aiTaskMediaMapper.insert(media); + } // 更新用户上传的参考文件:关联当前任务ID并软删除 LambdaUpdateWrapper updateMediaWrapper = new LambdaUpdateWrapper<>(); @@ -741,7 +740,7 @@ public class AiGenerationServiceImpl implements AiGenerationService { } // 更新任务状态 - return updateTaskStatus(taskUuid, status, ossUrl); + return updateTaskStatus(taskUuid, status, null); } catch (Exception e) { log.error("处理AI任务回调异常,UUID:{},异常信息:{}", form.getUuid(), e.getMessage(), e); @@ -795,13 +794,6 @@ public class AiGenerationServiceImpl implements AiGenerationService { } } - /** - * 下载外部URL到OSS,返回OSS访问地址 - */ - private String downloadExternalUrlToOss(String externalUrl) { - return downloadExternalUrlToOss(externalUrl, "ai/generate/"); - } - /** * 下载外部URL到指定OSS目录,返回OSS访问地址 */