package com.youlai.boot.mini.controller; import com.youlai.boot.common.annotation.Log; import com.youlai.boot.common.annotation.RepeatSubmit; import com.youlai.boot.common.enums.ActionTypeEnum; import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.common.result.Result; import com.youlai.boot.framework.security.util.SecurityUtils; import com.youlai.boot.mini.model.form.*; import com.youlai.boot.mini.model.vo.AiVideoCallbackVO; import com.youlai.boot.mini.service.AiGenerationService; import com.youlai.boot.mini.service.WxSubscribeService; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Pattern; import org.springframework.security.access.prepost.PreAuthorize; import com.baomidou.mybatisplus.core.metadata.IPage; import com.youlai.boot.mini.model.query.AiTaskMediaQuery; import com.youlai.boot.mini.model.vo.AiGenerationTaskVO; import cn.hutool.core.util.StrUtil; import java.util.List; import com.youlai.boot.mini.model.vo.UserUploadMediaVO; @Tag(name = "AI生成相关接口") @RestController @RequestMapping("/api/v1/mini/ai/generation") @RequiredArgsConstructor @Valid public class AiGenerationController { private final AiGenerationService aiGenerationService; private final WxSubscribeService wxSubscribeService; @Operation(summary = "上传AI生成参考文件") @PostMapping(value = "/upload/reference", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @RepeatSubmit @Log(module = LogModuleEnum.AI_TASK_MEDIA, value = ActionTypeEnum.INSERT) public Result> uploadReferenceFile( @RequestParam(name = "images", required = false) List images, @RequestParam(name = "videos", required = false) List videos ) { Long userId = SecurityUtils.getUserId(); List urlList = aiGenerationService.uploadReferenceFile(images, videos, userId); return Result.success(urlList); } @Operation(summary = "提交单图生成任务") @PostMapping("/generate-single-image") @Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.INSERT) public Result generateSingleImage(@Valid @RequestBody AiSingleImageGenerateForm form) { Long userId = SecurityUtils.getUserId(); String taskUuid = aiGenerationService.createAndGenerateImage(form, userId); return Result.success(taskUuid); } @Operation(summary = "单图任务回调接口") @PostMapping("/single-image/task/callback") @Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.UPDATE) public Result singleImageTaskCallback(@Valid @RequestBody AiSingleImageCallbackForm form) { boolean success = aiGenerationService.handleTaskCallback(form); return Result.success(success); } @Operation(summary = "提交四宫格漫画生成任务") @PostMapping("/generate-four-panel") @Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.INSERT) public Result generateFourPanelImage(@Valid @RequestBody AiFourPanelGenerateForm form) { Long userId = SecurityUtils.getUserId(); String taskUuid = aiGenerationService.createAndGenerateFourPanel(form, userId); return Result.success(taskUuid); } @Operation(summary = "四宫格漫画任务回调接口") @PostMapping("/four-panel/task/callback") @Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.UPDATE) public Result fourPanelTaskCallback(@Valid @RequestBody AiFourPanelCallbackForm form) { boolean success = aiGenerationService.handleFourPanelCallback(form); return Result.success(success); } @Operation(summary = "提交视频生成任务") @PostMapping("/generate-video") @Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.INSERT) public Result generateVideo(@Valid @RequestBody AiVideoGenerateForm form) { Long userId = SecurityUtils.getUserId(); String taskUuid = aiGenerationService.createAndGenerateVideo(form, userId); return Result.success(taskUuid); } @Operation(summary = "视频任务回调接口") @PostMapping("/video/task/callback") @Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.UPDATE) public Result videoTaskCallback(@Valid @RequestBody AiVideoCallbackVO vo) { boolean success = aiGenerationService.handleVideoTaskCallback(vo); return Result.success(success); } @Operation(summary = "上报微信订阅消息授权状态") @PostMapping("/subscribe/auth/report") @Log(module = LogModuleEnum.USER_SUBSCRIBE, value = ActionTypeEnum.UPDATE) public Result reportSubscribeAuth(@Valid @RequestBody WxSubscribeAuthForm form) { form.setUserId(SecurityUtils.getUserId()); wxSubscribeService.reportAuth(form); return Result.success(); } @Hidden @Operation(summary = "发送微信订阅消息") @PostMapping("/subscribe/send") @Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.INSERT) public Result sendSubscribeMessage(@Valid @RequestBody WxSubscribeSendForm form) { form.setUserId(SecurityUtils.getUserId()); Boolean result = wxSubscribeService.sendMessage(form); return Result.success(result); } @Operation(summary = "查询当前用户最近上传的图片视频") @GetMapping("/my/recent-upload") public Result> getMyRecentUpload() { Long userId = SecurityUtils.getUserId(); List voList = aiGenerationService.getRecentUploadVO(userId); return Result.success(voList); } @Operation(summary = "删除用户上传的图片/视频") @DeleteMapping("/my/upload/delete") @Log(module = LogModuleEnum.AI_TASK_MEDIA, value = ActionTypeEnum.DELETE) public Result deleteMyUpload(@RequestParam String uuid) { Long userId = SecurityUtils.getUserId(); boolean success = aiGenerationService.deleteUploadMedia(userId, uuid); return Result.success(success); } @Operation(summary = "查询我的AI生成任务历史") @GetMapping("/my/history") @Log(module = LogModuleEnum.AI_TASK_MEDIA, value = ActionTypeEnum.LIST) public Result> getMyAiGenerateHistory(@Valid AiTaskMediaQuery query) { Long userId = SecurityUtils.getUserId(); IPage result = aiGenerationService.getMyAiGenerateHistory(query, userId); return Result.success(result); } @Operation(summary = "设置微信订阅消息跳转版本") @PostMapping("/subscribe/mini-program-state/set") @Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.UPDATE) public Result setMiniProgramState( @Schema(description = "跳转版本:developer=开发版 trial=体验版 formal=正式版,为空时删除配置") @Pattern(regexp = "^(developer|trial|formal|)$", message = "参数只能是developer/trial/formal或为空") @RequestParam(required = false) String miniProgramState ) { aiGenerationService.setMiniProgramState(miniProgramState); return Result.success(); } //需要在三个回调接口中控制状态,更新其他状态; 更新状态时需要判断 当前状态已是成功或失败时不能再更新状态,当状态是失败时需要退还补偿积分; // mini_point_rule积分规则表,每个回调回退的积分是对应的规则扣减积分的负数值;mini_point_record积分记录表;mini_point_account用户积分账户 // 当外部服务挂掉,不回调我的接口时,需要根据时间段判断,如果超过一定时间未回调,则更新状态为失败,并退还积分; //TODO 现在 用户生成内容时,需要增加一个参数,同意或不同意公开生成的作品内容 //TODO mini 中增加 查询 用户公开生成作品的接口,考虑设计一下查询规则 满足平台前中后期运营需求 //TODO admin 中 后台管理中增加 查询用户生成任务历史的接口 //TODO admin 中 编辑用户生成作品历史接口 //TODO admin 中 增加 手动查询生成任务接口,手动跳转生成失败任务,或做个接口给用户手动刷新查看,需要注意状态流转,只有超时未完成任务可出现刷新按钮 }