Compare commits
16 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
b4ca8a6e12 | 20 hours ago |
|
|
98e4be3826 | 2 days ago |
|
|
481d72888d | 2 days ago |
|
|
9aa59d888a | 2 days ago |
|
|
89b0e34eb0 | 3 days ago |
|
|
e76df77ce5 | 4 days ago |
|
|
1b665c2f2c | 5 days ago |
|
|
784b9b4a13 | 1 week ago |
|
|
9fdb7d1900 | 1 week ago |
|
|
4d1a22f5de | 2 weeks ago |
|
|
3b73dc2135 | 2 weeks ago |
|
|
65fc5f0a81 | 2 weeks ago |
|
|
d138d40869 | 2 weeks ago |
|
|
e5489fe290 | 2 weeks ago |
|
|
90ef9bd7b2 | 2 weeks ago |
|
|
3a1a0c94c4 | 3 weeks ago |
47 changed files with 3001 additions and 13 deletions
@ -0,0 +1,200 @@ |
|||||
|
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.query.DiscoveryPublicWorkQuery; |
||||
|
import com.youlai.boot.mini.model.entity.MiniAiGenerationTask; |
||||
|
import com.youlai.boot.mini.model.vo.AiGenerationTaskVO; |
||||
|
import com.youlai.boot.mini.model.vo.DiscoveryPublicWorkVO; |
||||
|
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<List<String>> uploadReferenceFile( |
||||
|
@RequestParam(name = "images", required = false) List<MultipartFile> images, |
||||
|
@RequestParam(name = "videos", required = false) List<MultipartFile> videos |
||||
|
) { |
||||
|
Long userId = SecurityUtils.getUserId(); |
||||
|
List<String> 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<String> 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<Boolean> 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<String> 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<Boolean> 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<String> 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<Boolean> 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<Void> 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<Boolean> 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<List<UserUploadMediaVO>> getMyRecentUpload() { |
||||
|
Long userId = SecurityUtils.getUserId(); |
||||
|
List<UserUploadMediaVO> voList = aiGenerationService.getRecentUploadVO(userId); |
||||
|
return Result.success(voList); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "删除用户上传的图片/视频") |
||||
|
@PostMapping("/my/upload/delete") |
||||
|
@Log(module = LogModuleEnum.AI_TASK_MEDIA, value = ActionTypeEnum.DELETE) |
||||
|
public Result<Boolean> 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<IPage<AiGenerationTaskVO>> getMyAiGenerateHistory(@Valid AiTaskMediaQuery query) { |
||||
|
Long userId = SecurityUtils.getUserId(); |
||||
|
IPage<AiGenerationTaskVO> 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<Void> 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(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "更新任务可见范围") |
||||
|
@PostMapping("/my/task/visibility") |
||||
|
@Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.UPDATE) |
||||
|
public Result<Void> updateTaskVisibility(@Valid @RequestBody AiTaskVisibilityForm form) { |
||||
|
Long userId = SecurityUtils.getUserId(); |
||||
|
aiGenerationService.updateTaskVisibility(userId, form); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "查询公开作品发现页") |
||||
|
@GetMapping("/discovery/feed") |
||||
|
@Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.LIST) |
||||
|
public Result<IPage<DiscoveryPublicWorkVO>> getPublicDiscoveryFeed(@Valid DiscoveryPublicWorkQuery query) { |
||||
|
IPage<DiscoveryPublicWorkVO> result = aiGenerationService.getPublicDiscoveryFeed(query); |
||||
|
return Result.success(result); |
||||
|
} |
||||
|
|
||||
|
@Operation(summary = "手动同步AI任务状态") |
||||
|
@PostMapping("/task/{taskUuid}/sync-status") |
||||
|
@RepeatSubmit(expire = 60) |
||||
|
@Log(module = LogModuleEnum.AI_GENERATION_TASK, value = ActionTypeEnum.UPDATE) |
||||
|
public Result<Void> syncTaskStatus(@PathVariable String taskUuid) { |
||||
|
Long userId = SecurityUtils.getUserId(); |
||||
|
MiniAiGenerationTask task = aiGenerationService.getTaskByUuid(taskUuid); |
||||
|
if (task == null) { |
||||
|
return Result.failed("任务不存在"); |
||||
|
} |
||||
|
if (!task.getMiniUserId().equals(userId)) { |
||||
|
return Result.failed("无权操作该任务"); |
||||
|
} |
||||
|
aiGenerationService.syncTaskStatus(taskUuid); |
||||
|
return Result.success(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
package com.youlai.boot.mini.job; |
||||
|
|
||||
|
import com.youlai.boot.mini.service.AiGenerationService; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.scheduling.annotation.Scheduled; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@RequiredArgsConstructor |
||||
|
public class AiTaskTimeoutJob { |
||||
|
|
||||
|
private final AiGenerationService aiGenerationService; |
||||
|
|
||||
|
/** |
||||
|
* 每10分钟扫描一次超时的AI生成任务,标记为超时并退还积分 |
||||
|
*/ |
||||
|
@Scheduled(cron = "0 */10 * * * ?") |
||||
|
public void execute() { |
||||
|
log.info("开始扫描超时AI生成任务"); |
||||
|
try { |
||||
|
aiGenerationService.handleTimeoutTasks(); |
||||
|
} catch (Exception e) { |
||||
|
log.error("扫描超时AI生成任务异常", e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
package com.youlai.boot.mini.mapper; |
||||
|
|
||||
|
import com.youlai.boot.mini.model.entity.MiniAiGenerationTask; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* AI生成任务统一表 Mapper 接口 |
||||
|
* |
||||
|
* @author jwy |
||||
|
* @since |
||||
|
*/ |
||||
|
public interface MiniAiGenerationTaskMapper extends BaseMapper<MiniAiGenerationTask> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
package com.youlai.boot.mini.mapper; |
||||
|
|
||||
|
import com.youlai.boot.mini.model.entity.MiniAiTaskMedia; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* AI任务媒体文件表 Mapper 接口 |
||||
|
* |
||||
|
* @author jwy |
||||
|
* @since |
||||
|
*/ |
||||
|
public interface MiniAiTaskMediaMapper extends BaseMapper<MiniAiTaskMedia> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
package com.youlai.boot.mini.mapper; |
||||
|
|
||||
|
import com.youlai.boot.mini.model.entity.MiniUserSubscribe; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
import org.apache.ibatis.annotations.Select; |
||||
|
|
||||
|
/** |
||||
|
* 用户订阅状态表 Mapper 接口 |
||||
|
* |
||||
|
* @author jwy |
||||
|
* @since |
||||
|
*/ |
||||
|
public interface MiniUserSubscribeMapper extends BaseMapper<MiniUserSubscribe> { |
||||
|
|
||||
|
String getOpenidByUserId(Long aLong); |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
package com.youlai.boot.mini.model.dto; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* AI生成接口响应DTO |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
public class GenerateResponse { |
||||
|
/** |
||||
|
* 响应消息 |
||||
|
*/ |
||||
|
private String msg; |
||||
|
|
||||
|
/** |
||||
|
* 响应码,0表示成功 |
||||
|
*/ |
||||
|
private Integer code; |
||||
|
|
||||
|
/** |
||||
|
* 生成结果数据 |
||||
|
*/ |
||||
|
private GenerationData data; |
||||
|
|
||||
|
/** |
||||
|
* 请求ID |
||||
|
*/ |
||||
|
private String request_id; |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
package com.youlai.boot.mini.model.dto; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* AI生成结果数据DTO |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
public class GenerationData { |
||||
|
/** |
||||
|
* 使用的模型ID |
||||
|
*/ |
||||
|
private String model; |
||||
|
|
||||
|
/** |
||||
|
* 生成的图片列表 |
||||
|
*/ |
||||
|
private List<ImageData> data; |
||||
|
|
||||
|
/** |
||||
|
* 错误信息,无错误则为null |
||||
|
*/ |
||||
|
private Object error; |
||||
|
|
||||
|
/** |
||||
|
* Token使用统计 |
||||
|
*/ |
||||
|
private Object usage; |
||||
|
|
||||
|
/** |
||||
|
* 创建时间戳 |
||||
|
*/ |
||||
|
private Long created_at; |
||||
|
|
||||
|
/** |
||||
|
* 使用的工具 |
||||
|
*/ |
||||
|
private String tool; |
||||
|
|
||||
|
/** |
||||
|
* 创建时间戳 |
||||
|
*/ |
||||
|
private Long created; |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
package com.youlai.boot.mini.model.dto; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* 生成的图片数据DTO |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
public class ImageData { |
||||
|
/** |
||||
|
* 图片URL地址 |
||||
|
*/ |
||||
|
private String url; |
||||
|
|
||||
|
/** |
||||
|
* Base64编码的图片JSON |
||||
|
*/ |
||||
|
private String b64_json; |
||||
|
|
||||
|
/** |
||||
|
* 图片尺寸,格式如 1664x2496 |
||||
|
*/ |
||||
|
private String size; |
||||
|
} |
||||
@ -0,0 +1,94 @@ |
|||||
|
package com.youlai.boot.mini.model.entity; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.*; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
import com.fasterxml.jackson.annotation.JsonFormat; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString |
||||
|
@Accessors(chain = true) |
||||
|
@TableName("mini_ai_generation_task") |
||||
|
@Schema(description = "AI生成任务统一表") |
||||
|
public class MiniAiGenerationTask implements Serializable { |
||||
|
|
||||
|
@TableId(value = "id", type = IdType.AUTO) |
||||
|
@Schema(description = "笔记ID") |
||||
|
private Long id; |
||||
|
|
||||
|
|
||||
|
@TableField("uuid") |
||||
|
@Schema(description = "uuid唯一标识,前后端用这个进行数据交互") |
||||
|
private String uuid; |
||||
|
|
||||
|
@TableField("refund_uuid") |
||||
|
@Schema(description = "退积分唯一uuid") |
||||
|
private String refundUuid; |
||||
|
|
||||
|
@TableField("mini_user_id") |
||||
|
@Schema(description = "作者用户ID") |
||||
|
private Long miniUserId; |
||||
|
|
||||
|
@TableField("type") |
||||
|
@Schema(description = "生成类型:img_single, img_grid_4, video") |
||||
|
private String type; |
||||
|
|
||||
|
@TableField("generate_params") |
||||
|
@Schema(description = "生成参数JSON,不同类型参数不同") |
||||
|
private String generateParams; |
||||
|
|
||||
|
@TableField("video_task_uuid") |
||||
|
@Schema(description = "调用视频接口时返回") |
||||
|
private String videoTaskUuid; |
||||
|
|
||||
|
@TableField("status") |
||||
|
@Schema(description = "任务状态:0=生成中 1=成功 2=失败 3=超时 4=已取消") |
||||
|
private Integer status; |
||||
|
|
||||
|
@TableField("visibility") |
||||
|
@Schema(description = "可见范围:public-公开,private-仅自己可见,friends-好友可见") |
||||
|
private String visibility; |
||||
|
|
||||
|
@TableField("points_consumed") |
||||
|
@Schema(description = "消耗积分") |
||||
|
private Integer pointsConsumed; |
||||
|
|
||||
|
@TableField("create_time") |
||||
|
@Schema(description = "创建时间") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date createTime; |
||||
|
|
||||
|
@TableField("create_timestamp") |
||||
|
@Schema(description = "创建时间毫秒级时间戳") |
||||
|
private Long createTimestamp; |
||||
|
|
||||
|
@TableField("create_by") |
||||
|
@Schema(description = "创建人ID") |
||||
|
private Long createBy; |
||||
|
|
||||
|
@TableField("update_time") |
||||
|
@Schema(description = "更新时间") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date updateTime; |
||||
|
|
||||
|
@TableField("update_timestamp") |
||||
|
@Schema(description = "更新时间毫秒级时间戳") |
||||
|
private Long updateTimestamp; |
||||
|
|
||||
|
@TableField("update_by") |
||||
|
@Schema(description = "修改人ID") |
||||
|
private Long updateBy; |
||||
|
|
||||
|
@TableField("is_deleted") |
||||
|
@Schema(description = "逻辑删除标识(0-未删除 1-已删除)") |
||||
|
private Boolean deleted; |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,102 @@ |
|||||
|
package com.youlai.boot.mini.model.entity; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.*; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
import com.fasterxml.jackson.annotation.JsonFormat; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString |
||||
|
@Accessors(chain = true) |
||||
|
@TableName("mini_ai_task_media") |
||||
|
@Schema(description = "AI任务媒体文件表") |
||||
|
public class MiniAiTaskMedia implements Serializable { |
||||
|
|
||||
|
@TableId(value = "id", type = IdType.AUTO) |
||||
|
@Schema(description = "") |
||||
|
private Long id; |
||||
|
|
||||
|
|
||||
|
@TableField("uuid") |
||||
|
@Schema(description = "uuid唯一标识,前后端用这个进行数据交互") |
||||
|
private String uuid; |
||||
|
|
||||
|
@TableField("mini_user_id") |
||||
|
@Schema(description = "用户id") |
||||
|
private Long miniUserId; |
||||
|
|
||||
|
@TableField("task_id") |
||||
|
@Schema(description = "AI生成任务id") |
||||
|
private Long taskId; |
||||
|
|
||||
|
@TableField("file_source") |
||||
|
@Schema(description = "文件来源:user_upload(用户上传), ai_generated(AI生成)") |
||||
|
private String fileSource; |
||||
|
|
||||
|
@TableField("media_type") |
||||
|
@Schema(description = "媒体类型,image-图片,video-视频") |
||||
|
private String mediaType; |
||||
|
|
||||
|
@TableField("source_url") |
||||
|
@Schema(description = "资源URL") |
||||
|
private String sourceUrl; |
||||
|
|
||||
|
@TableField("storage_key") |
||||
|
@Schema(description = "对象存储中的key") |
||||
|
private String storageKey; |
||||
|
|
||||
|
@TableField("thumbnail_url") |
||||
|
@Schema(description = "缩略图URL(视频需要)") |
||||
|
private String thumbnailUrl; |
||||
|
|
||||
|
@TableField("width") |
||||
|
@Schema(description = "宽度(像素)") |
||||
|
private Integer width; |
||||
|
|
||||
|
@TableField("height") |
||||
|
@Schema(description = "高度(像素)") |
||||
|
private Integer height; |
||||
|
|
||||
|
@TableField("duration") |
||||
|
@Schema(description = "时长(秒,视频用)") |
||||
|
private Integer duration; |
||||
|
|
||||
|
@TableField("create_time") |
||||
|
@Schema(description = "创建时间") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date createTime; |
||||
|
|
||||
|
@TableField("create_timestamp") |
||||
|
@Schema(description = "创建时间毫秒级时间戳") |
||||
|
private Long createTimestamp; |
||||
|
|
||||
|
@TableField("create_by") |
||||
|
@Schema(description = "创建人ID") |
||||
|
private Long createBy; |
||||
|
|
||||
|
@TableField("update_time") |
||||
|
@Schema(description = "更新时间") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date updateTime; |
||||
|
|
||||
|
@TableField("update_timestamp") |
||||
|
@Schema(description = "更新时间毫秒级时间戳") |
||||
|
private Long updateTimestamp; |
||||
|
|
||||
|
@TableField("update_by") |
||||
|
@Schema(description = "修改人ID") |
||||
|
private Long updateBy; |
||||
|
|
||||
|
@TableField("is_deleted") |
||||
|
@Schema(description = "逻辑删除标识(0-未删除 1-已删除)") |
||||
|
private Boolean deleted; |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
package com.youlai.boot.mini.model.entity; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.*; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
import lombok.ToString; |
||||
|
import lombok.experimental.Accessors; |
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
import com.fasterxml.jackson.annotation.JsonFormat; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@ToString |
||||
|
@Accessors(chain = true) |
||||
|
@TableName("mini_user_subscribe") |
||||
|
@Schema(description = "用户订阅状态表") |
||||
|
public class MiniUserSubscribe implements Serializable { |
||||
|
|
||||
|
@TableId(value = "id", type = IdType.AUTO) |
||||
|
@Schema(description = "") |
||||
|
private Long id; |
||||
|
|
||||
|
|
||||
|
@TableField("uuid") |
||||
|
@Schema(description = "uuid唯一标识,前后端用这个进行数据交互") |
||||
|
private String uuid; |
||||
|
|
||||
|
@TableField("user_id") |
||||
|
@Schema(description = "用户id") |
||||
|
private Long userId; |
||||
|
|
||||
|
@TableField("template_id") |
||||
|
@Schema(description = "订阅消息模板ID") |
||||
|
private String templateId; |
||||
|
|
||||
|
@TableField("status") |
||||
|
@Schema(description = "授权状态:0=拒绝 1=同意一次 2=总是同意") |
||||
|
private Integer status; |
||||
|
|
||||
|
@TableField("create_time") |
||||
|
@Schema(description = "创建时间") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date createTime; |
||||
|
|
||||
|
@TableField("create_timestamp") |
||||
|
@Schema(description = "创建时间毫秒级时间戳") |
||||
|
private Long createTimestamp; |
||||
|
|
||||
|
@TableField("create_by") |
||||
|
@Schema(description = "创建人ID") |
||||
|
private Long createBy; |
||||
|
|
||||
|
@TableField("update_time") |
||||
|
@Schema(description = "更新时间") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
||||
|
private Date updateTime; |
||||
|
|
||||
|
@TableField("update_timestamp") |
||||
|
@Schema(description = "更新时间毫秒级时间戳") |
||||
|
private Long updateTimestamp; |
||||
|
|
||||
|
@TableField("update_by") |
||||
|
@Schema(description = "修改人ID") |
||||
|
private Long updateBy; |
||||
|
|
||||
|
@TableField("is_deleted") |
||||
|
@Schema(description = "逻辑删除标识(0-未删除 1-已删除)") |
||||
|
private Boolean deleted; |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
package com.youlai.boot.mini.model.enums; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonCreator; |
||||
|
import com.fasterxml.jackson.annotation.JsonValue; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
|
||||
|
|
||||
|
@Schema(description = "微信订阅消息授权状态") |
||||
|
public enum SubscribeStatusEnum { |
||||
|
|
||||
|
REJECT(0, "拒绝授权"), |
||||
|
ONCE(1, "同意一次"), |
||||
|
ALWAYS(2, "总是同意"); |
||||
|
|
||||
|
private final Integer value; |
||||
|
private final String desc; |
||||
|
|
||||
|
SubscribeStatusEnum(Integer value, String desc) { |
||||
|
this.value = value; |
||||
|
this.desc = desc; |
||||
|
} |
||||
|
|
||||
|
@JsonValue |
||||
|
public Integer getValue() { |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
public String getDesc() { |
||||
|
return desc; |
||||
|
} |
||||
|
|
||||
|
@JsonCreator |
||||
|
public static SubscribeStatusEnum from(Integer value) { |
||||
|
if (value == null) return null; |
||||
|
for (SubscribeStatusEnum e : values()) { |
||||
|
if (e.value.equals(value)) { |
||||
|
return e; |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public static boolean contains(Integer value) { |
||||
|
return from(value) != null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* AI回调图片通用结构 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@Schema(description = "AI回调图片通用结构") |
||||
|
public class AiCallbackImage { |
||||
|
|
||||
|
@Schema(description = "图片URL地址") |
||||
|
private String url; |
||||
|
|
||||
|
@JsonProperty("b64_json") |
||||
|
@Schema(description = "Base64编码的图片JSON") |
||||
|
private String b64Json; |
||||
|
|
||||
|
@Schema(description = "图片尺寸,格式如 2048x2048") |
||||
|
private String size; |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import jakarta.validation.constraints.NotBlank; |
||||
|
import lombok.Data; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 四宫格漫画任务回调请求表单 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@Schema(description = "四宫格漫画任务回调请求表单") |
||||
|
public class AiFourPanelCallbackForm { |
||||
|
|
||||
|
@NotBlank(message = "任务UUID不能为空") |
||||
|
@Schema(description = "任务唯一标识UUID", requiredMode = Schema.RequiredMode.REQUIRED) |
||||
|
private String uuid; |
||||
|
|
||||
|
@Schema(description = "任务状态:succeeded=成功,failed=失败") |
||||
|
private String status; |
||||
|
|
||||
|
@Schema(description = "生成的图片列表") |
||||
|
private List<AiCallbackImage> result; |
||||
|
|
||||
|
@JsonProperty("created_at") |
||||
|
@Schema(description = "创建时间戳") |
||||
|
private Long createdAt; |
||||
|
|
||||
|
@JsonProperty("updated_at") |
||||
|
@Schema(description = "更新时间戳") |
||||
|
private Long updatedAt; |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* AI四宫格漫画生成请求表单 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@Schema(description = "AI四宫格漫画生成请求表单") |
||||
|
public class AiFourPanelGenerateForm { |
||||
|
|
||||
|
@Schema(description = "参考图片URL列表,可选") |
||||
|
private List<String> referenceImage; |
||||
|
|
||||
|
@Schema(description = "使用的模型ID", defaultValue = "doubao-seedream-5-0-260128") |
||||
|
private String model; |
||||
|
|
||||
|
@Schema(description = "画风:1=治愈系水彩,2=Q版卡通,3=日式漫画,4=吉卜力风,5=3D毛绒,6=搞笑夸张", defaultValue = "1") |
||||
|
private Integer style; |
||||
|
|
||||
|
@Schema(description = "比例:1=1:1,2=2:3,3=3:4,4=4:3,5=5:4", defaultValue = "1") |
||||
|
private Integer ratio; |
||||
|
|
||||
|
@Schema(description = "图片尺寸:1=2K,2=4K,3=8K", defaultValue = "1") |
||||
|
private Integer imgSize; |
||||
|
|
||||
|
@Schema(description = "图片格式:1=png,2=jpeg", defaultValue = "1") |
||||
|
private Integer imgType; |
||||
|
|
||||
|
@Schema(description = "宠物物种:如cat、dog") |
||||
|
private String species; |
||||
|
|
||||
|
@Schema(description = "宠物品种:如British Shorthair、Corgi") |
||||
|
private String breed; |
||||
|
|
||||
|
@Schema(description = "宠物毛色:如blue-gray、orange") |
||||
|
private String color; |
||||
|
|
||||
|
@Schema(description = "眼睛颜色:如amber、blue") |
||||
|
private String eyeColor; |
||||
|
|
||||
|
@Schema(description = "体型:如round chubby、slim and elegant") |
||||
|
private String bodyType; |
||||
|
|
||||
|
@Schema(description = "特殊特征:如flat face、thick coat", defaultValue = "") |
||||
|
private String distinctiveFeatures; |
||||
|
|
||||
|
@Schema(description = "故事梗概:AI会据此生成四格漫画脚本") |
||||
|
private String description; |
||||
|
|
||||
|
@Schema(description = "可见范围: public公开 private仅自己", defaultValue = "private") |
||||
|
private String visibility; |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import jakarta.validation.constraints.NotBlank; |
||||
|
import lombok.Data; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 单图生成任务回调请求表单 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@Schema(description = "单图生成任务回调请求表单") |
||||
|
public class AiSingleImageCallbackForm { |
||||
|
|
||||
|
@NotBlank(message = "任务UUID不能为空") |
||||
|
@Schema(description = "任务唯一标识UUID", requiredMode = Schema.RequiredMode.REQUIRED) |
||||
|
private String uuid; |
||||
|
|
||||
|
@Schema(description = "任务状态:succeeded=成功,failed=失败") |
||||
|
private String status; |
||||
|
|
||||
|
@Schema(description = "生成的图片列表") |
||||
|
private List<AiCallbackImage> result; |
||||
|
|
||||
|
@JsonProperty("created_at") |
||||
|
@Schema(description = "创建时间戳") |
||||
|
private Long createdAt; |
||||
|
|
||||
|
@JsonProperty("updated_at") |
||||
|
@Schema(description = "更新时间戳") |
||||
|
private Long updatedAt; |
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* AI单图漫画生成请求表单 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@Schema(description = "AI单图漫画生成请求表单") |
||||
|
public class AiSingleImageGenerateForm { |
||||
|
|
||||
|
@Schema(description = "参考图片URL列表,可选") |
||||
|
private List<String> imageUrl; |
||||
|
|
||||
|
@Schema(description = "使用的模型ID", defaultValue = "doubao-seedream-5-0-260128") |
||||
|
private String model; |
||||
|
|
||||
|
@Schema(description = "画风:1=治愈系水彩,2=Q版卡通,3=日式漫画,4=吉卜力风,5=3D毛绒,6=搞笑夸张", defaultValue = "1") |
||||
|
private Integer style; |
||||
|
|
||||
|
@Schema(description = "比例:1=1:1,2=2:3,3=3:4,4=4:3,5=5:4", defaultValue = "1") |
||||
|
private Integer ratio; |
||||
|
|
||||
|
@Schema(description = "图片尺寸:1=2K,2=4K,3=8K", defaultValue = "1") |
||||
|
private Integer imgSize; |
||||
|
|
||||
|
@Schema(description = "图片格式:1=png,2=jpeg", defaultValue = "1") |
||||
|
private Integer imgType; |
||||
|
|
||||
|
@Schema(description = "宠物物种:如cat、dog") |
||||
|
private String species; |
||||
|
|
||||
|
@Schema(description = "宠物品种:如British Shorthair、Corgi") |
||||
|
private String breed; |
||||
|
|
||||
|
@Schema(description = "宠物毛色:如blue-gray、orange") |
||||
|
private String color; |
||||
|
|
||||
|
@Schema(description = "眼睛颜色:如amber、blue") |
||||
|
private String eyeColor; |
||||
|
|
||||
|
@Schema(description = "体型:如round chubby、slim and elegant") |
||||
|
private String bodyType; |
||||
|
|
||||
|
@Schema(description = "特殊特征:如flat face、thick coat", defaultValue = "") |
||||
|
private String distinctiveFeatures; |
||||
|
|
||||
|
@Schema(description = "场景描述:用户自定义生成场景") |
||||
|
private String description; |
||||
|
|
||||
|
@Schema(description = "可见范围: public公开 private仅自己", defaultValue = "private") |
||||
|
private String visibility; |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import jakarta.validation.constraints.NotBlank; |
||||
|
import jakarta.validation.constraints.Pattern; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@Schema(description = "更新任务可见范围表单") |
||||
|
public class AiTaskVisibilityForm { |
||||
|
|
||||
|
@NotBlank(message = "任务UUID不能为空") |
||||
|
@Schema(description = "任务UUID") |
||||
|
private String uuid; |
||||
|
|
||||
|
@NotBlank(message = "可见范围不能为空") |
||||
|
@Pattern(regexp = "^(public|private|friends)$", message = "可见范围只能是public/private/friends") |
||||
|
@Schema(description = "可见范围:public-公开,private-仅自己可见") |
||||
|
private String visibility; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* AI视频生成请求表单 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@Schema(description = "AI视频生成请求表单") |
||||
|
public class AiVideoGenerateForm { |
||||
|
|
||||
|
@Schema(description = "使用的模型ID", defaultValue = "doubao-seedance-2-0-260128") |
||||
|
private String model; |
||||
|
|
||||
|
@Schema(description = "内容数组,包含text、image_url、video_url、audio_url类型") |
||||
|
private List<VideoContentItem> content; |
||||
|
|
||||
|
@Schema(description = "分辨率: 480p, 720p, 1080p, 2K", defaultValue = "720p") |
||||
|
private String resolution = "720p"; |
||||
|
|
||||
|
@Schema(description = "视频时长,单位秒,最大15秒", defaultValue = "5") |
||||
|
private Integer duration = 5; |
||||
|
|
||||
|
@Schema(description = "可见范围: public公开 private仅自己", defaultValue = "private") |
||||
|
private String visibility; |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* 媒体URL通用结构 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@JsonInclude(JsonInclude.Include.NON_NULL) |
||||
|
@Schema(description = "媒体URL通用结构") |
||||
|
public class MediaUrl { |
||||
|
|
||||
|
@Schema(description = "资源URL地址") |
||||
|
private String url; |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* 视频生成内容项 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@JsonInclude(JsonInclude.Include.NON_NULL) |
||||
|
@Schema(description = "视频生成内容项") |
||||
|
public class VideoContentItem { |
||||
|
|
||||
|
@Schema(description = "内容类型:text/image_url/video_url/audio_url", allowableValues = {"text", "image_url", "video_url", "audio_url"}) |
||||
|
private String type; |
||||
|
|
||||
|
@Schema(description = "文本内容,type=text时必填") |
||||
|
private String text; |
||||
|
|
||||
|
@Schema(description = "图片URL结构,type=image_url时必填") |
||||
|
private MediaUrl image_url; |
||||
|
|
||||
|
@Schema(description = "视频URL结构,type=video_url时必填") |
||||
|
private MediaUrl video_url; |
||||
|
|
||||
|
@Schema(description = "音频URL结构,type=audio_url时必填") |
||||
|
private MediaUrl audio_url; |
||||
|
|
||||
|
@Schema(description = "内容角色标识,比如reference_image/reference_video/reference_audio") |
||||
|
private String role; |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import jakarta.validation.constraints.NotBlank; |
||||
|
import jakarta.validation.constraints.NotNull; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* 上报用户订阅授权请求 |
||||
|
*/ |
||||
|
@Data |
||||
|
@Schema(description = "上报用户订阅授权请求") |
||||
|
public class WxSubscribeAuthForm { |
||||
|
|
||||
|
// @NotBlank(message = "用户ID不能为空")
|
||||
|
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED) |
||||
|
private Long userId; |
||||
|
|
||||
|
@NotBlank(message = "模板ID不能为空") |
||||
|
@Schema(description = "订阅消息模板ID", requiredMode = Schema.RequiredMode.REQUIRED) |
||||
|
private String templateId; |
||||
|
|
||||
|
@NotNull(message = "授权状态不能为空") |
||||
|
@Schema(description = "授权状态:0=拒绝 1=同意一次 2=总是同意", requiredMode = Schema.RequiredMode.REQUIRED) |
||||
|
private Integer status; |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
package com.youlai.boot.mini.model.form; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import jakarta.validation.constraints.NotBlank; |
||||
|
import jakarta.validation.constraints.NotNull; |
||||
|
import lombok.Data; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* 发送订阅消息请求 |
||||
|
*/ |
||||
|
@Data |
||||
|
@Schema(description = "发送订阅消息请求") |
||||
|
public class WxSubscribeSendForm { |
||||
|
|
||||
|
// @NotBlank(message = "用户ID不能为空")
|
||||
|
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED) |
||||
|
private Long userId; |
||||
|
|
||||
|
@NotBlank(message = "模板ID不能为空") |
||||
|
@Schema(description = "订阅消息模板ID", requiredMode = Schema.RequiredMode.REQUIRED) |
||||
|
private String templateId; |
||||
|
|
||||
|
@Schema(description = "跳转页面,例:/pages/result/index?id=123") |
||||
|
private String page; |
||||
|
|
||||
|
@Schema(description = "跳转版本:developer=开发版 trial=体验版 formal=正式版,默认formal") |
||||
|
private String miniProgramState; |
||||
|
|
||||
|
@NotNull(message = "模板参数不能为空") |
||||
|
@Schema(description = "模板参数,key是模板字段名,value是对应的值", requiredMode = Schema.RequiredMode.REQUIRED) |
||||
|
private Map<String, String> templateParams; |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
package com.youlai.boot.mini.model.query; |
||||
|
|
||||
|
import com.youlai.boot.common.base.BaseQuery; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
/** |
||||
|
* 用户AI生成任务历史查询参数 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@Schema(description = "用户AI生成任务历史查询参数") |
||||
|
public class AiTaskMediaQuery extends BaseQuery { |
||||
|
|
||||
|
@Schema(description = "任务状态:0=生成中 1=成功 2=失败 3=超时 4=已取消,不传查全部") |
||||
|
private Integer status; |
||||
|
|
||||
|
@Schema(description = "生成类型:img_single(单图), img_grid_4(四宫格), video(视频),不传查全部") |
||||
|
private String type; |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
package com.youlai.boot.mini.model.query; |
||||
|
|
||||
|
import com.youlai.boot.common.base.BaseQuery; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@Schema(description = "发现页公开作品查询参数") |
||||
|
public class DiscoveryPublicWorkQuery extends BaseQuery { |
||||
|
|
||||
|
@Schema(description = "生成类型:img_single(单图), img_grid_4(四宫格), video(视频),不传查全部") |
||||
|
private String type; |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
package com.youlai.boot.mini.model.vo; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.Hidden; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* AI生成任务列表VO |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@Schema(description = "AI生成任务列表VO") |
||||
|
public class AiGenerationTaskVO { |
||||
|
|
||||
|
// @Hidden
|
||||
|
// @Schema(description = "任务ID",hidden = true)
|
||||
|
// private Long id;
|
||||
|
|
||||
|
@Schema(description = "任务唯一标识") |
||||
|
private String uuid; |
||||
|
|
||||
|
@Schema(description = "生成类型:img_single(单图), img_grid_4(四宫格), video(视频)") |
||||
|
private String type; |
||||
|
|
||||
|
@Schema(description = "任务状态:0=生成中 1=成功 2=失败 3=超时 4=已取消") |
||||
|
private Integer status; |
||||
|
|
||||
|
@Schema(description = "消耗积分") |
||||
|
private Integer pointsConsumed; |
||||
|
|
||||
|
@Schema(description = "可见范围:public(公开) / private(仅自己)") |
||||
|
private String visibility; |
||||
|
|
||||
|
@Schema(description = "创建时间") |
||||
|
private Date createTime; |
||||
|
|
||||
|
@Schema(description = "生成内容列表") |
||||
|
private List<MiniAiTaskMediaVO> generateContent; |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
package com.youlai.boot.mini.model.vo; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* AI视频生成任务回调请求 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@JsonInclude(JsonInclude.Include.NON_NULL) |
||||
|
@Schema(description = "AI视频生成任务回调请求") |
||||
|
public class AiVideoCallbackVO { |
||||
|
|
||||
|
@Schema(description = "第三方视频任务唯一ID") |
||||
|
private String id; |
||||
|
|
||||
|
@Schema(description = "使用的模型ID") |
||||
|
private String model; |
||||
|
|
||||
|
@Schema(description = "任务状态:queued排队中/running处理中/succeeded成功/failed失败") |
||||
|
private String status; |
||||
|
|
||||
|
@JsonProperty("created_at") |
||||
|
@Schema(description = "创建时间戳") |
||||
|
private Long createdAt; |
||||
|
|
||||
|
@JsonProperty("updated_at") |
||||
|
@Schema(description = "更新时间戳") |
||||
|
private Long updatedAt; |
||||
|
|
||||
|
@JsonProperty("service_tier") |
||||
|
@Schema(description = "服务等级") |
||||
|
private String serviceTier; |
||||
|
|
||||
|
@JsonProperty("execution_expires_after") |
||||
|
@Schema(description = "执行过期时间") |
||||
|
private Long executionExpiresAfter; |
||||
|
|
||||
|
@JsonProperty("generate_audio") |
||||
|
@Schema(description = "是否生成音频") |
||||
|
private Boolean generateAudio; |
||||
|
|
||||
|
@Schema(description = "是否为草稿") |
||||
|
private Boolean draft; |
||||
|
|
||||
|
@Schema(description = "生成结果,status=succeeded时返回") |
||||
|
private VideoCallbackContent content; |
||||
|
|
||||
|
@Schema(description = "资源消耗统计") |
||||
|
private VideoCallbackUsage usage; |
||||
|
|
||||
|
@Schema(description = "随机种子") |
||||
|
private Long seed; |
||||
|
|
||||
|
@Schema(description = "分辨率,比如720p") |
||||
|
private String resolution; |
||||
|
|
||||
|
@Schema(description = "比例,比如16:9") |
||||
|
private String ratio; |
||||
|
|
||||
|
@Schema(description = "视频时长,单位秒") |
||||
|
private Integer duration; |
||||
|
|
||||
|
@Schema(description = "帧率") |
||||
|
private Integer framespersecond; |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
package com.youlai.boot.mini.model.vo; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Getter |
||||
|
@Setter |
||||
|
@Schema(description = "发现页公开作品VO") |
||||
|
public class DiscoveryPublicWorkVO { |
||||
|
|
||||
|
@Schema(description = "任务唯一标识") |
||||
|
private String uuid; |
||||
|
|
||||
|
@Schema(description = "生成类型:img_single(单图), img_grid_4(四宫格), video(视频)") |
||||
|
private String type; |
||||
|
|
||||
|
@Schema(description = "消耗积分") |
||||
|
private Integer pointsConsumed; |
||||
|
|
||||
|
@Schema(description = "创建时间") |
||||
|
private Date createTime; |
||||
|
|
||||
|
@Schema(description = "生成内容列表") |
||||
|
private List<MiniAiTaskMediaVO> generateContent; |
||||
|
|
||||
|
@Schema(description = "创作者用户UUID") |
||||
|
private String userUuid; |
||||
|
|
||||
|
@Schema(description = "创作者昵称") |
||||
|
private String nickname; |
||||
|
|
||||
|
@Schema(description = "创作者头像") |
||||
|
private String avatar; |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
package com.youlai.boot.mini.model.vo; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
/** |
||||
|
* AI生成媒体内容VO |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@Schema(description = "AI生成媒体内容VO") |
||||
|
public class MiniAiTaskMediaVO { |
||||
|
|
||||
|
@Schema(description = "唯一标识") |
||||
|
private String uuid; |
||||
|
|
||||
|
@Schema(description = "媒体类型,image-图片,video-视频") |
||||
|
private String mediaType; |
||||
|
|
||||
|
@Schema(description = "资源URL") |
||||
|
private String sourceUrl; |
||||
|
|
||||
|
@Schema(description = "缩略图URL(视频需要)") |
||||
|
private String thumbnailUrl; |
||||
|
|
||||
|
@Schema(description = "宽度(像素)") |
||||
|
private Integer width; |
||||
|
|
||||
|
@Schema(description = "高度(像素)") |
||||
|
private Integer height; |
||||
|
|
||||
|
@Schema(description = "时长(秒,视频用)") |
||||
|
private Integer duration; |
||||
|
|
||||
|
@Schema(description = "创建时间") |
||||
|
private Date createTime; |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
package com.youlai.boot.mini.model.vo; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.fasterxml.jackson.annotation.JsonFormat; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
/** |
||||
|
* 用户上传媒体返回VO |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@Schema(description = "用户上传媒体返回VO") |
||||
|
public class UserUploadMediaVO { |
||||
|
|
||||
|
@Schema(description = "媒体UUID") |
||||
|
private String uuid; |
||||
|
|
||||
|
@Schema(description = "媒体类型:image-图片,video-视频") |
||||
|
private String mediaType; |
||||
|
|
||||
|
@Schema(description = "媒体访问地址") |
||||
|
private String sourceUrl; |
||||
|
|
||||
|
@Schema(description = "缩略图地址(视频才有)") |
||||
|
private String thumbnailUrl; |
||||
|
|
||||
|
@Schema(description = "视频时长(秒,视频才有)") |
||||
|
private Integer duration; |
||||
|
|
||||
|
@Schema(description = "图片宽度") |
||||
|
private Integer width; |
||||
|
|
||||
|
@Schema(description = "图片高度") |
||||
|
private Integer height; |
||||
|
|
||||
|
@Schema(description = "上传时间") |
||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
||||
|
private Date createTime; |
||||
|
|
||||
|
@TableField("create_timestamp") |
||||
|
@Schema(description = "创建时间毫秒级时间戳") |
||||
|
private Long createTimestamp; |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
package com.youlai.boot.mini.model.vo; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* AI视频生成回调结果内容 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@JsonInclude(JsonInclude.Include.NON_NULL) |
||||
|
@Schema(description = "AI视频生成回调结果内容") |
||||
|
public class VideoCallbackContent { |
||||
|
|
||||
|
@JsonProperty("video_url") |
||||
|
@Schema(description = "生成的视频地址") |
||||
|
private String videoUrl; |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
package com.youlai.boot.mini.model.vo; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* 视频生成资源消耗统计 |
||||
|
* |
||||
|
* @author youlai |
||||
|
*/ |
||||
|
@Data |
||||
|
@JsonInclude(JsonInclude.Include.NON_NULL) |
||||
|
@Schema(description = "视频生成资源消耗统计") |
||||
|
public class VideoCallbackUsage { |
||||
|
|
||||
|
@JsonProperty("completion_tokens") |
||||
|
@Schema(description = "完成token数") |
||||
|
private Integer completionTokens; |
||||
|
|
||||
|
@JsonProperty("total_tokens") |
||||
|
@Schema(description = "总token数") |
||||
|
private Integer totalTokens; |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
package com.youlai.boot.mini.model.vo; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
/** |
||||
|
* 微信接口通用返回 |
||||
|
*/ |
||||
|
@Data |
||||
|
public class WxApiResponse { |
||||
|
@JsonProperty("errcode") |
||||
|
private Integer errcode; |
||||
|
|
||||
|
@JsonProperty("errmsg") |
||||
|
private String errmsg; |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
package com.youlai.boot.mini.service; |
||||
|
|
||||
|
import com.youlai.boot.mini.model.entity.MiniAiTaskMedia; |
||||
|
import com.youlai.boot.mini.model.form.AiFourPanelGenerateForm; |
||||
|
import com.youlai.boot.mini.model.form.AiSingleImageCallbackForm; |
||||
|
import com.youlai.boot.mini.model.form.AiSingleImageGenerateForm; |
||||
|
import com.youlai.boot.mini.model.form.AiVideoGenerateForm; |
||||
|
import com.youlai.boot.mini.model.entity.MiniAiGenerationTask; |
||||
|
import com.youlai.boot.mini.model.form.AiFourPanelCallbackForm; |
||||
|
import com.youlai.boot.mini.model.form.AiTaskVisibilityForm; |
||||
|
import com.youlai.boot.mini.model.vo.AiVideoCallbackVO; |
||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
|
import com.youlai.boot.mini.model.query.AiTaskMediaQuery; |
||||
|
import com.youlai.boot.mini.model.query.DiscoveryPublicWorkQuery; |
||||
|
import com.youlai.boot.mini.model.vo.AiGenerationTaskVO; |
||||
|
import com.youlai.boot.mini.model.vo.DiscoveryPublicWorkVO; |
||||
|
import com.youlai.boot.mini.model.vo.UserUploadMediaVO; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface AiGenerationService { |
||||
|
|
||||
|
List<String> uploadReferenceFile(List<MultipartFile> images, List<MultipartFile> videos, Long userId); |
||||
|
|
||||
|
String createAndGenerateImage(AiSingleImageGenerateForm form, Long userId); |
||||
|
|
||||
|
String createAndGenerateFourPanel(AiFourPanelGenerateForm form, Long userId); |
||||
|
|
||||
|
boolean handleTaskCallback(AiSingleImageCallbackForm form); |
||||
|
|
||||
|
String createAndGenerateVideo(AiVideoGenerateForm form, Long userId); |
||||
|
|
||||
|
boolean handleVideoTaskCallback(AiVideoCallbackVO vo); |
||||
|
|
||||
|
boolean handleFourPanelCallback(AiFourPanelCallbackForm form); |
||||
|
|
||||
|
MiniAiGenerationTask getTaskByUuid(String uuid); |
||||
|
|
||||
|
List<UserUploadMediaVO> getRecentUploadVO(Long userId); |
||||
|
|
||||
|
boolean deleteUploadMedia(Long userId, String uuid); |
||||
|
|
||||
|
IPage<AiGenerationTaskVO> getMyAiGenerateHistory(AiTaskMediaQuery query, Long userId); |
||||
|
|
||||
|
void setMiniProgramState(String miniProgramState); |
||||
|
|
||||
|
void handleTimeoutTasks(); |
||||
|
|
||||
|
IPage<DiscoveryPublicWorkVO> getPublicDiscoveryFeed(DiscoveryPublicWorkQuery query); |
||||
|
|
||||
|
void updateTaskVisibility(Long userId, AiTaskVisibilityForm form); |
||||
|
|
||||
|
void syncTaskStatus(String taskUuid); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
package com.youlai.boot.mini.service; |
||||
|
|
||||
|
import com.youlai.boot.mini.model.form.WxSubscribeAuthForm; |
||||
|
import com.youlai.boot.mini.model.form.WxSubscribeSendForm; |
||||
|
|
||||
|
/** |
||||
|
* 微信订阅消息服务 |
||||
|
*/ |
||||
|
public interface WxSubscribeService { |
||||
|
|
||||
|
void reportAuth(WxSubscribeAuthForm form); |
||||
|
|
||||
|
Boolean sendMessage(WxSubscribeSendForm form); |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,148 @@ |
|||||
|
package com.youlai.boot.mini.service.impl; |
||||
|
|
||||
|
import cn.binarywang.wx.miniapp.api.WxMaService; |
||||
|
import cn.hutool.core.util.IdUtil; |
||||
|
import cn.hutool.core.util.StrUtil; |
||||
|
import cn.hutool.json.JSONUtil; |
||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
||||
|
import com.youlai.boot.common.exception.BusinessException; |
||||
|
import com.youlai.boot.common.exception.MsgException; |
||||
|
import com.youlai.boot.mini.model.enums.SubscribeStatusEnum; |
||||
|
import com.youlai.boot.mini.mapper.MiniUserSubscribeMapper; |
||||
|
import com.youlai.boot.mini.model.entity.MiniUserSubscribe; |
||||
|
import com.youlai.boot.mini.model.form.WxSubscribeAuthForm; |
||||
|
import com.youlai.boot.mini.model.form.WxSubscribeSendForm; |
||||
|
import com.youlai.boot.mini.model.vo.WxApiResponse; |
||||
|
import com.youlai.boot.mini.service.WxSubscribeService; |
||||
|
|
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.*; |
||||
|
|
||||
|
/** |
||||
|
* 微信订阅消息服务实现 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Service |
||||
|
@RequiredArgsConstructor |
||||
|
public class WxSubscribeServiceImpl implements WxSubscribeService { |
||||
|
|
||||
|
private final WxMaService wxMaService; |
||||
|
private final MiniUserSubscribeMapper userSubscribeMapper; |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void reportAuth(WxSubscribeAuthForm form) { |
||||
|
try { |
||||
|
// 查询用户openid
|
||||
|
String openid = userSubscribeMapper.getOpenidByUserId(form.getUserId()); |
||||
|
if (StrUtil.isBlank(openid)) { |
||||
|
throw new MsgException("用户未绑定微信"); |
||||
|
} |
||||
|
long currentTime = System.currentTimeMillis(); |
||||
|
|
||||
|
// 查询是否已有授权记录
|
||||
|
MiniUserSubscribe exist = userSubscribeMapper.selectOne(new LambdaQueryWrapper<MiniUserSubscribe>() |
||||
|
.eq(MiniUserSubscribe::getUserId, form.getUserId()) |
||||
|
.eq(MiniUserSubscribe::getTemplateId, form.getTemplateId()) |
||||
|
.eq(MiniUserSubscribe::getDeleted, 0) |
||||
|
.last("limit 1")); |
||||
|
|
||||
|
if (exist != null) { |
||||
|
exist.setStatus(form.getStatus()); |
||||
|
exist.setUpdateTime(new Date()); |
||||
|
exist.setUpdateTimestamp(currentTime); |
||||
|
userSubscribeMapper.updateById(exist); |
||||
|
} else { |
||||
|
MiniUserSubscribe subscribe = new MiniUserSubscribe(); |
||||
|
subscribe.setUuid(IdUtil.fastSimpleUUID()); |
||||
|
subscribe.setUserId(form.getUserId()); |
||||
|
subscribe.setTemplateId(form.getTemplateId()); |
||||
|
subscribe.setStatus(form.getStatus()); |
||||
|
subscribe.setCreateTime(new Date()); |
||||
|
subscribe.setCreateTimestamp(currentTime); |
||||
|
subscribe.setUpdateTime(new Date()); |
||||
|
subscribe.setUpdateTimestamp(currentTime); |
||||
|
subscribe.setDeleted(false); |
||||
|
userSubscribeMapper.insert(subscribe); |
||||
|
} |
||||
|
log.info("用户{}订阅授权上报成功,模板ID:{},状态:{}", form.getUserId(), form.getTemplateId(), form.getStatus()); |
||||
|
} catch (Exception e) { |
||||
|
log.error("上报用户订阅授权失败,请求参数:{}", JSONUtil.toJsonStr(form), e); |
||||
|
throw new MsgException("上报授权状态失败"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public Boolean sendMessage(WxSubscribeSendForm form) { |
||||
|
log.info("开始发送订阅消息,请求参数:{}", JSONUtil.toJsonStr(form)); |
||||
|
try { |
||||
|
// 1. 查询用户openid
|
||||
|
String openid = userSubscribeMapper.getOpenidByUserId(form.getUserId()); |
||||
|
if (StrUtil.isBlank(openid)) { |
||||
|
log.error("用户{}未绑定微信openid,无法发送订阅消息", form.getUserId()); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 2. 构建微信订阅消息参数(新版小程序订阅消息格式)
|
||||
|
Map<String, Map<String, String>> dataMap = new HashMap<>(); |
||||
|
for (Map.Entry<String, String> entry : form.getTemplateParams().entrySet()) { |
||||
|
Map<String, String> valueMap = new HashMap<>(); |
||||
|
valueMap.put("value", entry.getValue()); |
||||
|
dataMap.put(entry.getKey(), valueMap); |
||||
|
} |
||||
|
|
||||
|
Map<String, Object> messageMap = new HashMap<>(); |
||||
|
messageMap.put("touser", openid); |
||||
|
messageMap.put("template_id", form.getTemplateId()); |
||||
|
messageMap.put("page", form.getPage()); |
||||
|
messageMap.put("miniprogram_state", form.getMiniProgramState()); |
||||
|
messageMap.put("lang", "zh_CN"); |
||||
|
messageMap.put("data", dataMap); |
||||
|
|
||||
|
// 3. 调用微信接口发送
|
||||
|
String responseStr = wxMaService.post("https://api.weixin.qq.com/cgi-bin/message/subscribe/send", JSONUtil.toJsonStr(messageMap)); |
||||
|
WxApiResponse response = JSONUtil.toBean(responseStr, WxApiResponse.class); |
||||
|
log.info("微信订阅消息发送结果,用户:{},返回:{}", form.getUserId(), JSONUtil.toJsonStr(response)); |
||||
|
|
||||
|
// 4. 根据返回结果更新用户订阅状态
|
||||
|
MiniUserSubscribe subscribe = userSubscribeMapper.selectOne(new LambdaQueryWrapper<MiniUserSubscribe>() |
||||
|
.eq(MiniUserSubscribe::getUserId, form.getUserId()) |
||||
|
.eq(MiniUserSubscribe::getTemplateId, form.getTemplateId()) |
||||
|
.eq(MiniUserSubscribe::getDeleted, 0) |
||||
|
.last("limit 1") |
||||
|
); |
||||
|
|
||||
|
if (response.getErrcode() == 0) { |
||||
|
log.info("订阅消息发送成功,用户:{},模板ID:{}", form.getUserId(), form.getTemplateId()); |
||||
|
// 如果是同意一次的状态,发送后更新为未授权
|
||||
|
if (subscribe != null && SubscribeStatusEnum.ONCE.getValue().equals(subscribe.getStatus())) { |
||||
|
subscribe.setStatus(SubscribeStatusEnum.REJECT.getValue()); |
||||
|
subscribe.setUpdateTime(new Date()); |
||||
|
userSubscribeMapper.updateById(subscribe); |
||||
|
} |
||||
|
return true; |
||||
|
} else if (response.getErrcode() == 43101) { |
||||
|
// 用户未订阅,更新本地状态为拒绝
|
||||
|
log.warn("用户{}未订阅该模板消息,更新本地状态为拒绝", form.getUserId()); |
||||
|
if (subscribe != null) { |
||||
|
subscribe.setStatus(SubscribeStatusEnum.REJECT.getValue()); |
||||
|
subscribe.setUpdateTime(new Date()); |
||||
|
userSubscribeMapper.updateById(subscribe); |
||||
|
} |
||||
|
return false; |
||||
|
} else { |
||||
|
log.error("订阅消息发送失败,错误码:{},错误信息:{}", response.getErrcode(), response.getErrmsg()); |
||||
|
return false; |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
log.error("发送订阅消息异常,请求参数:{}", JSONUtil.toJsonStr(form), e); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper |
||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
|
||||
|
<mapper namespace="com.youlai.boot.mini.mapper.MiniAiGenerationTaskMapper"> |
||||
|
|
||||
|
|
||||
|
</mapper> |
||||
@ -0,0 +1,9 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper |
||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
|
||||
|
<mapper namespace="com.youlai.boot.mini.mapper.MiniAiTaskMediaMapper"> |
||||
|
|
||||
|
|
||||
|
</mapper> |
||||
@ -0,0 +1,15 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper |
||||
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
|
||||
|
<mapper namespace="com.youlai.boot.mini.mapper.MiniUserSubscribeMapper"> |
||||
|
|
||||
|
<select id="getOpenidByUserId" resultType="java.lang.String"> |
||||
|
SELECT openid |
||||
|
FROM sys_user_social |
||||
|
WHERE user_id = #{userId} |
||||
|
LIMIT 1 |
||||
|
</select> |
||||
|
|
||||
|
</mapper> |
||||
Loading…
Reference in new issue