diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java deleted file mode 100644 index e7ff80a98..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java +++ /dev/null @@ -1,14 +0,0 @@ -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; - -@Schema(description = "管理后台 - AI 绘画公开的分页 Request VO") -@Data -public class AiImagePublicPageReqVO extends PageParam { - - @Schema(description = "提示词") - private String prompt; - -} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java deleted file mode 100644 index e98c643d6..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java +++ /dev/null @@ -1,51 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; -import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - AI 知识库") -@RestController -@RequestMapping("/ai/knowledge") -@Validated -public class AiKnowledgeController { - - @Resource - private AiKnowledgeService knowledgeService; - - @GetMapping("/page") - @Operation(summary = "获取知识库分页") - public CommonResult> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) { - PageResult pageResult = knowledgeService.getKnowledgePage(getLoginUserId(), pageReqVO); - return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class)); - } - - @PostMapping("/create") - @Operation(summary = "创建知识库") - public CommonResult createKnowledge(@RequestBody @Valid AiKnowledgeCreateReqVO createReqVO) { - return success(knowledgeService.createKnowledge(createReqVO, getLoginUserId())); - } - - @PutMapping("/update") - @Operation(summary = "更新知识库") - public CommonResult updateKnowledge(@RequestBody @Valid AiKnowledgeUpdateReqVO updateReqVO) { - knowledgeService.updateKnowledge(updateReqVO, getLoginUserId()); - return success(true); - } -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java deleted file mode 100644 index 62e3e0395..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; -import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - AI 知识库文档") -@RestController -@RequestMapping("/ai/knowledge/document") -@Validated -public class AiKnowledgeDocumentController { - - @Resource - private AiKnowledgeDocumentService documentService; - - @PostMapping("/create") - @Operation(summary = "新建文档") - public CommonResult createKnowledgeDocument(@Valid AiKnowledgeDocumentCreateReqVO reqVO) { - Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO); - return success(knowledgeDocumentId); - } - - @GetMapping("/page") - @Operation(summary = "获取文档分页") - public CommonResult> getKnowledgeDocumentPage(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) { - PageResult pageResult = documentService.getKnowledgeDocumentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class)); - } - - @PutMapping("/update") - @Operation(summary = "更新文档") - public CommonResult updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) { - documentService.updateKnowledgeDocument(reqVO); - return success(true); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java deleted file mode 100644 index de131dcc2..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; -import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - AI 知识库段落") -@RestController -@RequestMapping("/ai/knowledge/segment") -@Validated -public class AiKnowledgeSegmentController { - - @Resource - private AiKnowledgeSegmentService segmentService; - - @GetMapping("/page") - @Operation(summary = "获取段落分页") - public CommonResult> getKnowledgeSegmentPage(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) { - PageResult pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class)); - } - - @PutMapping("/update") - @Operation(summary = "更新段落内容") - public CommonResult updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) { - segmentService.updateKnowledgeSegment(reqVO); - return success(true); - } - - @PutMapping("/update-status") - @Operation(summary = "启禁用段落内容") - public CommonResult updateKnowledgeSegmentStatus(@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) { - segmentService.updateKnowledgeSegmentStatus(reqVO); - return success(true); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java deleted file mode 100644 index 76c001bd3..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - AI 知识库文档的分页 Request VO") -@Data -public class AiKnowledgeDocumentPageReqVO extends PageParam { - - @Schema(description = "文档名称", example = "Java 开发手册") - private String name; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java deleted file mode 100644 index 96ca61b3d..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - AI 知识库-文档 Response VO") -@Data -public class AiKnowledgeDocumentRespVO extends PageParam { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") - private Long id; - - @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") - private Long knowledgeId; - - @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") - private String name; - - @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....") - private String content; - - @Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") - private String url; - - @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Integer tokens; - - @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008") - private Integer wordCount; - - @Schema(description = "切片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer sliceStatus; - - @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java deleted file mode 100644 index cb676306b..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - - -@Schema(description = "管理后台 - AI 更新 知识库-文档 Request VO") -@Data -public class AiKnowledgeDocumentUpdateReqVO { - - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583") - @NotNull(message = "编号不能为空") - private Long id; - - @Schema(description = "是否启用", example = "1") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "名称", example = "Java 开发手册") - private String name; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java deleted file mode 100644 index fcf271c44..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - AI 知识库创建 Request VO") -@Data -public class AiKnowledgeCreateReqVO { - - @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南") - @NotBlank(message = "知识库名称不能为空") - private String name; - - @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档") - private String description; - - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]") - private List visibilityPermissions; - - @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "嵌入模型不能为空") - private Long modelId; - - @Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5") - @NotNull(message = "相似性阈值不能为空") - private Double similarityThreshold; - - @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") - @NotNull(message = "topK 不能为空") - private Integer topK; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java deleted file mode 100644 index 75b9b5727..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; - - -@Schema(description = "管理后台 - AI 知识库文档的创建 Request VO") -@Data -public class AiKnowledgeDocumentCreateReqVO { - - @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204") - @NotNull(message = "知识库编号不能为空") - private Long knowledgeId; - - @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "三方登陆") - @NotBlank(message = "文档名称不能为空") - private String name; - - @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") - @URL(message = "文档 URL 格式不正确") - private String url; - - @Schema(description = "每个段落的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800") - @NotNull(message = "每个段落的目标 token 数不能为空") - private Integer defaultSegmentTokens; - - @Schema(description = "每个段落的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350") - @NotNull(message = "每个段落的最小字符数不能为空") - private Integer minSegmentWordCount; - - @Schema(description = "丢弃阈值:低于此阈值的段落会被丢弃", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") - @NotNull(message = "丢弃阈值不能为空") - private Integer minChunkLengthToEmbed; - - @Schema(description = "最大段落数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") - @NotNull(message = "最大段落数不能为空") - private Integer maxNumSegments; - - @Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "分块是否保留分隔符不能为空") - private Boolean keepSeparator; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java deleted file mode 100644 index 941732f1a..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - AI 知识库的分页 Request VO") -@Data -public class AiKnowledgePageReqVO extends PageParam { - - @Schema(description = "知识库名称", example = "Java 开发手册") - private String name; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java deleted file mode 100644 index 3ff8a1c75..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - - -@Schema(description = "管理后台 - AI 知识库 Response VO") -@Data -public class AiKnowledgeRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") - private Long id; - - @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南") - private String name; - - @Schema(description = "知识库描述", example = "帮助你快速构建系统") - private String description; - - @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14") - private Long modelId; - - @Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat") - private String model; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java deleted file mode 100644 index 7f23d5d8e..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO") -@Data -public class AiKnowledgeUpdateReqVO { - - @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204") - @NotNull(message = "知识库编号不能为空") - private Long id; - - @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "") - @NotBlank(message = "知识库名称不能为空") - private String name; - - @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "") - private String description; - - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - private List visibilityPermissions; - - @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "嵌入模型不能为空") - private Long modelId; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java deleted file mode 100644 index 8be3db501..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - AI 知识库分段的分页 Request VO") -@Data -public class AiKnowledgeSegmentPageReqVO extends PageParam { - - @Schema(description = "分段状态", example = "1") - private Integer status; - - @Schema(description = "文档编号", example = "1") - private Integer documentId; - - @Schema(description = "分段内容关键字", example = "Java 开发") - private String keyword; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java deleted file mode 100644 index 5e3f2d8cb..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - AI 知识库-文档 Response VO") -@Data -public class AiKnowledgeSegmentRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") - private Long id; - - @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") - private Long documentId; - - @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") - private Long knowledgeId; - - @Schema(description = "向量库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1858496a-1dde-4edf-a43e-0aed08f37f8c") - private String vectorId; - - @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") - private String content; - - @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Integer tokens; - - @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008") - private Integer wordCount; - - @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java deleted file mode 100644 index 75349df62..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - - -@Schema(description = "管理后台 - AI 知识库段落召回 Request VO") -@Data -public class AiKnowledgeSegmentSearchReqVO { - - @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") - private Long knowledgeId; - - @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 学习路线") - private String content; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java deleted file mode 100644 index 23b1461e2..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - - -@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO") -@Data -public class AiKnowledgeSegmentUpdateReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") - private Long id; - - @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") - private String content; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java deleted file mode 100644 index 4216635c7..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - - -@Schema(description = "管理后台 - AI 知识库段落的更新状态 Request VO") -@Data -public class AiKnowledgeSegmentUpdateStatusReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") - private Long id; - - @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "是否启用不能为空") - @InEnum(CommonStatusEnum.class) - private Integer status; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java deleted file mode 100644 index f65e809e9..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - AI 思维导图 Response VO") -@Data -public class AiMindMapRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3373") - private Long id; - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4325") - private Long userId; - - @Schema(description = "生成内容提示", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 学习路线") - private String prompt; - - @Schema(description = "生成的思维导图内容") - private String generatedContent; - - @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") - private String platform; - - @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo-0125") - private String model; - - @Schema(description = "错误信息") - private String errorMessage; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java deleted file mode 100644 index b1706154a..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java +++ /dev/null @@ -1,74 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.util.List; - -/** - * AI 知识库 DO - * - * @author xiaoxin - */ -@TableName(value = "ai_knowledge", autoResultMap = true) -@Data -public class AiKnowledgeDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 用户编号 - *

- * 关联 AdminUserDO 的 userId 字段 - */ - private Long userId; - /** - * 知识库名称 - */ - private String name; - /** - * 知识库描述 - */ - private String description; - - /** - * 可见权限,选择哪些人可见 - *

- * -1 所有人可见,其他为各自用户编号 - */ - @TableField(typeHandler = LongListTypeHandler.class) - private List visibilityPermissions; - /** - * 嵌入模型编号 - */ - private Long modelId; - /** - * 模型标识 - */ - private String model; - - /** - * topK - */ - private Integer topK; - /** - * 相似度阈值 - */ - private Double similarityThreshold; - - /** - * 状态 - *

- * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java deleted file mode 100644 index 297944611..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -/** - * AI 知识库-文档 DO - * - * @author xiaoxin - */ -@TableName(value = "ai_knowledge_document") -@Data -public class AiKnowledgeDocumentDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 知识库编号 - *

- * 关联 {@link AiKnowledgeDO#getId()} - */ - private Long knowledgeId; - /** - * 文件名称 - */ - private String name; - /** - * 内容 - */ - private String content; - /** - * 文件 URL - */ - private String url; - /** - * 文档 token 数量 - */ - private Integer tokens; - /** - * 文档字符数 - */ - private Integer wordCount; - - - // ========== 自定义分段所用参数 ========== - // TODO @新:3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。 - /** - * 每个文本块的目标 token 数 - */ - private Integer defaultSegmentTokens; - /** - * 每个文本块的最小字符数 - */ - private Integer minSegmentWordCount; - /** - * 低于此值的块会被丢弃 - */ - private Integer minChunkLengthToEmbed; - /** - * 最大块数 - */ - private Integer maxNumSegments; - /** - * 分块是否保留分隔符 - */ - private Boolean keepSeparator; - // =================================== - - /** - * 切片状态 - *

- * 枚举 {@link AiKnowledgeDocumentStatusEnum} - */ - private Integer sliceStatus; - - /** - * 状态 - *

- * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java deleted file mode 100644 index 9bb3d3338..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -/** - * AI 知识库-文档分段 DO - * - * @author xiaoxin - */ -@TableName(value = "ai_knowledge_segment") -@Data -public class AiKnowledgeSegmentDO extends BaseDO { - - public static final String FIELD_KNOWLEDGE_ID = "knowledgeId"; - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 向量库的编号 - */ - private String vectorId; - /** - * 知识库编号 - *

- * 关联 {@link AiKnowledgeDO#getId()} - */ - private Long knowledgeId; - /** - * 文档编号 - *

- * 关联 {@link AiKnowledgeDocumentDO#getId()} - */ - private Long documentId; - /** - * 切片内容 - */ - private String content; - /** - * 字符数 - */ - private Integer wordCount; - /** - * token 数量 - */ - private Integer tokens; - /** - * 状态 - *

- * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java deleted file mode 100644 index 7692d1ced..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; - -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.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * AI 知识库-文档 Mapper - * - * @author xiaoxin - */ -@Mapper -public interface AiKnowledgeDocumentMapper extends BaseMapperX { - - default PageResult selectPage(AiKnowledgeDocumentPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName()) - .orderByDesc(AiKnowledgeDocumentDO::getId)); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java deleted file mode 100644 index f07a9a2af..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -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.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * AI 知识库基础信息 Mapper - * - * @author xiaoxin - */ -@Mapper -public interface AiKnowledgeMapper extends BaseMapperX { - - default PageResult selectPage(Long userId, AiKnowledgePageReqVO pageReqVO) { - return selectPage(pageReqVO, new LambdaQueryWrapperX() - .eq(AiKnowledgeDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) - .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName()) - .and(e -> e.apply("FIND_IN_SET(" + userId + ",visibility_permissions)").or(m -> m.apply("FIND_IN_SET(-1,visibility_permissions)"))) - .orderByDesc(AiKnowledgeDO::getId)); - } -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java deleted file mode 100644 index 094f19b52..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; - -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.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * AI 知识库-分片 Mapper - * - * @author xiaoxin - */ -@Mapper -public interface AiKnowledgeSegmentMapper extends BaseMapperX { - - default PageResult selectPage(AiKnowledgeSegmentPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId()) - .eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus()) - .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getKeyword()) - .orderByDesc(AiKnowledgeSegmentDO::getId)); - } - - default List selectListByVectorIds(List vectorIdList) { - return selectList(new LambdaQueryWrapperX() - .in(AiKnowledgeSegmentDO::getVectorId, vectorIdList) - .orderByDesc(AiKnowledgeSegmentDO::getId)); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java deleted file mode 100644 index 3de0ac01d..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.knowledge; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; - -/** - * AI 知识库-文档 Service 接口 - * - * @author xiaoxin - */ -public interface AiKnowledgeDocumentService { - - /** - * 创建文档 - * - * @param createReqVO 文档创建 Request VO - * @return 文档编号 - */ - Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO); - - - /** - * 获取文档分页 - * - * @param pageReqVO 分页参数 - * @return 文档分页 - */ - PageResult getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO); - - /** - * 更新文档 - * - * @param reqVO 更新信息 - */ - void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO); -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java deleted file mode 100644 index 2e335f40d..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ /dev/null @@ -1,130 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.knowledge; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.http.HttpUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; -import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; -import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; -import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; -import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.document.Document; -import org.springframework.ai.reader.tika.TikaDocumentReader; -import org.springframework.ai.tokenizer.TokenCountEstimator; -import org.springframework.ai.transformer.splitter.TokenTextSplitter; -import org.springframework.ai.vectorstore.VectorStore; -import org.springframework.core.io.ByteArrayResource; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCUMENT_NOT_EXISTS; - -/** - * AI 知识库文档 Service 实现类 - * - * @author xiaoxin - */ -@Service -@Slf4j -public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService { - - @Resource - private AiKnowledgeDocumentMapper documentMapper; - @Resource - private AiKnowledgeSegmentMapper segmentMapper; - - @Resource - private TokenCountEstimator tokenCountEstimator; - @Resource - private AiKnowledgeService knowledgeService; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { - // 0. 校验并获取向量存储实例 - VectorStore vectorStore = knowledgeService.getVectorStoreById(createReqVO.getKnowledgeId()); - - // 1.1 下载文档 - TikaDocumentReader loader = new TikaDocumentReader(downloadFile(createReqVO.getUrl())); - List documents = loader.get(); - Document document = CollUtil.getFirst(documents); - // 1.2 文档记录入库 - String content = document.getContent(); - AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) - .setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length()) - .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); - documentMapper.insert(documentDO); - Long documentId = documentDO.getId(); - if (CollUtil.isEmpty(documents)) { - return documentId; - } - - // 2 构造文本分段器 - TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(createReqVO.getDefaultSegmentTokens(), createReqVO.getMinSegmentWordCount(), createReqVO.getMinChunkLengthToEmbed(), - createReqVO.getMaxNumSegments(), createReqVO.getKeepSeparator()); - // 2.1 文档分段 - List segments = tokenTextSplitter.apply(documents); - // 2.2 分段内容入库 - List segmentDOList = CollectionUtils.convertList(segments, - segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) - .setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId()) - .setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - segmentMapper.insertBatch(segmentDOList); - - // 3. 向量化并存储 - segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId())); - vectorStore.add(segments); - return documentId; - } - - @Override - public PageResult getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) { - return documentMapper.selectPage(pageReqVO); - } - - @Override - public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) { - // 1. 校验文档是否存在 - validateKnowledgeDocumentExists(reqVO.getId()); - // 2. 更新文档 - AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class); - documentMapper.updateById(document); - } - - /** - * 校验文档是否存在 - * - * @param id 文档编号 - * @return 文档信息 - */ - private AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) { - AiKnowledgeDocumentDO knowledgeDocument = documentMapper.selectById(id); - if (knowledgeDocument == null) { - throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS); - } - return knowledgeDocument; - } - - private org.springframework.core.io.Resource downloadFile(String url) { - try { - byte[] bytes = HttpUtil.downloadBytes(url); - return new ByteArrayResource(bytes); - } catch (Exception e) { - log.error("[downloadFile][url({}) 下载失败]", url, e); - throw new RuntimeException(e); - } - } - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java deleted file mode 100644 index 91bffc276..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.knowledge; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; - -import java.util.List; - -/** - * AI 知识库段落 Service 接口 - * - * @author xiaoxin - */ -public interface AiKnowledgeSegmentService { - - /** - * 获取段落分页 - * - * @param pageReqVO 分页查询 - * @return 文档分页 - */ - PageResult getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO); - - /** - * 更新段落的内容 - * - * @param reqVO 更新内容 - */ - void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO); - - /** - * 更新段落的状态 - * - * @param reqVO 更新内容 - */ - void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO); - - /** - * 召回段落 - * - * @param reqVO 召回请求信息 - * @return 召回的段落 - */ - List similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO); - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java deleted file mode 100644 index bfb22eecd..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ /dev/null @@ -1,134 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.knowledge; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.ListUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.document.Document; -import org.springframework.ai.vectorstore.SearchRequest; -import org.springframework.ai.vectorstore.VectorStore; -import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.List; -import java.util.Objects; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS; - -/** - * AI 知识库分片 Service 实现类 - * - * @author xiaoxin - */ -@Service -@Slf4j -public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService { - - @Resource - private AiKnowledgeSegmentMapper segmentMapper; - - @Resource - private AiKnowledgeService knowledgeService; - @Resource - private AiChatModelService chatModelService; - @Resource - private AiApiKeyService apiKeyService; - - @Override - public PageResult getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) { - return segmentMapper.selectPage(pageReqVO); - } - - @Override - public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) { - // 1. 校验 - AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId()); - - // 2.1 获取知识库向量实例 - VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId()); - // 2.2 删除原向量 - vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId())); - // 2.3 重新向量化 - Document document = new Document(reqVO.getContent()); - document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId()); - vectorStore.add(List.of(document)); - - // 3. 更新段落内容 - AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); - knowledgeSegment.setVectorId(document.getId()); - segmentMapper.updateById(knowledgeSegment); - } - - @Override - public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) { - // 0 校验 - AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId()); - // 1 获取知识库向量实例 - VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId()); - AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); - - if (Objects.equals(reqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { - // 2.1 启用重新向量化 - Document document = new Document(oldKnowledgeSegment.getContent()); - document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId()); - vectorStore.add(List.of(document)); - knowledgeSegment.setVectorId(document.getId()); - } else { - // 2.2 禁用删除向量 - vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId())); - knowledgeSegment.setVectorId(""); - } - // 3 更新段落状态 - segmentMapper.updateById(knowledgeSegment); - } - - @Override - public List similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO) { - // 1. 校验 - AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqVO.getKnowledgeId()); - AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); - - // 2. 获取向量存储实例 - VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); - - // 3.1 向量检索 - List documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent()) - .withTopK(knowledge.getTopK()) - .withSimilarityThreshold(knowledge.getSimilarityThreshold()) - .withFilterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build())); - if (CollUtil.isEmpty(documentList)) { - return ListUtil.empty(); - } - // 3.2 段落召回 - return segmentMapper.selectListByVectorIds(CollUtil.getFieldValues(documentList, "id", String.class)); - } - - /** - * 校验段落是否存在 - * - * @param id 文档编号 - * @return 段落信息 - */ - private AiKnowledgeSegmentDO validateKnowledgeSegmentExists(Long id) { - AiKnowledgeSegmentDO knowledgeSegment = segmentMapper.selectById(id); - if (knowledgeSegment == null) { - throw exception(KNOWLEDGE_SEGMENT_NOT_EXISTS); - } - return knowledgeSegment; - } - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java deleted file mode 100644 index 7060076a4..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.knowledge; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; -import org.springframework.ai.vectorstore.VectorStore; - -/** - * AI 知识库-基础信息 Service 接口 - * - * @author xiaoxin - */ -public interface AiKnowledgeService { - - /** - * 创建知识库 - * - * @param createReqVO 创建信息 - * @param userId 用户编号 - * @return 编号 - */ - Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId); - - /** - * 更新知识库 - * - * @param updateReqVO 更新信息 - * @param userId 用户编号 - */ - void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId); - - /** - * 校验知识库是否存在 - * - * @param id 记录编号 - */ - AiKnowledgeDO validateKnowledgeExists(Long id); - - /** - * 获得知识库分页 - * - * @param userId 用户编号 - * @param pageReqVO 分页查询 - * @return 知识库分页 - */ - PageResult getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO); - - /** - * 根据知识库编号获取向量存储实例 - * - * @param id 知识库编号 - * @return 向量存储实例 - */ - VectorStore getVectorStoreById(Long id); - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java deleted file mode 100644 index 2af8bc817..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java +++ /dev/null @@ -1,91 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.knowledge; - -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.vectorstore.VectorStore; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS; - -/** - * AI 知识库-基础信息 Service 实现类 - * - * @author xiaoxin - */ -@Service -@Slf4j -public class AiKnowledgeServiceImpl implements AiKnowledgeService { - - @Resource - private AiKnowledgeMapper knowledgeMapper; - - @Resource - private AiChatModelService chatModelService; - @Resource - private AiApiKeyService apiKeyService; - - @Override - public Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId) { - // 1. 校验模型配置 - AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getModelId()); - - // 2. 插入知识库 - AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) - .setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus()); - knowledgeMapper.insert(knowledgeBase); - return knowledgeBase.getId(); - } - - @Override - public void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId) { - // 1.1 校验知识库存在 - AiKnowledgeDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId()); - if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) { - throw exception(KNOWLEDGE_NOT_EXISTS); - } - // 1.2 校验模型配置 - AiChatModelDO model = chatModelService.validateChatModel(updateReqVO.getModelId()); - - // 2. 更新知识库 - AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class); - updateDO.setModel(model.getModel()); - knowledgeMapper.updateById(updateDO); - } - - @Override - public AiKnowledgeDO validateKnowledgeExists(Long id) { - AiKnowledgeDO knowledgeBase = knowledgeMapper.selectById(id); - if (knowledgeBase == null) { - throw exception(KNOWLEDGE_NOT_EXISTS); - } - return knowledgeBase; - } - - @Override - public PageResult getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO) { - return knowledgeMapper.selectPage(userId, pageReqVO); - } - - @Override - public VectorStore getVectorStoreById(Long id) { - AiKnowledgeDO knowledge = validateKnowledgeExists(id); - AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); - // 创建或获取 VectorStore 对象 - return apiKeyService.getOrCreateVectorStore(model.getKeyId()); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java deleted file mode 100644 index a72d50c4a..000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2023 - 2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.ai.autoconfigure.vectorstore.redis; - -import org.springframework.ai.embedding.EmbeddingModel; -import org.springframework.ai.vectorstore.RedisVectorStore; -import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; -import redis.clients.jedis.JedisPooled; - -/** - * TODO @xin 先拿 spring-ai 最新代码覆盖,1.0.0-M1 跟 redis 自动配置会冲突 - * - * TODO 这个官方,有说啥时候 fix 哇? - * TODO 看着是列在1.0.0-M2版本 - * - * @author Christian Tzolov - * @author Eddú Meléndez - */ -@AutoConfiguration(after = RedisAutoConfiguration.class) -@ConditionalOnClass({JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class}) -@ConditionalOnBean(JedisConnectionFactory.class) -@EnableConfigurationProperties(RedisVectorStoreProperties.class) -public class RedisVectorStoreAutoConfiguration { - - @Bean - @ConditionalOnMissingBean - public RedisVectorStore vectorStore(EmbeddingModel embeddingModel, RedisVectorStoreProperties properties, - JedisConnectionFactory jedisConnectionFactory) { - - var config = RedisVectorStoreConfig.builder() - .withIndexName(properties.getIndex()) - .withPrefix(properties.getPrefix()) - .build(); - - return new RedisVectorStore(config, embeddingModel, - new JedisPooled(jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort()), - properties.isInitializeSchema()); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java deleted file mode 100644 index de80401ed..000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright 2023 - 2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.ai.vectorstore; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ai.document.Document; -import org.springframework.ai.embedding.EmbeddingModel; -import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import redis.clients.jedis.JedisPooled; -import redis.clients.jedis.Pipeline; -import redis.clients.jedis.json.Path2; -import redis.clients.jedis.search.*; -import redis.clients.jedis.search.Schema.FieldType; -import redis.clients.jedis.search.schemafields.*; -import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; - -import java.text.MessageFormat; -import java.util.*; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -/** - * The RedisVectorStore is for managing and querying vector data in a Redis database. It - * offers functionalities like adding, deleting, and performing similarity searches on - * documents. - * - * The store utilizes RedisJSON and RedisSearch to handle JSON documents and to index and - * search vector data. It supports various vector algorithms (e.g., FLAT, HSNW) for - * efficient similarity searches. Additionally, it allows for custom metadata fields in - * the documents to be stored alongside the vector and content data. - * - * This class requires a RedisVectorStoreConfig configuration object for initialization, - * which includes settings like Redis URI, index name, field names, and vector algorithms. - * It also requires an EmbeddingModel to convert documents into embeddings before storing - * them. - * - * @author Julien Ruaux - * @author Christian Tzolov - * @author Eddú Meléndez - * @see VectorStore - * @see RedisVectorStoreConfig - * @see EmbeddingModel - */ -public class RedisVectorStore implements VectorStore, InitializingBean { - - public enum Algorithm { - - FLAT, HSNW - - } - - public record MetadataField(String name, FieldType fieldType) { - - public static MetadataField text(String name) { - return new MetadataField(name, FieldType.TEXT); - } - - public static MetadataField numeric(String name) { - return new MetadataField(name, FieldType.NUMERIC); - } - - public static MetadataField tag(String name) { - return new MetadataField(name, FieldType.TAG); - } - - } - - /** - * Configuration for the Redis vector store. - */ - public static final class RedisVectorStoreConfig { - - private final String indexName; - - private final String prefix; - - private final String contentFieldName; - - private final String embeddingFieldName; - - private final Algorithm vectorAlgorithm; - - private final List metadataFields; - - private RedisVectorStoreConfig() { - this(builder()); - } - - private RedisVectorStoreConfig(Builder builder) { - this.indexName = builder.indexName; - this.prefix = builder.prefix; - this.contentFieldName = builder.contentFieldName; - this.embeddingFieldName = builder.embeddingFieldName; - this.vectorAlgorithm = builder.vectorAlgorithm; - this.metadataFields = builder.metadataFields; - } - - /** - * Start building a new configuration. - * @return The entry point for creating a new configuration. - */ - public static Builder builder() { - - return new Builder(); - } - - /** - * {@return the default config} - */ - public static RedisVectorStoreConfig defaultConfig() { - - return builder().build(); - } - - public static class Builder { - - private String indexName = DEFAULT_INDEX_NAME; - - private String prefix = DEFAULT_PREFIX; - - private String contentFieldName = DEFAULT_CONTENT_FIELD_NAME; - - private String embeddingFieldName = DEFAULT_EMBEDDING_FIELD_NAME; - - private Algorithm vectorAlgorithm = DEFAULT_VECTOR_ALGORITHM; - - private List metadataFields = new ArrayList<>(); - - private Builder() { - } - - /** - * Configures the Redis index name to use. - * @param name the index name to use - * @return this builder - */ - public Builder withIndexName(String name) { - this.indexName = name; - return this; - } - - /** - * Configures the Redis key prefix to use (default: "embedding:"). - * @param prefix the prefix to use - * @return this builder - */ - public Builder withPrefix(String prefix) { - this.prefix = prefix; - return this; - } - - /** - * Configures the Redis content field name to use. - * @param name the content field name to use - * @return this builder - */ - public Builder withContentFieldName(String name) { - this.contentFieldName = name; - return this; - } - - /** - * Configures the Redis embedding field name to use. - * @param name the embedding field name to use - * @return this builder - */ - public Builder withEmbeddingFieldName(String name) { - this.embeddingFieldName = name; - return this; - } - - /** - * Configures the Redis vector algorithmto use. - * @param algorithm the vector algorithm to use - * @return this builder - */ - public Builder withVectorAlgorithm(Algorithm algorithm) { - this.vectorAlgorithm = algorithm; - return this; - } - - public Builder withMetadataFields(MetadataField... fields) { - return withMetadataFields(Arrays.asList(fields)); - } - - public Builder withMetadataFields(List fields) { - this.metadataFields = fields; - return this; - } - - /** - * {@return the immutable configuration} - */ - public RedisVectorStoreConfig build() { - - return new RedisVectorStoreConfig(this); - } - - } - - } - - private final boolean initializeSchema; - - public static final String DEFAULT_INDEX_NAME = "spring-ai-index"; - - public static final String DEFAULT_CONTENT_FIELD_NAME = "content"; - - public static final String DEFAULT_EMBEDDING_FIELD_NAME = "embedding"; - - public static final String DEFAULT_PREFIX = "embedding:"; - - public static final Algorithm DEFAULT_VECTOR_ALGORITHM = Algorithm.HSNW; - - private static final String QUERY_FORMAT = "%s=>[KNN %s @%s $%s AS %s]"; - - private static final Path2 JSON_SET_PATH = Path2.of("$"); - - private static final String JSON_PATH_PREFIX = "$."; - - private static final Logger logger = LoggerFactory.getLogger(RedisVectorStore.class); - - private static final Predicate RESPONSE_OK = Predicate.isEqual("OK"); - - private static final Predicate RESPONSE_DEL_OK = Predicate.isEqual(1l); - - private static final String VECTOR_TYPE_FLOAT32 = "FLOAT32"; - - private static final String EMBEDDING_PARAM_NAME = "BLOB"; - - public static final String DISTANCE_FIELD_NAME = "vector_score"; - - private static final String DEFAULT_DISTANCE_METRIC = "COSINE"; - - private final JedisPooled jedis; - - private final EmbeddingModel embeddingModel; - - private final RedisVectorStoreConfig config; - - private FilterExpressionConverter filterExpressionConverter; - - public RedisVectorStore(RedisVectorStoreConfig config, EmbeddingModel embeddingModel, JedisPooled jedis, - boolean initializeSchema) { - - Assert.notNull(config, "Config must not be null"); - Assert.notNull(embeddingModel, "Embedding model must not be null"); - this.initializeSchema = initializeSchema; - - this.jedis = jedis; - this.embeddingModel = embeddingModel; - this.config = config; - this.filterExpressionConverter = new RedisFilterExpressionConverter(this.config.metadataFields); - } - - public JedisPooled getJedis() { - return this.jedis; - } - - @Override - public void add(List documents) { - try (Pipeline pipeline = this.jedis.pipelined()) { - for (Document document : documents) { - var embedding = this.embeddingModel.embed(document); - document.setEmbedding(embedding); - - var fields = new HashMap(); - fields.put(this.config.embeddingFieldName, embedding); - fields.put(this.config.contentFieldName, document.getContent()); - fields.putAll(document.getMetadata()); - pipeline.jsonSetWithEscape(key(document.getId()), JSON_SET_PATH, fields); - } - List responses = pipeline.syncAndReturnAll(); - Optional errResponse = responses.stream().filter(Predicate.not(RESPONSE_OK)).findAny(); - if (errResponse.isPresent()) { - String message = MessageFormat.format("Could not add document: {0}", errResponse.get()); - if (logger.isErrorEnabled()) { - logger.error(message); - } - throw new RuntimeException(message); - } - } - } - - private String key(String id) { - return this.config.prefix + id; - } - - @Override - public Optional delete(List idList) { - try (Pipeline pipeline = this.jedis.pipelined()) { - for (String id : idList) { - pipeline.jsonDel(key(id)); - } - List responses = pipeline.syncAndReturnAll(); - Optional errResponse = responses.stream().filter(Predicate.not(RESPONSE_DEL_OK)).findAny(); - if (errResponse.isPresent()) { - if (logger.isErrorEnabled()) { - logger.error("Could not delete document: {}", errResponse.get()); - } - return Optional.of(false); - } - return Optional.of(true); - } - } - - @Override - public List similaritySearch(SearchRequest request) { - - Assert.isTrue(request.getTopK() > 0, "The number of documents to returned must be greater than zero"); - Assert.isTrue(request.getSimilarityThreshold() >= 0 && request.getSimilarityThreshold() <= 1, - "The similarity score is bounded between 0 and 1; least to most similar respectively."); - - String filter = nativeExpressionFilter(request); - - String queryString = String.format(QUERY_FORMAT, filter, request.getTopK(), this.config.embeddingFieldName, - EMBEDDING_PARAM_NAME, DISTANCE_FIELD_NAME); - - List returnFields = new ArrayList<>(); - this.config.metadataFields.stream().map(MetadataField::name).forEach(returnFields::add); - returnFields.add(this.config.embeddingFieldName); - returnFields.add(this.config.contentFieldName); - returnFields.add(DISTANCE_FIELD_NAME); - var embedding = toFloatArray(this.embeddingModel.embed(request.getQuery())); - Query query = new Query(queryString).addParam(EMBEDDING_PARAM_NAME, RediSearchUtil.toByteArray(embedding)) - .returnFields(returnFields.toArray(new String[0])) - .setSortBy(DISTANCE_FIELD_NAME, true) - .dialect(2); - - SearchResult result = this.jedis.ftSearch(this.config.indexName, query); - return result.getDocuments() - .stream() - .filter(d -> similarityScore(d) >= request.getSimilarityThreshold()) - .map(this::toDocument) - .toList(); - } - - private Document toDocument(redis.clients.jedis.search.Document doc) { - var id = doc.getId().substring(this.config.prefix.length()); - var content = doc.hasProperty(this.config.contentFieldName) ? doc.getString(this.config.contentFieldName) - : null; - Map metadata = this.config.metadataFields.stream() - .map(MetadataField::name) - .filter(doc::hasProperty) - .collect(Collectors.toMap(Function.identity(), doc::getString)); - metadata.put(DISTANCE_FIELD_NAME, 1 - similarityScore(doc)); - return new Document(id, content, metadata); - } - - private float similarityScore(redis.clients.jedis.search.Document doc) { - return (2 - Float.parseFloat(doc.getString(DISTANCE_FIELD_NAME))) / 2; - } - - private String nativeExpressionFilter(SearchRequest request) { - if (request.getFilterExpression() == null) { - return "*"; - } - return "(" + this.filterExpressionConverter.convertExpression(request.getFilterExpression()) + ")"; - } - - @Override - public void afterPropertiesSet() { - - if (!this.initializeSchema) { - return; - } - - // If index already exists don't do anything - if (this.jedis.ftList().contains(this.config.indexName)) { - return; - } - - String response = this.jedis.ftCreate(this.config.indexName, - FTCreateParams.createParams().on(IndexDataType.JSON).addPrefix(this.config.prefix), schemaFields()); - if (!RESPONSE_OK.test(response)) { - String message = MessageFormat.format("Could not create index: {0}", response); - throw new RuntimeException(message); - } - } - - private Iterable schemaFields() { - Map vectorAttrs = new HashMap<>(); - vectorAttrs.put("DIM", this.embeddingModel.dimensions()); - vectorAttrs.put("DISTANCE_METRIC", DEFAULT_DISTANCE_METRIC); - vectorAttrs.put("TYPE", VECTOR_TYPE_FLOAT32); - List fields = new ArrayList<>(); - fields.add(TextField.of(jsonPath(this.config.contentFieldName)).as(this.config.contentFieldName).weight(1.0)); - fields.add(VectorField.builder() - .fieldName(jsonPath(this.config.embeddingFieldName)) - .algorithm(vectorAlgorithm()) - .attributes(vectorAttrs) - .as(this.config.embeddingFieldName) - .build()); - - if (!CollectionUtils.isEmpty(this.config.metadataFields)) { - for (MetadataField field : this.config.metadataFields) { - fields.add(schemaField(field)); - } - } - return fields; - } - - private SchemaField schemaField(MetadataField field) { - String fieldName = jsonPath(field.name); - switch (field.fieldType) { - case NUMERIC: - return NumericField.of(fieldName).as(field.name); - case TAG: - return TagField.of(fieldName).as(field.name); - case TEXT: - return TextField.of(fieldName).as(field.name); - default: - throw new IllegalArgumentException( - MessageFormat.format("Field {0} has unsupported type {1}", field.name, field.fieldType)); - } - } - - private VectorAlgorithm vectorAlgorithm() { - if (config.vectorAlgorithm == Algorithm.HSNW) { - return VectorAlgorithm.HNSW; - } - return VectorAlgorithm.FLAT; - } - - private String jsonPath(String field) { - return JSON_PATH_PREFIX + field; - } - - private static float[] toFloatArray(List embeddingDouble) { - float[] embeddingFloat = new float[embeddingDouble.size()]; - int i = 0; - for (Double d : embeddingDouble) { - embeddingFloat[i++] = d.floatValue(); - } - return embeddingFloat; - } - -} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventType.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventType.java deleted file mode 100644 index 537e03e03..000000000 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventType.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * BPM 边界事件 (boundary event) 自定义类型枚举 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmBoundaryEventType { - - USER_TASK_TIMEOUT(1,"用户任务超时"); - - private final Integer type; - private final String name; - - public static BpmBoundaryEventType typeOf(Integer type) { - return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values()); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmFieldPermissionEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmFieldPermissionEnum.java deleted file mode 100644 index 5a9b4b26a..000000000 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmFieldPermissionEnum.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * BPM 表单权限的枚举 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmFieldPermissionEnum { - - READ(1, "只读"), - WRITE(2, "可编辑"), - NONE(3, "隐藏"); - - /** - * 权限 - */ - private final Integer permission; - /** - * 名字 - */ - private final String name; - - public static BpmFieldPermissionEnum valueOf(Integer permission) { - return ArrayUtil.firstMatch(item -> item.getPermission().equals(permission), values()); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModeConditionType.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModeConditionType.java deleted file mode 100644 index 234ec7e47..000000000 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModeConditionType.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 仿钉钉的流程器设计器条件节点的条件类型 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmSimpleModeConditionType implements IntArrayValuable { - - EXPRESSION(1, "条件表达式"), - RULE(2, "条件规则"); - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModeConditionType::getType).toArray(); - - private final Integer type; - - private final String name; - - public static BpmSimpleModeConditionType valueOf(Integer type) { - return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values()); - } - - @Override - public int[] array() { - return ARRAYS; - } -} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java deleted file mode 100644 index 36ad0e5ee..000000000 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java +++ /dev/null @@ -1,76 +0,0 @@ -package cn.iocoder.yudao.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; -import java.util.Objects; - -/** - * 仿钉钉的流程器设计器的模型节点类型 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmSimpleModelNodeType implements IntArrayValuable { - - // 0 ~ 1 开始和结束 - START_NODE(0, "startEvent", "开始节点"), - END_NODE(1, "endEvent", "结束节点"), - - // 10 ~ 49 各种节点 - START_USER_NODE(10, "userTask", "发起人节点"), // 发起人节点。前端的开始节点,Id 固定 - APPROVE_NODE(11, "userTask", "审批人节点"), - COPY_NODE(12, "serviceTask", "抄送人节点"), - - // 50 ~ 条件分支 - CONDITION_NODE(50, "sequenceFlow", "条件节点"), // 用于构建流转条件的表达式 - CONDITION_BRANCH_NODE(51, " “parallelGateway”", "条件分支节点"), // TODO @jason:是不是改成叫 条件分支? - PARALLEL_BRANCH_NODE(52, "exclusiveGateway", "并行分支节点"), // TODO @jason:是不是一个 并行分支 ?就可以啦? 后面是否去掉并行网关。只用包容网关 - INCLUSIVE_BRANCH_NODE(53, "inclusiveGateway", "包容分支节点"), - // TODO @jason:建议整合 join,最终只有 条件分支、并行分支、包容分支,三种~ - // TODO @芋艿。 感觉还是分开好理解一点,也好处理一点。前端结构中把聚合节点显示并传过来。 - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray(); - - public static final String BPMN_USER_TASK_TYPE = "userTask"; - - private final Integer type; - private final String bpmnType; - private final String name; - - /** - * 判断是否为分支节点 - * - * @param type 节点类型 - */ - public static boolean isBranchNode(Integer type) { - return Objects.equals(CONDITION_BRANCH_NODE.getType(), type) - || Objects.equals(PARALLEL_BRANCH_NODE.getType(), type) - || Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type); - } - - /** - * 判断是否需要记录的节点 - * - * @param bpmnType bpmn节点类型 - */ - public static boolean isRecordNode(String bpmnType) { - return Objects.equals(APPROVE_NODE.getBpmnType(), bpmnType) - || Objects.equals(END_NODE.getBpmnType(), bpmnType); - } - - public static BpmSimpleModelNodeType valueOf(Integer type) { - return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values()); - } - - @Override - public int[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java deleted file mode 100644 index 9d4dd63af..000000000 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 多人审批方式的枚举 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmUserTaskApproveMethodEnum implements IntArrayValuable { - - RANDOM(1, "随机挑选一人审批"), - RATIO(2, "多人会签(按通过比例)"), // 会签(按通过比例) - ANY(3, "多人或签(一人通过或拒绝)"), // 或签(通过只需一人,拒绝只需一人) - SEQUENTIAL(4, "依次审批"); // 依次审批 - - /** - * 审批方式 - */ - private final Integer method; - - /** - * 名字 - */ - private final String name; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskApproveMethodEnum::getMethod).toArray(); - - public static BpmUserTaskApproveMethodEnum valueOf(Integer method) { - return ArrayUtil.firstMatch(item -> item.getMethod().equals(method), values()); - } - - @Override - public int[] array() { - return ARRAYS; - } -} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java deleted file mode 100644 index 7a7242a49..000000000 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.bpm.enums.definition; - -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * BPM 用户任务的审批人为空时,处理类型枚举 - * - * @author 芋道源码 - */ -@RequiredArgsConstructor -@Getter -public enum BpmUserTaskAssignEmptyHandlerTypeEnum implements IntArrayValuable { - - APPROVE(1), // 自动通过 - REJECT(2), // 自动拒绝 - ASSIGN_USER(3), // 指定人员审批 - ASSIGN_ADMIN(4), // 转交给流程管理员 - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskAssignEmptyHandlerTypeEnum::getType).toArray(); - - private final Integer type; - - @Override - public int[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java deleted file mode 100644 index 501281502..000000000 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.bpm.enums.definition; - -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * BPM 用户任务的审批人与发起人相同时,处理类型枚举 - * - * @author 芋道源码 - */ -@RequiredArgsConstructor -@Getter -public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements IntArrayValuable { - - START_USER_AUDIT(1), // 由发起人对自己审批 - SKIP(2), // 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过 - TRANSFER_DEPT_LEADER(3); // 转交给部门负责人审批【参考飞书】:若部门负责人为空,则自动通过 - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmUserTaskAssignStartUserHandlerTypeEnum::getType).toArray(); - - private final Integer type; - - @Override - public int[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/base/package-info.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/base/package-info.java deleted file mode 100644 index 41ce65081..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/base/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 基础包,放一些通用的 VO 类 - */ -package cn.iocoder.yudao.module.bpm.controller.admin.base; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java deleted file mode 100644 index e245b3026..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.bpm.controller.admin.base.user; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "用户精简信息 VO") -@Data -public class UserSimpleBaseVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - private String nickname; - - @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") - private String avatar; - -} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java deleted file mode 100644 index 053583d4f..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotEmpty; - -@Schema(description = "管理后台 - 流程模型的更新 BPMN XML Request VO") -@Data -public class BpmModeUpdateBpmnReqVO { - - @Schema(description = "流程编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "流程编号不能为空") - private String id; - - @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "BPMN XML 不能为空") - private String bpmnXml; - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java deleted file mode 100644 index a43a4f969..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; - -/** - * BPM 流程 MetaInfo Response DTO - * 主要用于 { Model#setMetaInfo(String)} 的存储 - * - * 最终,它的字段和 {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} 是一致的 - * - * @author 芋道源码 - */ -@Data -public class BpmModelMetaInfoVO { - - @Schema(description = "流程图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg") - @NotEmpty(message = "流程图标不能为空") - @URL(message = "流程图标格式不正确") - private String icon; - - @Schema(description = "流程描述", example = "我是描述") - private String description; - - @Schema(description = "流程类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @InEnum(BpmModelTypeEnum.class) - @NotNull(message = "流程类型不能为空") - private Integer type; - - @Schema(description = "表单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @InEnum(BpmModelFormTypeEnum.class) - @NotNull(message = "表单类型不能为空") - private Integer formType; - @Schema(description = "表单编号", example = "1024") - private Long formId; // formType 为 NORMAL 使用,必须非空 - @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", - example = "/bpm/oa/leave/create") - private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空 - @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", - example = "/bpm/oa/leave/view") - private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空 - - @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否可见不能为空") - private Boolean visible; - - @Schema(description = "可发起用户编号数组", example = "[1,2,3]") - private List startUserIds; - - @Schema(description = "可管理用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2,4,6]") - @NotEmpty(message = "可管理用户编号数组不能为空") - private List managerUserIds; - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java deleted file mode 100644 index afee5ff19..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ /dev/null @@ -1,220 +0,0 @@ -package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.bpm.enums.definition.*; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; -import java.util.Map; - -@Schema(description = "管理后台 - 仿钉钉流程设计模型节点 VO") -@Data -@JsonInclude(JsonInclude.Include.NON_NULL) -public class BpmSimpleModelNodeVO { - - @Schema(description = "模型节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent_1") - @NotEmpty(message = "模型节点编号不能为空") - private String id; - - @Schema(description = "模型节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "模型节点类型不能为空") - @InEnum(BpmSimpleModelNodeType.class) - private Integer type; - - @Schema(description = "模型节点名称", example = "领导审批") - private String name; - - // TODO @jason:和 gpt 大模型对了下这个字段的命名,貌似叫 displayText 合适点。可以等最后我们全局替换下。(优先级:低) - @Schema(description = "节点展示内容", example = "指定成员: 芋道源码") - private String showText; - - @Schema(description = "子节点") - private BpmSimpleModelNodeVO childNode; // 补充说明:在该模型下,子节点有且仅有一个,不会有多个 - - @Schema(description = "条件节点") - private List conditionNodes; // 补充说明:有且仅有条件、并行、包容等分支会使用 - - @Schema(description = "条件类型", example = "1") - @InEnum(BpmSimpleModeConditionType.class) - private Integer conditionType; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE - - @Schema(description = "条件表达式", example = "${day>3}") - private String conditionExpression; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE - - @Schema(description = "是否默认条件", example = "true") - private Boolean defaultFlow; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE - /** - * 条件组 - */ - private ConditionGroups conditionGroups; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE - - @Schema(description = "候选人策略", example = "30") - @InEnum(BpmTaskCandidateStrategyEnum.class) - private Integer candidateStrategy; // 用于审批,抄送节点 - - @Schema(description = "候选人参数") - private String candidateParam; // 用于审批,抄送节点 - - @Schema(description = "审批节点类型", example = "1") - @InEnum(BpmUserTaskApproveTypeEnum.class) - private Integer approveType; // 用于审批节点 - - @Schema(description = "多人审批方式", example = "1") - @InEnum(BpmUserTaskApproveMethodEnum.class) - private Integer approveMethod; // 用于审批节点 - - @Schema(description = "通过比例", example = "100") - private Integer approveRatio; // 通过比例,当多人审批方式为:多人会签(按通过比例) 需要设置 - - @Schema(description = "表单权限", example = "[]") - private List> fieldsPermission; - - @Schema(description = "操作按钮设置", example = "[]") - private List buttonsSetting; // 用于审批节点 - - // TODO @jason:看看是不是可以简化;@芋艿: 暂时先放着。不知道后面是否会用到 - /** - * 附加节点 Id, 该节点不从前端传入。 由程序生成. 由于当个节点无法完成功能。 需要附加节点来完成。 - */ - @JsonIgnore - private String attachNodeId; - /** - * 审批节点拒绝处理 - */ - private RejectHandler rejectHandler; - - /** - * 审批节点超时处理 - */ - private TimeoutHandler timeoutHandler; - - @Schema(description = "审批节点的审批人与发起人相同时,对应的处理类型", example = "1") - @InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class) - private Integer assignStartUserHandlerType; - - /** - * 空处理策略 - */ - private AssignEmptyHandler assignEmptyHandler; - - @Schema(description = "审批节点拒绝处理策略") - @Data - public static class RejectHandler { - - @Schema(description = "拒绝处理类型", example = "1") - @InEnum(BpmUserTaskRejectHandlerType.class) - private Integer type; - - @Schema(description = "任务拒绝后驳回的节点 Id", example = "Activity_1") - private String returnNodeId; - } - - @Schema(description = "审批节点超时处理策略") - @Valid - @Data - public static class TimeoutHandler { - - @Schema(description = "是否开启超时处理", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - @NotNull(message = "是否开启超时处理不能为空") - private Boolean enable; - - @Schema(description = "任务超时未处理的行为", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "任务超时未处理的行为不能为空") - @InEnum(BpmUserTaskTimeoutHandlerTypeEnum.class) - private Integer type; - - @Schema(description = "超时时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "PT6H") - @NotEmpty(message = "超时时间不能为空") - private String timeDuration; - - @Schema(description = "最大提醒次数", example = "1") - private Integer maxRemindCount; - - } - - @Schema(description = "空处理策略") - @Data - @Valid - public static class AssignEmptyHandler { - - @Schema(description = "空处理类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "空处理类型不能为空") - @InEnum(BpmUserTaskAssignEmptyHandlerTypeEnum.class) - private Integer type; - - @Schema(description = "指定人员审批的用户编号数组", example = "1") - private List userIds; - - } - - @Schema(description = "操作按钮设置") - @Data - @Valid - public static class OperationButtonSetting { - - // TODO @jason:是不是按钮的标识?id 会和数据库的 id 自增有点模糊,key 标识会更合理一点点哈。 - @Schema(description = "按钮 Id", example = "1") - private Integer id; - - @Schema(description = "显示名称", example = "审批") - private String displayName; - - @Schema(description = "是否启用", example = "true") - private Boolean enable; - } - - @Schema(description = "条件组") - @Data - @Valid - public static class ConditionGroups { - - @Schema(description = "条件组下的条件关系是否为与关系", example = "true") - @NotNull(message = "条件关系不能为空") - private Boolean and; - - @Schema(description = "条件组下的条件", example = "[]") - @NotEmpty(message = "条件不能为空") - private List conditions; - } - - @Schema(description = "条件") - @Data - @Valid - public static class Condition { - - @Schema(description = "条件下的规则关系是否为与关系", example = "true") - @NotNull(message = "规则关系不能为空") - private Boolean and; - - @Schema(description = "条件下的规则", example = "[]") - @NotEmpty(message = "规则不能为空") - private List rules; - } - - @Schema(description = "条件规则") - @Data - @Valid - public static class ConditionRule { - - @Schema(description = "运行符号", example = "==") - @NotEmpty(message = "运行符号不能为空") - private String opCode; - - @Schema(description = "运算符左边的值,例如某个流程变量", example = "startUserId") - @NotEmpty(message = "运算符左边的值不能为空") - private String leftSide; - - @Schema(description = "运算符右边的值", example = "1") - @NotEmpty(message = "运算符右边的值不能为空") - private String rightSide; - } - - // TODO @芋艿:条件;建议可以固化的一些选项;然后有个表达式兜底;要支持 -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java deleted file mode 100644 index c3a525ae6..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -// TODO @jason:需要考虑,如果某个节点的配置不正确,需要有提示;具体怎么实现,可以讨论下; -@Schema(description = "管理后台 - 仿钉钉流程设计模型的新增/修改 Request VO") -@Data -public class BpmSimpleModelUpdateReqVO { - - @Schema(description = "流程模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotEmpty(message = "流程模型编号不能为空") - private String id; // 对应 Flowable act_re_model 表 ID_ 字段 - - @Schema(description = "仿钉钉流程设计模型对象", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "仿钉钉流程设计模型对象不能为空") - @Valid - private BpmSimpleModelNodeVO simpleModel; - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java deleted file mode 100644 index 445a09321..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; - -import cn.hutool.core.util.StrUtil; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.AssertTrue; - -// TODO @jason:这个可以简化下,使用 @RequestParam。嘿嘿,主要 VO 项不要太多 -@Schema(description = "管理后台 - 审批详情 Request VO") -@Data -public class BpmApprovalDetailReqVO { - - @Schema(description = "流程定义的编号", example = "1024") - private String processDefinitionId; - - @Schema(description = "流程实例的编号", example = "1024") - private String processInstanceId; - - @AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空") - @JsonIgnore - public boolean isValidProcessParam() { - return StrUtil.isNotEmpty(processDefinitionId) || StrUtil.isNotEmpty(processInstanceId); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java deleted file mode 100644 index 283373893..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java +++ /dev/null @@ -1,87 +0,0 @@ -package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - - -@Schema(description = "管理后台 - 审批详情 Response VO") -@Data -public class BpmApprovalDetailRespVO { - - @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 - - @Schema(description = "审批信息列表", requiredMode = Schema.RequiredMode.REQUIRED) - private List approveNodes; - - @Schema(description = "审批节点信息") - @Data - public static class ApprovalNodeInfo { - - @Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode") - private String id; - - @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "发起人") - private String name; - - @Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer nodeType; // 参见 BpmSimpleModelNodeType 枚举 - - @Schema(description = "节点状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - private Integer status; // 参见 BpmTaskStatusEnum 枚举 - - @Schema(description = "节点的开始时间") - private LocalDateTime startTime; - @Schema(description = "节点的结束时间") - private LocalDateTime endTime; - - @Schema(description = "审批节点的任务信息") - private List tasks; - - @Schema(description = "候选人用户列表") - // TODO @jason:candidateUserList => candidateUsers,保持和 tasks 的命名风格一致哈 - private List candidateUserList; // 用于未运行任务节点 - - } - - // TODO @jason:可以替换成 UserSimpleBaseVO。简化下 - @Schema(description = "用户信息") - @Data - public static class User { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - private String nickname; - - @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") - private String avatar; - - } - - @Schema(description = "审批任务信息") - @Data - public static class ApprovalTaskInfo { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private String id; - - @Schema(description = "任务所属人", example = "1024") - private User ownerUser; - - @Schema(description = "任务分配人", example = "2048") - private User assigneeUser; - - @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; // 参见 BpmTaskStatusEnum 枚举 - - @Schema(description = "审批意见", example = "同意") - private String reason; - - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmFormFieldsPermissionReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmFormFieldsPermissionReqVO.java deleted file mode 100644 index 39dda8246..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmFormFieldsPermissionReqVO.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; - -import cn.hutool.core.util.StrUtil; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.AssertTrue; - -@Schema(description = "管理后台 - 表单字段权限 Request VO") -@Data -public class BpmFormFieldsPermissionReqVO { - - @Schema(description = "流程定义的编号", example = "1024") - private String processDefinitionId; - - @Schema(description = "流程实例的编号", example = "1024") - private String processInstanceId; - - @Schema(description = "流程活动编号", example = "StartUserNode") - private String activityId; // 对应 BPMN XML 节点 Id - - @Schema(description = "流程任务编号", example = "95f2f08b-621b-11ef-bf39-00ff4722db8b") - private String taskId; // UserTask 对应的Id - - @AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空") - @JsonIgnore - public boolean isValidProcessParam() { - return StrUtil.isNotEmpty(processDefinitionId) || StrUtil.isNotEmpty(processInstanceId); - } - - @AssertTrue(message = "流程活动编号和流程任务编号编号不能同时为空") - @JsonIgnore - public boolean isValidActivityParam() { - return StrUtil.isNotEmpty(activityId) || StrUtil.isNotEmpty(taskId); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractDeptLeaderStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractDeptLeaderStrategy.java deleted file mode 100644 index d5ac6706f..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractDeptLeaderStrategy.java +++ /dev/null @@ -1,81 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import cn.iocoder.yudao.module.system.api.dept.DeptApi; -import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; - -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -/** - * 部门的负责人 {@link BpmTaskCandidateStrategy} 抽象类 - * - * @author jason - */ -public abstract class BpmTaskCandidateAbstractDeptLeaderStrategy extends BpmTaskCandidateAbstractStrategy { - - protected DeptApi deptApi; - - public BpmTaskCandidateAbstractDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) { - super(adminUserApi); - this.deptApi = deptApi; - } - - /** - * 获得指定层级的部门负责人,只有第 level 的负责人 - * - * @param dept 指定部门 - * @param level 第几级 - * @return 部门负责人的编号 - */ - protected Long getAssignLevelDeptLeaderId(DeptRespDTO dept, Integer level) { - Assert.isTrue(level > 0, "level 必须大于 0"); - if (dept == null) { - return null; - } - DeptRespDTO currentDept = dept; - for (int i = 1; i < level; i++) { - DeptRespDTO parentDept = deptApi.getDept(currentDept.getParentId()).getCheckedData(); - if (parentDept == null) { // 找不到父级部门,到了最高级。返回最高级的部门负责人 - break; - } - currentDept = parentDept; - } - return currentDept.getLeaderUserId(); - } - - /** - * 获得连续层级的部门负责人,包含 [1, level] 的负责人 - * - * @param deptIds 指定部门编号数组 - * @param level 最大层级 - * @return 连续部门负责人 Id - */ - protected Set getMultiLevelDeptLeaderIds(List deptIds, Integer level) { - Assert.isTrue(level > 0, "level 必须大于 0"); - if (CollUtil.isEmpty(deptIds)) { - return new HashSet<>(); - } - Set deptLeaderIds = new LinkedHashSet<>(); // 保证有序 - for (Long deptId : deptIds) { - DeptRespDTO dept = deptApi.getDept(deptId).getCheckedData(); - for (int i = 0; i < level; i++) { - if (dept.getLeaderUserId() != null) { - deptLeaderIds.add(dept.getLeaderUserId()); - } - DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData(); - if (parentDept == null) { // 找不到父级部门. 已经到了最高层级了 - break; - } - dept = parentDept; - } - } - return deptLeaderIds; - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractStrategy.java deleted file mode 100644 index 8ff2bdaab..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAbstractStrategy.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; - -import java.util.Map; -import java.util.Set; - -/** - * {@link BpmTaskCandidateStrategy} 抽象类 - * - * @author jason - */ -public abstract class BpmTaskCandidateAbstractStrategy implements BpmTaskCandidateStrategy { - - protected AdminUserApi adminUserApi; - - public BpmTaskCandidateAbstractStrategy(AdminUserApi adminUserApi) { - this.adminUserApi = adminUserApi; - } - - @Override - public void removeDisableUsers(Set users) { - if (CollUtil.isEmpty(users)) { - return; - } - Map userMap = adminUserApi.getUserMap(users); - users.removeIf(id -> { - AdminUserRespDTO user = userMap.get(id); - return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()); - }); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAssignEmptyStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAssignEmptyStrategy.java deleted file mode 100644 index 611f34ab1..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateAssignEmptyStrategy.java +++ /dev/null @@ -1,66 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; - -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import org.flowable.engine.delegate.DelegateExecution; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -/** - * 审批人为空 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author kyle - */ -@Component -public class BpmTaskCandidateAssignEmptyStrategy extends BpmTaskCandidateAbstractStrategy { - - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmProcessDefinitionService processDefinitionService; - - public BpmTaskCandidateAssignEmptyStrategy(AdminUserApi adminUserApi) { - super(adminUserApi); - } - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY; - } - - @Override - public void validateParam(String param) { - } - - @Override - public Set calculateUsers(DelegateExecution execution, String param) { - // 情况一:指定人员审批 - Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(execution.getCurrentFlowElement()); - if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType())) { - Set users = new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(execution.getCurrentFlowElement())); - removeDisableUsers(users); - return users; - } - - // 情况二:流程管理员 - if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType())) { - BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId()); - Assert.notNull(processDefinition, "流程定义({})不存在", execution.getProcessDefinitionId()); - return new HashSet<>(processDefinition.getManagerUserIds()); - } - - // 都不满足,还是返回空 - return new HashSet<>(); - } - -} \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptLeaderMultiStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptLeaderMultiStrategy.java deleted file mode 100644 index de0906923..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateDeptLeaderMultiStrategy.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; - -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.util.string.StrUtils; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import cn.iocoder.yudao.module.system.api.dept.DeptApi; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import org.springframework.stereotype.Component; - -import java.util.Set; - -/** - * 连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author jason - */ -@Component -public class BpmTaskCandidateDeptLeaderMultiStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy { - - public BpmTaskCandidateDeptLeaderMultiStrategy(AdminUserApi adminUserApi, DeptApi deptApi) { - super(adminUserApi, deptApi); - } - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.MULTI_DEPT_LEADER_MULTI; - } - - @Override - public void validateParam(String param) { - // 参数格式: | 分隔:1)左边为部门(多个部门用 , 分隔)。2)右边为部门层级 - String[] params = param.split("\\|"); - Assert.isTrue(params.length == 2, "参数格式不匹配"); - deptApi.validateDeptList(StrUtils.splitToLong(params[0], ",")).checkError(); - Assert.isTrue(Integer.parseInt(params[1]) > 0, "部门层级必须大于 0"); - } - - @Override - public Set calculateUsers(String param) { - String[] params = param.split("\\|"); - return getMultiLevelDeptLeaderIds(StrUtils.splitToLong(params[0], ","), Integer.valueOf(params[1])); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java deleted file mode 100644 index 9dfe95eed..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; - -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.util.number.NumberUtils; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; -import cn.iocoder.yudao.module.system.api.dept.DeptApi; -import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.HashSet; -import java.util.Set; - -import static cn.hutool.core.collection.ListUtil.toList; - -/** - * 发起人连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author jason - */ -@Component -public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy { - - @Resource - @Lazy - private BpmProcessInstanceService processInstanceService; - - public BpmTaskCandidateStartUserDeptLeaderMultiStrategy(AdminUserApi adminUserApi, DeptApi deptApi) { - super(adminUserApi, deptApi); - } - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER_MULTI; - } - - @Override - public void validateParam(String param) { - // 参数是部门的层级 - Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0"); - } - - @Override - public Set calculateUsers(DelegateExecution execution, String param) { - // 获得流程发起人 - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); - // 获取发起人的 multi 部门负责人 - DeptRespDTO dept = getStartUserDept(startUserId); - if (dept == null) { - return new HashSet<>(); - } - Set users = getMultiLevelDeptLeaderIds(toList(dept.getId()), Integer.valueOf(param)); // 参数是部门的层级 - // TODO @jason:这里 removeDisableUsers 的原因是啥呀? - removeDisableUsers(users); - return users; - } - - @Override - public Set calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) { - DeptRespDTO dept = getStartUserDept(startUserId); - if (dept == null) { - return new HashSet<>(); - } - Set users = getMultiLevelDeptLeaderIds(toList(dept.getId()), Integer.valueOf(param)); // 参数是部门的层级 - removeDisableUsers(users); - return users; - } - - /** - * 获取发起人的部门 - * - * @param startUserId 发起人 Id - */ - protected DeptRespDTO getStartUserDept(Long startUserId) { - AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData(); - if (startUser.getDeptId() == null) { // 找不到部门 - return null; - } - return deptApi.getDept(startUser.getDeptId()).getCheckedData(); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java deleted file mode 100644 index 4d1a83da9..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserDeptLeaderStrategy.java +++ /dev/null @@ -1,91 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; - -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.util.number.NumberUtils; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; -import cn.iocoder.yudao.module.system.api.dept.DeptApi; -import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.HashSet; -import java.util.Set; - -import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; - -/** - * 发起人的部门负责人, 可以是上级部门负责人 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author jason - */ -@Component -public class BpmTaskCandidateStartUserDeptLeaderStrategy extends BpmTaskCandidateAbstractDeptLeaderStrategy { - - @Resource - @Lazy // 避免循环依赖 - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER; - } - - public BpmTaskCandidateStartUserDeptLeaderStrategy(AdminUserApi adminUserApi, DeptApi deptApi) { - super(adminUserApi, deptApi); - } - - @Override - public void validateParam(String param) { - // 参数是部门的层级 - Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0"); - } - - @Override - public Set calculateUsers(DelegateExecution execution, String param) { - // 获得流程发起人 - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); - // 获取发起人的部门负责人 - Set users = getStartUserDeptLeader(startUserId, param); - removeDisableUsers(users); - return users; - } - - @Override - public Set calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) { - // 获取发起人的部门负责人 - Set users = getStartUserDeptLeader(startUserId, param); - removeDisableUsers(users); - return users; - } - - private Set getStartUserDeptLeader(Long startUserId, String param) { - DeptRespDTO dept = getStartUserDept(startUserId); - if (dept == null) { - return new HashSet<>(); - } - Long deptLeaderId = getAssignLevelDeptLeaderId(dept, Integer.valueOf(param)); // 参数是部门的层级 - return deptLeaderId != null ? asSet(deptLeaderId) : new HashSet<>(); - } - - /** - * 获取发起人的部门 - * - * @param startUserId 发起人 Id - */ - protected DeptRespDTO getStartUserDept(Long startUserId) { - AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData(); - if (startUser.getDeptId() == null) { // 找不到部门 - return null; - } - return deptApi.getDept(startUser.getDeptId()).getCheckedData(); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java deleted file mode 100644 index 9b4c89519..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/BpmTaskCandidateStartUserStrategy.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy; - -import cn.iocoder.yudao.framework.common.util.collection.SetUtils; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.Set; - -/** - * 发起人自己 {@link BpmTaskCandidateUserStrategy} 实现类 - *

- * 适合场景:用于需要发起人信息复核等场景 - * - * @author jason - */ -@Component -public class BpmTaskCandidateStartUserStrategy extends BpmTaskCandidateAbstractStrategy { - - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmProcessInstanceService processInstanceService; - - public BpmTaskCandidateStartUserStrategy(AdminUserApi adminUserApi) { - super(adminUserApi); - } - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.START_USER; - } - - @Override - public void validateParam(String param) { - } - - @Override - public boolean isParamRequired() { - return false; - } - - @Override - public Set calculateUsers(DelegateExecution execution, String param) { - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - Set users = SetUtils.asSet(Long.valueOf(processInstance.getStartUserId())); - removeDisableUsers(users); - return users; - } - - @Override - public Set calculateUsers(Long startUserId, ProcessInstance processInstance, String activityId, String param) { - Set users = SetUtils.asSet(startUserId); - removeDisableUsers(users); - return users; - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java deleted file mode 100644 index e2a7252b7..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.el; - -import org.flowable.common.engine.api.variable.VariableContainer; -import org.flowable.common.engine.impl.el.function.AbstractFlowableVariableExpressionFunction; -import org.springframework.stereotype.Component; - -// TODO @jason:这个自定义转换的原因是啥呀? -/** - * 根据流程变量 variable 的类型, 转换参数的值 - * - * @author jason - */ -@Component -public class VariableConvertByTypeExpressionFunction extends AbstractFlowableVariableExpressionFunction { - - public VariableConvertByTypeExpressionFunction() { - super("convertByType"); - } - - public static Object convertByType(VariableContainer variableContainer, String variableName, Object parmaValue) { - Object variable = variableContainer.getVariable(variableName); - if (variable != null && parmaValue != null) { - // 如果值不是字符串类型, 流程变量的类型是字符串。 把值转成字符串 - if (!(parmaValue instanceof String) && variable instanceof String ) { - return parmaValue.toString(); - } - } - return parmaValue; - } -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/SimpleModelConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/SimpleModelConstants.java deleted file mode 100644 index 2275f0c12..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/SimpleModelConstants.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums; - -// TODO @jason:要不合并到 BpmnModelConstants 那 -/** - * 仿钉钉快搭 JSON 常量信息 - * - * @author jason - */ -public interface SimpleModelConstants { - - // TODO @芋艿:条件表达式的字段名 - - /** - * 网关节点默认序列流属性 - */ - String DEFAULT_FLOW_ATTRIBUTE = "defaultFlow"; - - /** - * 条件节点的条件类型属性 - */ - String CONDITION_TYPE_ATTRIBUTE = "conditionType"; - - /** - * 条件节点条件表达式属性 - */ - String CONDITION_EXPRESSION_ATTRIBUTE = "conditionExpression"; - - /** - * 条件规则的条件组属性 - */ - String CONDITION_GROUPS_ATTRIBUTE = "conditionGroups"; - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java deleted file mode 100644 index e956b1f64..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.delegate.JavaDelegate; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.Set; - -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate.BEAN_NAME; - -/** - * 处理抄送用户的 {@link JavaDelegate} 的实现类 - * - * 目前只有快搭模式的【抄送节点】使用 - * - * @author jason - */ -@Component(BEAN_NAME) -public class BpmCopyTaskDelegate implements JavaDelegate { - - public static final String BEAN_NAME = "bpmCopyTaskDelegate"; - - @Resource - private BpmTaskCandidateInvoker taskCandidateInvoker; - - @Resource - private BpmProcessInstanceCopyService processInstanceCopyService; - - @Override - public void execute(DelegateExecution execution) { - // 1. 获得抄送人 - Set userIds = taskCandidateInvoker.calculateUsers(execution); - if (CollUtil.isEmpty(userIds)) { - return; - } - // 2. 执行抄送 - FlowElement currentFlowElement = execution.getCurrentFlowElement(); - processInstanceCopyService.createProcessInstanceCopy(userIds, execution.getProcessInstanceId(), - currentFlowElement.getId(), null, currentFlowElement.getName()); - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java deleted file mode 100644 index 2ea7bf14b..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ /dev/null @@ -1,632 +0,0 @@ -package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.*; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.RejectHandler; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModeConditionType; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveMethodEnum; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; -import org.flowable.bpmn.BpmnAutoLayout; -import org.flowable.bpmn.model.Process; -import org.flowable.bpmn.model.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting; -import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler; -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*; -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveMethodEnum.*; -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum.USER; -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP; -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum.REMINDER; -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum.START_USER; -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; -import static org.flowable.bpmn.constants.BpmnXMLConstants.*; - -/** - * 仿钉钉快搭模型相关的工具方法 - * - * @author jason - */ -public class SimpleModelUtils { - - /** - * 聚合网关节点 Id 后缀 - */ - public static final String JOIN_GATE_WAY_NODE_ID_SUFFIX = "_join"; - - /** - * 所有审批人同意的表达式 - */ - public static final String ALL_APPROVE_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances >= nrOfInstances }"; - - /** - * 任一一名审批人同意的表达式 - */ - public static final String ANY_OF_APPROVE_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances > 0 }"; - - /** - * 按通过比例完成表达式 - */ - public static final String APPROVE_BY_RATIO_COMPLETE_EXPRESSION = "${ nrOfCompletedInstances/nrOfInstances >= %s}"; - - // TODO @yunai:注释需要完善下; - - /** - * 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model (待完善) - * - * @param processId 流程标识 - * @param processName 流程名称 - * @param simpleModelNode 仿钉钉流程设计模型数据结构 - * @return Bpmn Model - */ - public static BpmnModel buildBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) { - BpmnModel bpmnModel = new BpmnModel(); - // 不加这个 解析 Message 会报 NPE 异常 . - bpmnModel.setTargetNamespace(BPMN2_NAMESPACE); // TODO @jason:待定:是不是搞个自定义的 namespace; - // TODO 芋艿:后续在 review - - Process process = new Process(); - process.setId(processId); - process.setName(processName); - process.setExecutable(Boolean.TRUE); // TODO @jason:这个是必须设置的么? - bpmnModel.addProcess(process); - - // TODO 芋艿:这里可能纠结下,到底前端传递,还是后端创建出来。 - // 目前前端的第一个节点是“发起人节点”。这里构建一个 StartNode,用于创建 Bpmn 的 StartEvent 节点 - BpmSimpleModelNodeVO startNode = buildStartSimpleModelNode(); - startNode.setChildNode(simpleModelNode); - // 从 前端模型数据结构 SimpleModel 构建 FlowNode 并添加到 Main Process - traverseNodeToBuildFlowNode(startNode, process); - // 找到 end event - EndEvent endEvent = (EndEvent) CollUtil.findOne(process.getFlowElements(), item -> item instanceof EndEvent); - - // 构建并添加节点之间的连线 Sequence Flow - traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId()); - // 自动布局 - new BpmnAutoLayout(bpmnModel).execute(); - return bpmnModel; - } - - private static BpmSimpleModelNodeVO buildStartSimpleModelNode() { - BpmSimpleModelNodeVO startNode = new BpmSimpleModelNodeVO(); - startNode.setId(START_EVENT_NODE_ID); - startNode.setName(START_EVENT_NODE_NAME); - startNode.setType(START_NODE.getType()); - return startNode; - } - - // TODO @芋艿:在优化下这个注释 - private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { - // 1.1 无效节点返回 - if (!isValidNode(node)) { - return; - } - // 1.2 END_NODE 直接返回 - BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType()); - Assert.notNull(nodeType, "模型节点类型不支持"); - if (nodeType == END_NODE) { - return; - } - // 2.1 情况一:普通节点 - BpmSimpleModelNodeVO childNode = node.getChildNode(); - if (!BpmSimpleModelNodeType.isBranchNode(node.getType())) { - if (!isValidNode(childNode)) { - // 2.1.1 普通节点且无孩子节点。分两种情况 - // a.结束节点 b. 条件分支的最后一个节点.与分支节点的孩子节点或聚合节点建立连线。 - if (StrUtil.isNotEmpty(node.getAttachNodeId())) { - // 2.1.1.1 如果有附加节点. 需要先建立和附加节点的连线。再建立附加节点和目标节点的连线 - List sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), targetNodeId); - sequenceFlows.forEach(process::addFlowElement); - } else { - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), targetNodeId, null, null, null); - process.addFlowElement(sequenceFlow); - } - } else { - // 2.1.2 普通节点且有孩子节点。建立连线 - if (StrUtil.isNotEmpty(node.getAttachNodeId())) { - // 2.1.1.2 如果有附加节点. 需要先建立和附加节点的连线。再建立附加节点和目标节点的连线 - List sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), childNode.getId()); - sequenceFlows.forEach(process::addFlowElement); - } else { - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), childNode.getId(), null, null, null); - process.addFlowElement(sequenceFlow); - } - // 递归调用后续节点 - traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId); - } - } else { - // 2.2 情况二:分支节点 - List conditionNodes = node.getConditionNodes(); - Assert.notEmpty(conditionNodes, "分支节点的条件节点不能为空"); - // 分支终点节点 Id - String branchEndNodeId = null; - if (nodeType == CONDITION_BRANCH_NODE) { // 条件分支 - // 分两种情况 1. 分支节点有孩子节点为孩子节点 Id 2. 分支节点孩子为无效节点时 (分支嵌套且为分支最后一个节点) 为分支终点节点Id - branchEndNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; - } else if (nodeType == PARALLEL_BRANCH_NODE) { // 并行分支 - // 分支节点:分支终点节点 Id 为程序创建的网关集合节点。目前不会从前端传入。 - branchEndNodeId = node.getId() + JOIN_GATE_WAY_NODE_ID_SUFFIX; - } - // TODO 包容网关待实现 - Assert.notEmpty(branchEndNodeId, "分支终点节点 Id 不能为空"); - // 3.1 遍历分支节点. 如下情况: - // 分支1、A->B->C->D->E 和 分支2、A->D->E。 A为分支节点, D为A孩子节点 - for (BpmSimpleModelNodeVO item : conditionNodes) { - // TODO @jason:条件分支的情况下,需要分 item 搞的条件,和 conditionNodes 搞的条件 - // @芋艿 这个是啥意思。 这里的 item 的节点类型为 BpmSimpleModelNodeType.CONDITION_NODE 类型,没有对应的 bpmn 的节点。 仅仅用于构建条件表达式。 - Assert.isTrue(Objects.equals(item.getType(), CONDITION_NODE.getType()), "条件节点类型不符合"); - // 构建表达式,可以为空. 并行分支为空 - String conditionExpression = buildConditionExpression(item); - BpmSimpleModelNodeVO nextNodeOnCondition = item.getChildNode(); - // 3.2 分支有后续节点, 分支1: A->B->C->D - if (isValidNode(nextNodeOnCondition)) { - // 3.2.1 建立 A->B - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), nextNodeOnCondition.getId(), - item.getId(), item.getName(), conditionExpression); - process.addFlowElement(sequenceFlow); - // 3.2.2 递归调用后续节点连线。 建立 B->C->D 的连线 - traverseNodeToBuildSequenceFlow(process, nextNodeOnCondition, branchEndNodeId); - } else { - // 3.3 分支无后续节点 建立 A->D - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, - item.getId(), item.getName(), conditionExpression); - process.addFlowElement(sequenceFlow); - } - } - // 如果是并行分支。由于是程序创建的聚合网关。需要手工创建聚合网关和下一个节点的连线 - if (nodeType == PARALLEL_BRANCH_NODE) { - String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId, null, null, null); - process.addFlowElement(sequenceFlow); - } - // 4.递归调用后续节点 继续递归建立 D->E 的连线 - traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId); - } - } - - /** - * 构建有附加节点的连线 - * - * @param nodeId 当前节点 Id - * @param attachNodeId 附属节点 Id - * @param targetNodeId 目标节点 Id - */ - private static List buildAttachNodeSequenceFlow(String nodeId, String attachNodeId, String targetNodeId) { - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(nodeId, attachNodeId, null, null, null); - SequenceFlow attachSequenceFlow = buildBpmnSequenceFlow(attachNodeId, targetNodeId, null, null, null); - return CollUtil.newArrayList(sequenceFlow, attachSequenceFlow); - } - - /** - * 构造条件表达式 - * - * @param conditionNode 条件节点 - */ - public static String buildConditionExpression(BpmSimpleModelNodeVO conditionNode) { - BpmSimpleModeConditionType conditionTypeEnum = BpmSimpleModeConditionType.valueOf(conditionNode.getConditionType()); - String conditionExpression = null; - if (conditionTypeEnum == BpmSimpleModeConditionType.EXPRESSION) { - conditionExpression = conditionNode.getConditionExpression(); - } else if (conditionTypeEnum == BpmSimpleModeConditionType.RULE) { - ConditionGroups conditionGroups = conditionNode.getConditionGroups(); - if (conditionGroups != null && CollUtil.isNotEmpty(conditionGroups.getConditions())) { - List strConditionGroups = conditionGroups.getConditions().stream().map(item -> { - if (CollUtil.isNotEmpty(item.getRules())) { - Boolean and = item.getAnd(); - List list = CollectionUtils.convertList(item.getRules(), (rule) -> { - // 如果非数值类型加引号 - String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide() : "\"" + rule.getRightSide() + "\""; - return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide); - }); - return "(" + CollUtil.join(list, and ? " && " : " || ") + ")"; - } else { - return ""; - } - }).collect(Collectors.toList()); - conditionExpression = String.format("${%s}", CollUtil.join(strConditionGroups, conditionGroups.getAnd() ? " && " : " || ")); - } - } - // TODO 待增加其它类型 - return conditionExpression; - } - - private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId, String seqFlowId, String seqName, String conditionExpression) { - Assert.notEmpty(sourceId, "sourceId 不能为空"); - Assert.notEmpty(targetId, "targetId 不能为空"); - // TODO @jason:如果 seqFlowId 不存在的时候,是不是要生成一个默认的 seqFlowId? @芋艿: 貌似不需要,Flowable 会默认生成 - // TODO @jason:如果 name 不存在的时候,是不是要生成一个默认的 name? @芋艿: 不需要生成默认的吧? 这个会在流程图展示的, 一般用户填写的。不好生成默认的吧 - SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId); - if (StrUtil.isNotEmpty(conditionExpression)) { - sequenceFlow.setConditionExpression(conditionExpression); - } - if (StrUtil.isNotEmpty(seqFlowId)) { - sequenceFlow.setId(seqFlowId); - } - if (StrUtil.isNotEmpty(seqName)) { - sequenceFlow.setName(seqName); - } - return sequenceFlow; - } - - // TODO @芋艿 改成了 traverseNodeToBuildFlowNode, 连线的叫 traverseNodeToBuildSequenceFlow - private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) { - // 判断是否有效节点 - if (!isValidNode(node)) { - return; - } - BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType()); - Assert.notNull(nodeType, "模型节点类型不支持"); - - List flowElements = buildFlowNode(node, nodeType); - flowElements.forEach(process::addFlowElement); - - // 如果不是网关类型的接口, 并且chileNode为空退出 - // 如果是“分支”节点,则递归处理条件 - if (BpmSimpleModelNodeType.isBranchNode(node.getType()) - && ArrayUtil.isNotEmpty(node.getConditionNodes())) { - node.getConditionNodes().forEach(item -> traverseNodeToBuildFlowNode(item.getChildNode(), process)); - } - - // 如果有“子”节点,则递归处理子节点 - traverseNodeToBuildFlowNode(node.getChildNode(), process); - } - - public static boolean isValidNode(BpmSimpleModelNodeVO node) { - return node != null && node.getId() != null; - } - - public static boolean isSequentialApproveNode(BpmSimpleModelNodeVO node) { - return APPROVE_NODE.getType().equals(node.getType()) && SEQUENTIAL.getMethod().equals(node.getApproveMethod()); - } - - private static List buildFlowNode(BpmSimpleModelNodeVO node, BpmSimpleModelNodeType nodeType) { - List list = new ArrayList<>(); - switch (nodeType) { - case START_NODE: { // 开始节点 - StartEvent startEvent = convertStartNode(node); - list.add(startEvent); - break; - } - case END_NODE: { // 结束节点 - EndEvent endEvent = convertEndNode(node); - list.add(endEvent); - break; - } - case START_USER_NODE: { // 发起人节点 - UserTask userTask = convertStartUserNode(node); - list.add(userTask); - break; - } - case APPROVE_NODE: { // 审批节点 - List flowElements = convertApproveNode(node); - list.addAll(flowElements); - break; - } - case COPY_NODE: { // 抄送节点 - ServiceTask serviceTask = convertCopyNode(node); - list.add(serviceTask); - break; - } - case CONDITION_BRANCH_NODE: { - ExclusiveGateway exclusiveGateway = convertConditionBranchNode(node); - list.add(exclusiveGateway); - break; - } - case PARALLEL_BRANCH_NODE: { - List parallelGateways = convertParallelBranchNode(node); - list.addAll(parallelGateways); - break; - } - - case INCLUSIVE_BRANCH_NODE: { - // TODO jason 待实现 - break; - } - default: { - // TODO 其它节点类型的实现 - } - } - return list; - } - - private static UserTask convertStartUserNode(BpmSimpleModelNodeVO node) { - return buildBpmnStartUserTask(node); - } - - private static List convertApproveNode(BpmSimpleModelNodeVO node) { - List flowElements = new ArrayList<>(); - UserTask userTask = buildBpmnUserTask(node); - flowElements.add(userTask); - - // 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理 - if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) { - BoundaryEvent boundaryEvent = buildUserTaskTimeoutBoundaryEvent(userTask, node.getTimeoutHandler()); - flowElements.add(boundaryEvent); - } - return flowElements; - } - - /** - * 添加 UserTask 用户的审批超时 BoundaryEvent 事件 - * - * @param userTask 审批任务 - * @param timeoutHandler 超时处理器 - * @return BoundaryEvent 超时事件 - */ - private static BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, TimeoutHandler timeoutHandler) { - // 1.1 定时器边界事件 - BoundaryEvent boundaryEvent = new BoundaryEvent(); - boundaryEvent.setId("Event-" + IdUtil.fastUUID()); - boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断 - boundaryEvent.setAttachedToRef(userTask); - // 1.2 定义超时时间、最大提醒次数 - TimerEventDefinition eventDefinition = new TimerEventDefinition(); - eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration()); - if (Objects.equals(REMINDER.getType(), timeoutHandler.getType()) && - timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) { - eventDefinition.setTimeCycle(String.format("R%d/%s", - timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration())); - } - boundaryEvent.addEventDefinition(eventDefinition); - - // 2.1 添加定时器边界事件类型 - addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventType.USER_TASK_TIMEOUT.getType().toString()); - // 2.2 添加超时执行动作元素 - addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, StrUtil.toStringOrNull(timeoutHandler.getType())); - return boundaryEvent; - } - - private static List convertParallelBranchNode(BpmSimpleModelNodeVO node) { - ParallelGateway parallelGateway = new ParallelGateway(); - parallelGateway.setId(node.getId()); - // TODO @jason:setName - - // TODO @芋艿 + jason:合并网关;是不是要有条件啥的。微信讨论 - // 并行聚合网关有程序创建。前端不需要传入 - ParallelGateway joinParallelGateway = new ParallelGateway(); - joinParallelGateway.setId(node.getId() + JOIN_GATE_WAY_NODE_ID_SUFFIX); - return CollUtil.newArrayList(parallelGateway, joinParallelGateway); - } - - private static ServiceTask convertCopyNode(BpmSimpleModelNodeVO node) { - ServiceTask serviceTask = new ServiceTask(); - serviceTask.setId(node.getId()); - serviceTask.setName(node.getName()); - serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - serviceTask.setImplementation("${" + BpmCopyTaskDelegate.BEAN_NAME + "}"); - - // 添加抄送候选人元素 - addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), serviceTask); - // 添加表单字段权限属性元素 - addFormFieldsPermission(node.getFieldsPermission(), serviceTask); - return serviceTask; - } - - /** - * 给节点添加候选人元素 - */ - private static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) { - addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY, - candidateStrategy == null ? null : candidateStrategy.toString()); - addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, candidateParam); - } - - private static ExclusiveGateway convertConditionBranchNode(BpmSimpleModelNodeVO node) { - Assert.notEmpty(node.getConditionNodes(), "条件分支节点不能为空"); - ExclusiveGateway exclusiveGateway = new ExclusiveGateway(); - exclusiveGateway.setId(node.getId()); - // 寻找默认的序列流 - BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(), - item -> BooleanUtil.isTrue(item.getDefaultFlow())); - if (defaultSeqFlow != null) { - exclusiveGateway.setDefaultFlow(defaultSeqFlow.getId()); - } - return exclusiveGateway; - } - - private static InclusiveGateway convertInclusiveBranchNode(BpmSimpleModelNodeVO node, Boolean isFork) { - InclusiveGateway inclusiveGateway = new InclusiveGateway(); - inclusiveGateway.setId(node.getId()); - // TODO @jason:这里是不是 setName 哈; - - // TODO @芋艿 + jason:是不是搞个合并网关;这里微信讨论下,有点奇怪; - // @芋艿 isFork 为 false 就是合并网关。由前端传入。这个前端暂时还未实现 - if (isFork) { - Assert.notEmpty(node.getConditionNodes(), "条件节点不能为空"); - // 寻找默认的序列流 - BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne( - node.getConditionNodes(), item -> BooleanUtil.isTrue(item.getDefaultFlow())); - if (defaultSeqFlow != null) { - inclusiveGateway.setDefaultFlow(defaultSeqFlow.getId()); - } - } - return inclusiveGateway; - } - - private static UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node) { - UserTask userTask = new UserTask(); - userTask.setId(node.getId()); - userTask.setName(node.getName()); - - // 如果不是审批人节点,则直接返回 - addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, StrUtil.toStringOrNull(node.getApproveType())); - if (ObjectUtil.notEqual(node.getApproveType(), USER.getType())) { - return userTask; - } - - // 添加候选人元素 - addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask); - // 添加表单字段权限属性元素 - addFormFieldsPermission(node.getFieldsPermission(), userTask); - // 添加操作按钮配置属性元素 - addButtonsSetting(node.getButtonsSetting(), userTask); - // 处理多实例(审批方式) - processMultiInstanceLoopCharacteristics(node.getApproveMethod(), node.getApproveRatio(), userTask); - // 添加任务被拒绝的处理元素 - addTaskRejectElements(node.getRejectHandler(), userTask); - // 添加用户任务的审批人与发起人相同时的处理元素 - addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask); - // 添加用户任务的空处理元素 - addAssignEmptyHandlerType(node.getAssignEmptyHandler(), userTask); - // 设置审批任务的截止时间 - if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) { - userTask.setDueDate(node.getTimeoutHandler().getTimeDuration()); - } - return userTask; - } - - private static void addTaskRejectElements(RejectHandler rejectHandler, UserTask userTask) { - if (rejectHandler == null) { - return; - } - addExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE, StrUtil.toStringOrNull(rejectHandler.getType())); - addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId()); - } - - private static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) { - if (assignStartUserHandlerType == null) { - return; - } - addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString()); - } - - private static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { - if (emptyHandler == null) { - return; - } - addExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE, StrUtil.toStringOrNull(emptyHandler.getType())); - addExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS, StrUtil.join(",", emptyHandler.getUserIds())); - } - - private static void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) { - BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod); - // TODO @jason:这种枚举,最终不要去掉哈 BpmUserTaskApproveMethodEnum。因为容易不经意重叠 - if (approveMethodEnum == null || approveMethodEnum == RANDOM) { - return; - } - // 添加审批方式的扩展属性 - addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_METHOD, - approveMethod == null ? null : approveMethod.toString()); - MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); - // 设置 collectionVariable。本系统用不到。仅仅为了 Flowable 校验不报错。 - multiInstanceCharacteristics.setInputDataItem("${coll_userList}"); - if (approveMethodEnum == BpmUserTaskApproveMethodEnum.ANY) { - multiInstanceCharacteristics.setCompletionCondition(ANY_OF_APPROVE_COMPLETE_EXPRESSION); - multiInstanceCharacteristics.setSequential(false); - userTask.setLoopCharacteristics(multiInstanceCharacteristics); - } else if (approveMethodEnum == SEQUENTIAL) { - multiInstanceCharacteristics.setCompletionCondition(ALL_APPROVE_COMPLETE_EXPRESSION); - multiInstanceCharacteristics.setSequential(true); - multiInstanceCharacteristics.setLoopCardinality("1"); - userTask.setLoopCharacteristics(multiInstanceCharacteristics); - } else if (approveMethodEnum == RATIO) { - Assert.notNull(approveRatio, "通过比例不能为空"); - multiInstanceCharacteristics.setCompletionCondition( - String.format(APPROVE_BY_RATIO_COMPLETE_EXPRESSION, String.format("%.2f", approveRatio / (double) 100))); - multiInstanceCharacteristics.setSequential(false); - } - userTask.setLoopCharacteristics(multiInstanceCharacteristics); - } - - /** - * 给节点添加操作按钮设置元素 - */ - private static void addButtonsSetting(List buttonsSetting, UserTask userTask) { - if (CollUtil.isNotEmpty(buttonsSetting)) { - List> list = CollectionUtils.convertList(buttonsSetting, item -> { - Map settingMap = MapUtil.newHashMap(16); - settingMap.put(BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE, String.valueOf(item.getId())); - settingMap.put(BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE, item.getDisplayName()); - settingMap.put(BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE, String.valueOf(item.getEnable())); - return settingMap; - }); - list.forEach(item -> addExtensionElement(userTask, BUTTON_SETTING_ELEMENT, item)); - } - } - - /** - * 给节点添加表单字段权限元素 - */ - private static void addFormFieldsPermission(List> fieldsPermissions, FlowElement flowElement) { - if (CollUtil.isNotEmpty(fieldsPermissions)) { - fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item)); - } - } - - private static void addExtensionElement(FlowElement element, String name, Map attributes) { - if (attributes == null) { - return; - } - ExtensionElement extensionElement = new ExtensionElement(); - extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); - extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); - extensionElement.setName(name); - attributes.forEach((key, value) -> { - ExtensionAttribute extensionAttribute = new ExtensionAttribute(key, value); - extensionAttribute.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); - extensionElement.addAttribute(extensionAttribute); - }); - element.addExtensionElement(extensionElement); - } - - private static void addExtensionElement(FlowElement element, String name, String value) { - if (value == null) { - return; - } - ExtensionElement extensionElement = new ExtensionElement(); - extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); - extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); - extensionElement.setElementText(value); - extensionElement.setName(name); - element.addExtensionElement(extensionElement); - } - - // ========== 各种 build 节点的方法 ========== - - private static StartEvent convertStartNode(BpmSimpleModelNodeVO node) { - StartEvent startEvent = new StartEvent(); - startEvent.setId(node.getId()); - startEvent.setName(node.getName()); - return startEvent; - } - - private static UserTask buildBpmnStartUserTask(BpmSimpleModelNodeVO node) { - UserTask userTask = new UserTask(); - userTask.setId(node.getId()); - userTask.setName(node.getName()); - // 人工审批 - addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, USER.getType().toString()); - // 候选人策略为发起人自己 - addCandidateElements(START_USER.getStrategy(), null, userTask); - // 添加表单字段权限属性元素 - addFormFieldsPermission(node.getFieldsPermission(), userTask); - // 添加操作按钮配置属性元素 - addButtonsSetting(node.getButtonsSetting(), userTask); - // 使用自动通过策略 TODO @芋艿 复用了SKIP, 是否需要新加一个策略;TODO @芋艿:【回复】是不是应该类似飞书,搞个草稿状态。待定;还有一种策略,不标记自动通过,而是首次发起后,第一个节点,自动通过; - addAssignStartUserHandlerType(SKIP.getType(), userTask); - return userTask; - } - - private static EndEvent convertEndNode(BpmSimpleModelNodeVO node) { - EndEvent endEvent = new EndEvent(); - endEvent.setId(node.getId()); - endEvent.setName(node.getName()); - - // TODO @芋艿 + jason:要不要加一个终止定义? - return endEvent; - } - -} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/bo/AlreadyRunApproveNodeRespBO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/bo/AlreadyRunApproveNodeRespBO.java deleted file mode 100644 index 4d92d2e77..000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/bo/AlreadyRunApproveNodeRespBO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.bpm.service.task.bo; - -import lombok.Data; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ApprovalNodeInfo; - -/** - * 已经进行中的审批节点 Response BO - * - * @author jason - */ -@Data -public class AlreadyRunApproveNodeRespBO { - - /** - * 审批节点信息数组 - */ - private List approveNodes; - - /** - * 已运行的节点 ID 数组 (对应 Bpmn XML 节点 id) - */ - private Set runNodeIds; - - /** - * 正在运行的节点的审批信息(key: activityId, value: 审批信息) - *

- * 用于依次审批,需要加上候选人信息 - */ - private Map runningApprovalNodes; - -} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordRespDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordRespDTO.java deleted file mode 100644 index 82fe21257..000000000 --- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/dto/CombinationRecordRespDTO.java +++ /dev/null @@ -1,110 +0,0 @@ -package cn.iocoder.yudao.module.promotion.api.combination.dto; - -import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * 拼团记录 Response DTO - * - * @author 芋道源码 - */ -@Data -public class CombinationRecordRespDTO { - - /** - * 编号,主键自增 - */ - private Long id; - - /** - * 拼团活动编号 - * - * 关联 CombinationActivityDO 的 id 字段 - */ - private Long activityId; - /** - * 拼团商品单价 - * - * 冗余 CombinationProductDO 的 combinationPrice 字段 - */ - private Integer combinationPrice; - /** - * SPU 编号 - */ - private Long spuId; - /** - * 商品名字 - */ - private String spuName; - /** - * 商品图片 - */ - private String picUrl; - /** - * SKU 编号 - */ - private Long skuId; - /** - * 购买的商品数量 - */ - private Integer count; - - /** - * 用户编号 - */ - private Long userId; - - /** - * 用户昵称 - */ - private String nickname; - /** - * 用户头像 - */ - private String avatar; - - /** - * 团长编号 - */ - private Long headId; - /** - * 开团状态 - * - * 关联 {@link CombinationRecordStatusEnum} - */ - private Integer status; - /** - * 订单编号 - */ - private Long orderId; - /** - * 开团需要人数 - * - * 关联 CombinationActivityDO 的 userSize 字段 - */ - private Integer userSize; - /** - * 已加入拼团人数 - */ - private Integer userCount; - /** - * 是否虚拟成团 - */ - private Boolean virtualGroup; - - /** - * 过期时间 - */ - private LocalDateTime expireTime; - /** - * 开始时间 (订单付款后开始的时间) - */ - private LocalDateTime startTime; - /** - * 结束时间(成团时间/失败时间) - */ - private LocalDateTime endTime; - -} diff --git a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/dto/PointValidateJoinRespDTO.java b/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/dto/PointValidateJoinRespDTO.java deleted file mode 100644 index e3b993461..000000000 --- a/yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/point/dto/PointValidateJoinRespDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.promotion.api.point.dto; - -import lombok.Data; - -/** - * 校验参与积分商城 Response DTO - */ -@Data -public class PointValidateJoinRespDTO { - - /** - * 可兑换次数 - */ - private Integer count; - /** - * 所需兑换积分 - */ - private Integer point; - /** - * 所需兑换金额,单位:分 - */ - private Integer price; - -} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java deleted file mode 100644 index d8c1a3e0e..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/point/PointActivityApiImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.promotion.api.point; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO; -import cn.iocoder.yudao.module.promotion.service.point.PointActivityService; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -/** - * 积分商城活动 Api 接口实现类 - * - * @author HUIHUI - */ -@RestController // 提供 RESTful API 接口,给 Feign 调用 -@Validated -public class PointActivityApiImpl implements PointActivityApi { - - @Resource - private PointActivityService pointActivityService; - - @Override - public CommonResult validateJoinPointActivity(Long activityId, Long skuId, Integer count) { - return success(pointActivityService.validateJoinPointActivity(activityId, skuId, count)); - } - - @Override - public CommonResult updatePointStockDecr(Long id, Long skuId, Integer count) { - pointActivityService.updatePointStockDecr(id, skuId, count); - return success(true); - } - - @Override - public CommonResult updatePointStockIncr(Long id, Long skuId, Integer count) { - pointActivityService.updatePointStockIncr(id, skuId, count); - return success(true); - } - -} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/PointActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/PointActivityController.java deleted file mode 100644 index 1a166983c..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/PointActivityController.java +++ /dev/null @@ -1,141 +0,0 @@ -package cn.iocoder.yudao.module.promotion.controller.admin.point; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; -import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityRespVO; -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO; -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductRespVO; -import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO; -import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO; -import cn.iocoder.yudao.module.promotion.service.point.PointActivityService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; - -@Tag(name = "管理后台 - 积分商城活动") -@RestController -@RequestMapping("/promotion/point-activity") -@Validated -public class PointActivityController { - - @Resource - private PointActivityService pointActivityService; - @Resource - private ProductSpuApi productSpuApi; - - @PostMapping("/create") - @Operation(summary = "创建积分商城活动") - @PreAuthorize("@ss.hasPermission('promotion:point-activity:create')") - public CommonResult createPointActivity(@Valid @RequestBody PointActivitySaveReqVO createReqVO) { - return success(pointActivityService.createPointActivity(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新积分商城活动") - @PreAuthorize("@ss.hasPermission('promotion:point-activity:update')") - public CommonResult updatePointActivity(@Valid @RequestBody PointActivitySaveReqVO updateReqVO) { - pointActivityService.updatePointActivity(updateReqVO); - return success(true); - } - - @PutMapping("/close") - @Operation(summary = "关闭积分商城活动") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('promotion:point-activity:close')") - public CommonResult closeSeckillActivity(@RequestParam("id") Long id) { - pointActivityService.closePointActivity(id); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除积分商城活动") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('promotion:point-activity:delete')") - public CommonResult deletePointActivity(@RequestParam("id") Long id) { - pointActivityService.deletePointActivity(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得积分商城活动") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('promotion:point-activity:query')") - public CommonResult getPointActivity(@RequestParam("id") Long id) { - PointActivityDO pointActivity = pointActivityService.getPointActivity(id); - if (pointActivity == null) { - return success(null); - } - - List products = pointActivityService.getPointProductListByActivityIds(Collections.singletonList(id)); - PointActivityRespVO respVO = BeanUtils.toBean(pointActivity, PointActivityRespVO.class); - respVO.setProducts(BeanUtils.toBean(products, PointProductRespVO.class)); - return success(respVO); - } - - @GetMapping("/page") - @Operation(summary = "获得积分商城活动分页") - @PreAuthorize("@ss.hasPermission('promotion:point-activity:query')") - public CommonResult> getPointActivityPage(@Valid PointActivityPageReqVO pageReqVO) { - PageResult pageResult = pointActivityService.getPointActivityPage(pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty(pageResult.getTotal())); - } - - // 拼接数据 - List resultList = buildPointActivityRespVOList(pageResult.getList()); - return success(new PageResult<>(resultList, pageResult.getTotal())); - } - - @GetMapping("/list-by-ids") - @Operation(summary = "获得积分商城活动列表,基于活动编号数组") - @Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]") - public CommonResult> getPointActivityListByIds(@RequestParam("ids") List ids) { - // 1. 获得开启的活动列表 - List activityList = pointActivityService.getPointActivityListByIds(ids); - activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus())); - if (CollUtil.isEmpty(activityList)) { - return success(Collections.emptyList()); - } - // 2. 拼接返回 - List result = buildPointActivityRespVOList(activityList); - return success(result); - } - - private List buildPointActivityRespVOList(List activityList) { - List products = pointActivityService.getPointProductListByActivityIds( - convertSet(activityList, PointActivityDO::getId)); - Map> productsMap = convertMultiMap(products, PointProductDO::getActivityId); - Map spuMap = productSpuApi.getSpusMap( - convertSet(activityList, PointActivityDO::getSpuId)); - List result = BeanUtils.toBean(activityList, PointActivityRespVO.class); - result.forEach(activity -> { - // 设置 product 信息 - PointProductDO minProduct = getMinObject(productsMap.get(activity.getId()), PointProductDO::getPoint); - assert minProduct != null; - activity.setPoint(minProduct.getPoint()).setPrice(minProduct.getPrice()); - findAndThen(spuMap, activity.getSpuId(), - spu -> activity.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice())); - }); - return result; - } - -} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivityRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivityRespVO.java deleted file mode 100644 index d81b3d690..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivityRespVO.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity; - -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductRespVO; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - 积分商城活动 Response VO") -@Data -@ExcelIgnoreUnannotated -public class PointActivityRespVO { - - @Schema(description = "积分商城活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11373") - @ExcelProperty("积分商城活动编号") - private Long id; - - @Schema(description = "积分商城活动商品", requiredMode = Schema.RequiredMode.REQUIRED, example = "19509") - @ExcelProperty("积分商城活动商品") - private Long spuId; - - @Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty("活动状态") - private Integer status; - - @Schema(description = "积分商城活动库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty("积分商城活动库存") - private Integer stock; // 剩余库存积分兑换时扣减 - - @Schema(description = "积分商城活动总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty("积分商城活动总库存") - private Integer totalStock; - - @Schema(description = "备注", example = "你说的对") - @ExcelProperty("备注") - private String remark; - - @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("排序") - private Integer sort; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - - @Schema(description = "积分商城商品", requiredMode = Schema.RequiredMode.REQUIRED) - private List products; - - // ========== 商品字段 ========== - - @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取 - example = "618大促") - private String spuName; - @Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取 - example = "https://www.iocoder.cn/xx.png") - private String picUrl; - @Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取 - example = "50") - private Integer marketPrice; - - //======================= 显示所需兑换积分最少的 sku 信息 ======================= - - @Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED) - private Integer point; - - @Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860") - private Integer price; - -} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivitySaveReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivitySaveReqVO.java deleted file mode 100644 index c81db109a..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/activity/PointActivitySaveReqVO.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity; - -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductSaveReqVO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - 积分商城活动新增/修改 Request VO") -@Data -public class PointActivitySaveReqVO { - - @Schema(description = "积分商城活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11373") - private Long id; - - @Schema(description = "积分商城活动商品", requiredMode = Schema.RequiredMode.REQUIRED, example = "19509") - @NotNull(message = "积分商城活动商品不能为空") - private Long spuId; - - @Schema(description = "备注", example = "你说的对") - private String remark; - - @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "排序不能为空") - private Integer sort; - - @Schema(description = "积分商城商品", requiredMode = Schema.RequiredMode.REQUIRED) - private List products; - -} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/product/PointProductRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/product/PointProductRespVO.java deleted file mode 100644 index 8e8250b38..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/product/PointProductRespVO.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product; - -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 积分商城商品 Response VO") -@Data -@ExcelIgnoreUnannotated -public class PointProductRespVO { - - @Schema(description = "积分商城商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31718") - private Long id; - - @Schema(description = "积分商城活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "29388") - private Long activityId; - - @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8112") - private Long spuId; - - @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2736") - private Long skuId; - - @Schema(description = "可兑换数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3926") - private Integer count; - - @Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED) - private Integer point; - - @Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860") - private Integer price; - - @Schema(description = "积分商城商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private Integer stock; - - @Schema(description = "积分商城商品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer activityStatus; - -} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/product/PointProductSaveReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/product/PointProductSaveReqVO.java deleted file mode 100644 index 9863e73b1..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/point/vo/product/PointProductSaveReqVO.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 积分商城商品新增/修改 Request VO") -@Data -public class PointProductSaveReqVO { - - @Schema(description = "积分商城商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31718") - private Long id; - - @Schema(description = "积分商城活动 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "29388") - @NotNull(message = "积分商城活动 id不能为空") - private Long activityId; - - @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8112") - @NotNull(message = "商品 SPU 编号不能为空") - private Long spuId; - - @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2736") - @NotNull(message = "商品 SKU 编号不能为空") - private Long skuId; - - @Schema(description = "可兑换数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3926") - @NotNull(message = "可兑换数量不能为空") - private Integer count; - - @Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "兑换积分不能为空") - private Integer point; - - @Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860") - @NotNull(message = "兑换金额,单位:分不能为空") - private Integer price; - - @Schema(description = "积分商城商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - @NotNull(message = "积分商城商品不能为空") - private Integer stock; - - @Schema(description = "积分商城商品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @NotNull(message = "积分商城商品状态不能为空") - private Integer activityStatus; - -} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/AppPointActivityController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/AppPointActivityController.java deleted file mode 100644 index 8c2f3e3ce..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/AppPointActivityController.java +++ /dev/null @@ -1,121 +0,0 @@ -package cn.iocoder.yudao.module.promotion.controller.app.point; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; -import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; -import cn.iocoder.yudao.module.promotion.controller.app.point.vo.AppPointActivityDetailRespVO; -import cn.iocoder.yudao.module.promotion.controller.app.point.vo.AppPointActivityPageReqVO; -import cn.iocoder.yudao.module.promotion.controller.app.point.vo.AppPointActivityRespVO; -import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO; -import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO; -import cn.iocoder.yudao.module.promotion.service.point.PointActivityService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import javax.annotation.security.PermitAll; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; - -@Tag(name = "用户 App - 积分商城活动") -@RestController -@RequestMapping("/promotion/point-activity") -@Validated -public class AppPointActivityController { - - @Resource - private PointActivityService pointActivityService; - - @Resource - private ProductSpuApi productSpuApi; - - @GetMapping("/page") - @Operation(summary = "获得积分商城活动分页") - @PermitAll - public CommonResult> getPointActivityPage(AppPointActivityPageReqVO pageReqVO) { - // 1. 查询满足当前阶段的活动 - PageResult pageResult = pointActivityService.getPointActivityPage( - BeanUtils.toBean(pageReqVO, PointActivityPageReqVO.class)); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty(pageResult.getTotal())); - } - - // 2. 拼接数据 - List resultList = buildAppPointActivityRespVOList(pageResult.getList()); - return success(new PageResult<>(resultList, pageResult.getTotal())); - } - - @GetMapping("/get-detail") - @Operation(summary = "获得积分商城活动明细") - @Parameter(name = "id", description = "活动编号", required = true, example = "1024") - @PermitAll - public CommonResult getPointActivity(@RequestParam("id") Long id) { - // 1. 获取活动 - PointActivityDO activity = pointActivityService.getPointActivity(id); - if (activity == null - || ObjUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { - return success(null); - } - - // 2. 拼接数据 - List products = pointActivityService.getPointProductListByActivityIds(Collections.singletonList(id)); - PointProductDO minProduct = getMinObject(products, PointProductDO::getPoint); - assert minProduct != null; - AppPointActivityDetailRespVO respVO = BeanUtils.toBean(activity, AppPointActivityDetailRespVO.class) - .setProducts(BeanUtils.toBean(products, AppPointActivityDetailRespVO.Product.class)) - .setPoint(minProduct.getPoint()).setPrice(minProduct.getPrice()); - return success(respVO); - } - - @GetMapping("/list-by-ids") - @Operation(summary = "获得积分商城活动列表,基于活动编号数组") - @Parameter(name = "ids", description = "活动编号数组", required = true, example = "[1024, 1025]") - @PermitAll - public CommonResult> getCombinationActivityListByIds(@RequestParam("ids") List ids) { - // 1. 获得开启的活动列表 - List activityList = pointActivityService.getPointActivityListByIds(ids); - activityList.removeIf(activity -> CommonStatusEnum.isDisable(activity.getStatus())); - if (CollUtil.isEmpty(activityList)) { - return success(Collections.emptyList()); - } - // 2. 拼接返回 - List result = buildAppPointActivityRespVOList(activityList); - return success(result); - } - - private List buildAppPointActivityRespVOList(List activityList) { - List products = pointActivityService.getPointProductListByActivityIds( - convertSet(activityList, PointActivityDO::getId)); - Map> productsMap = convertMultiMap(products, PointProductDO::getActivityId); - Map spuMap = productSpuApi.getSpusMap( - convertSet(activityList, PointActivityDO::getSpuId)); - List result = BeanUtils.toBean(activityList, AppPointActivityRespVO.class); - result.forEach(activity -> { - // 设置 product 信息 - PointProductDO minProduct = getMinObject(productsMap.get(activity.getId()), PointProductDO::getPoint); - assert minProduct != null; - activity.setPoint(minProduct.getPoint()).setPrice(minProduct.getPrice()); - findAndThen(spuMap, activity.getSpuId(), - spu -> activity.setSpuName(spu.getName()).setPicUrl(spu.getPicUrl()).setMarketPrice(spu.getMarketPrice())); - }); - return result; - } - -} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityDetailRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityDetailRespVO.java deleted file mode 100644 index 2e02f107a..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityDetailRespVO.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.promotion.controller.app.point.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; - -@Schema(description = "用户 App - 积分商城活动的详细 Response VO") -@Data -public class AppPointActivityDetailRespVO { - - @Schema(description = "积分商城活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11373") - private Long id; - - @Schema(description = "积分商城活动商品", requiredMode = Schema.RequiredMode.REQUIRED, example = "19509") - private Long spuId; - - @Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer status; - - @Schema(description = "积分商城活动库存(剩余库存积分兑换时扣减)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer stock; - - @Schema(description = "积分商城活动总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer totalStock; - - @Schema(description = "备注", example = "你说的对") - private String remark; - - @Schema(description = "商品信息数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List products; - - //======================= 显示所需兑换积分最少的 SKU 信息 ======================= - - @Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED) - private Integer point; - - @Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860") - private Integer price; - - @Schema(description = "商品信息") - @Data - public static class Product { - - @Schema(description = "积分商城商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31718") - private Long id; - - @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2736") - private Long skuId; - - @Schema(description = "可兑换数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3926") - private Integer count; - - @Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED) - private Integer point; - - @Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860") - private Integer price; - - @Schema(description = "积分商城商品库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private Integer stock; - - } - -} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityPageReqVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityPageReqVO.java deleted file mode 100644 index 6a4119563..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityPageReqVO.java +++ /dev/null @@ -1,15 +0,0 @@ -package cn.iocoder.yudao.module.promotion.controller.app.point.vo; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -@Schema(description = "用户 App - 积分商城活动分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class AppPointActivityPageReqVO extends PageParam { - -} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityRespVO.java deleted file mode 100644 index 29f4f97c1..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/point/vo/AppPointActivityRespVO.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.promotion.controller.app.point.vo; - -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "用户 App - 积分商城活动 Response VO") -@Data -public class AppPointActivityRespVO { - - @Schema(description = "积分商城活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11373") - @ExcelProperty("积分商城活动编号") - private Long id; - - @Schema(description = "积分商城活动商品", requiredMode = Schema.RequiredMode.REQUIRED, example = "19509") - @ExcelProperty("积分商城活动商品") - private Long spuId; - - @Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty("活动状态") - private Integer status; - - @Schema(description = "积分商城活动库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty("积分商城活动库存") - private Integer stock; // 剩余库存积分兑换时扣减 - - @Schema(description = "积分商城活动总库存", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty("积分商城活动总库存") - private Integer totalStock; - - // TODO @puhui999:只返回必要的字段,例如说 remark、sort、createTime 应该是不需要的呢。也可以看看别的也不需要哈。 - - @Schema(description = "备注", example = "你说的对") - @ExcelProperty("备注") - private String remark; - - @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("排序") - private Integer sort; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - - // ========== 商品字段 ========== - - @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 name 读取 - example = "618大促") - private String spuName; - @Schema(description = "商品主图", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 picUrl 读取 - example = "https://www.iocoder.cn/xx.png") - private String picUrl; - @Schema(description = "商品市场价,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, // 从 SPU 的 marketPrice 读取 - example = "50") - private Integer marketPrice; - - //======================= 显示所需兑换积分最少的 sku 信息 ======================= - - @Schema(description = "兑换积分", requiredMode = Schema.RequiredMode.REQUIRED) - private Integer point; - - @Schema(description = "兑换金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "15860") - private Integer price; - -} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/point/PointActivityDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/point/PointActivityDO.java deleted file mode 100644 index c3345be88..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/point/PointActivityDO.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.module.promotion.dal.dataobject.point; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -/** - * 积分商城活动 DO - * - * @author HUIHUI - */ -@TableName(value = "promotion_point_activity", autoResultMap = true) -@KeySequence("promotion_point_activity_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class PointActivityDO extends BaseDO { - - /** - * 积分商城活动编号 - */ - @TableId - private Long id; - /** - * 积分商城活动商品 - */ - private Long spuId; - /** - * 活动状态 - * - * 枚举 {@link CommonStatusEnum 对应的类} - */ - private Integer status; - /** - * 备注 - */ - private String remark; - /** - * 排序 - */ - private Integer sort; - - /** - * 积分商城活动库存(剩余库存积分兑换时扣减) - */ - private Integer stock; - /** - * 积分商城活动总库存 - */ - private Integer totalStock; - -} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/point/PointProductDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/point/PointProductDO.java deleted file mode 100644 index 041ac5a03..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/point/PointProductDO.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.promotion.dal.dataobject.point; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -/** - * 积分商城商品 DO - * - * @author HUIHUI - */ -@TableName("promotion_point_product") -@KeySequence("promotion_point_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class PointProductDO extends BaseDO { - - /** - * 积分商城商品编号 - */ - @TableId - private Long id; - /** - * 积分商城活动 id - * - * 关联 {@link PointActivityDO#getId()} - */ - private Long activityId; - /** - * 商品 SPU 编号 - */ - private Long spuId; - /** - * 商品 SKU 编号 - */ - private Long skuId; - /** - * 可兑换次数 - */ - private Integer count; - /** - * 所需兑换积分 - */ - private Integer point; - /** - * 所需兑换金额,单位:分 - */ - private Integer price; - /** - * 积分商城商品库存 - */ - private Integer stock; - /** - * 积分商城商品状态 - * - * 枚举 {@link CommonStatusEnum 对应的类} - */ - private Integer activityStatus; - -} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointActivityMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointActivityMapper.java deleted file mode 100644 index d724c32ee..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointActivityMapper.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.promotion.dal.mysql.point; - -import cn.hutool.core.lang.Assert; -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.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; -import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import org.apache.ibatis.annotations.Mapper; - -/** - * 积分商城活动 Mapper - * - * @author HUIHUI - */ -@Mapper -public interface PointActivityMapper extends BaseMapperX { - - default PageResult selectPage(PointActivityPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(PointActivityDO::getSpuId, reqVO.getSpuId()) - .eqIfPresent(PointActivityDO::getStatus, reqVO.getStatus()) - .eqIfPresent(PointActivityDO::getRemark, reqVO.getRemark()) - .eqIfPresent(PointActivityDO::getSort, reqVO.getSort()) - .betweenIfPresent(PointActivityDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(PointActivityDO::getId)); - } - - /** - * 更新活动库存(减少) - * - * @param id 活动编号 - * @param count 扣减的库存数量(正数) - * @return 影响的行数 - */ - default int updateStockDecr(Long id, int count) { - Assert.isTrue(count > 0); - return update(null, new LambdaUpdateWrapper() - .eq(PointActivityDO::getId, id) - .ge(PointActivityDO::getStock, count) - .setSql("stock = stock - " + count)); - } - - /** - * 更新活动库存(增加) - * - * @param id 活动编号 - * @param count 增加的库存数量(正数) - * @return 影响的行数 - */ - default int updateStockIncr(Long id, int count) { - Assert.isTrue(count > 0); - return update(null, new LambdaUpdateWrapper() - .eq(PointActivityDO::getId, id) - .setSql("stock = stock + " + count)); - } - -} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java deleted file mode 100644 index 3c6d469a2..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/point/PointProductMapper.java +++ /dev/null @@ -1,66 +0,0 @@ -package cn.iocoder.yudao.module.promotion.dal.mysql.point; - -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -/** - * 积分商城商品 Mapper - * - * @author HUIHUI - */ -@Mapper -public interface PointProductMapper extends BaseMapperX { - - default List selectListByActivityId(Collection activityIds) { - return selectList(PointProductDO::getActivityId, activityIds); - } - - default List selectListByActivityId(Long activityId) { - return selectList(PointProductDO::getActivityId, activityId); - } - - default void updateByActivityId(PointProductDO pointProductDO) { - update(pointProductDO, new LambdaUpdateWrapper() - .eq(PointProductDO::getActivityId, pointProductDO.getActivityId())); - } - - default PointProductDO selectListByActivityIdAndSkuId(Long activityId, Long skuId) { - return selectOne(PointProductDO::getActivityId, activityId, - PointProductDO::getSkuId, skuId); - } - - /** - * 更新活动库存(减少) - * - * @param id 活动编号 - * @param count 扣减的库存数量(减少库存) - * @return 影响的行数 - */ - default int updateStockDecr(Long id, int count) { - Assert.isTrue(count > 0); - return update(null, new LambdaUpdateWrapper() - .eq(PointProductDO::getId, id) - .ge(PointProductDO::getStock, count) - .setSql("stock = stock - " + count)); - } - - /** - * 更新活动库存(增加) - * - * @param id 活动编号 - * @param count 需要增加的库存(增加库存) - * @return 影响的行数 - */ - default int updateStockIncr(Long id, int count) { - Assert.isTrue(count > 0); - return update(null, new LambdaUpdateWrapper() - .eq(PointProductDO::getId, id) - .setSql("stock = stock + " + count)); - } -} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java deleted file mode 100644 index e27b227c6..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityService.java +++ /dev/null @@ -1,112 +0,0 @@ -package cn.iocoder.yudao.module.promotion.service.point; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO; -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO; -import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO; -import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO; - -import javax.validation.Valid; -import java.util.Collection; -import java.util.List; - -/** - * 积分商城活动 Service 接口 - * - * @author HUIHUI - */ -public interface PointActivityService { - - /** - * 创建积分商城活动 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createPointActivity(@Valid PointActivitySaveReqVO createReqVO); - - /** - * 更新积分商城活动 - * - * @param updateReqVO 更新信息 - */ - void updatePointActivity(@Valid PointActivitySaveReqVO updateReqVO); - - /** - * 更新积分商城商品库存(减少) - * - * @param id 活动编号 - * @param skuId sku 编号 - * @param count 数量(正数) - */ - void updatePointStockDecr(Long id, Long skuId, Integer count); - - /** - * 更新积分商城商品库存(增加) - * - * @param id 活动编号 - * @param skuId sku 编号 - * @param count 数量(正数) - */ - void updatePointStockIncr(Long id, Long skuId, Integer count); - - /** - * 关闭积分商城活动 - * - * @param id 编号 - */ - void closePointActivity(Long id); - - /** - * 删除积分商城活动 - * - * @param id 编号 - */ - void deletePointActivity(Long id); - - /** - * 获得积分商城活动 - * - * @param id 编号 - * @return 积分商城活动 - */ - PointActivityDO getPointActivity(Long id); - - /** - * 获得积分商城活动分页 - * - * @param pageReqVO 分页查询 - * @return 积分商城活动分页 - */ - PageResult getPointActivityPage(PointActivityPageReqVO pageReqVO); - - /** - * 获得积分商城活动列表 - * - * @param ids 活动编号 - * @return 积分商城活动列表 - */ - List getPointActivityListByIds(Collection ids); - - /** - * 获得活动商品 - * - * @param activityIds 活动编号 - * @return 获得活动商品 - */ - List getPointProductListByActivityIds(Collection activityIds); - - /** - * 【下单前】校验是否参与积分商城活动 - * - * 如果校验失败,则抛出业务异常 - * - * @param activityId 活动编号 - * @param skuId SKU 编号 - * @param count 数量 - * @return 积分商城商品信息 - */ - PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count); - -} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java deleted file mode 100644 index e6781c548..000000000 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/point/PointActivityServiceImpl.java +++ /dev/null @@ -1,309 +0,0 @@ -package cn.iocoder.yudao.module.promotion.service.point; - -import cn.hutool.core.util.ObjectUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; -import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; -import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; -import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; -import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO; -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivityPageReqVO; -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.activity.PointActivitySaveReqVO; -import cn.iocoder.yudao.module.promotion.controller.admin.point.vo.product.PointProductSaveReqVO; -import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointActivityDO; -import cn.iocoder.yudao.module.promotion.dal.dataobject.point.PointProductDO; -import cn.iocoder.yudao.module.promotion.dal.mysql.point.PointActivityMapper; -import cn.iocoder.yudao.module.promotion.dal.mysql.point.PointProductMapper; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import javax.annotation.Resource; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static cn.hutool.core.collection.CollUtil.intersectionDistinct; -import static cn.hutool.core.collection.CollUtil.isNotEmpty; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; -import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; -import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; -import static java.util.Collections.singletonList; - -/** - * 积分商城活动 Service 实现类 - * - * @author HUIHUI - */ -@Service -@Validated -public class PointActivityServiceImpl implements PointActivityService { - - @Resource - private PointActivityMapper pointActivityMapper; - @Resource - private PointProductMapper pointProductMapper; - - @Resource - private ProductSpuApi productSpuApi; - @Resource - private ProductSkuApi productSkuApi; - - private static List buildPointProductDO(PointActivityDO pointActivity, List products) { - return BeanUtils.toBean(products, PointProductDO.class, product -> - product.setSpuId(pointActivity.getSpuId()).setActivityId(pointActivity.getId()) - .setActivityStatus(pointActivity.getStatus())); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createPointActivity(PointActivitySaveReqVO createReqVO) { - // 1.1 校验商品是否存在 - validateProductExists(createReqVO.getSpuId(), createReqVO.getProducts()); - // 1.2 校验商品是否已经参加别的活动 - validatePointActivityProductConflicts(null, createReqVO.getProducts()); - - // 2.1 插入积分商城活动 - PointActivityDO pointActivity = BeanUtils.toBean(createReqVO, PointActivityDO.class) - .setStatus(CommonStatusEnum.ENABLE.getStatus()) - .setStock(getSumValue(createReqVO.getProducts(), PointProductSaveReqVO::getStock, Integer::sum)); - pointActivity.setTotalStock(pointActivity.getStock()); - pointActivityMapper.insert(pointActivity); - // 2.2 插入积分商城活动商品 - pointProductMapper.insertBatch(buildPointProductDO(pointActivity, createReqVO.getProducts())); - return pointActivity.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updatePointActivity(PointActivitySaveReqVO updateReqVO) { - // 1.1 校验存在 - PointActivityDO activity = validatePointActivityExists(updateReqVO.getId()); - if (CommonStatusEnum.DISABLE.getStatus().equals(activity.getStatus())) { - throw exception(POINT_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED); - } - // 1.2 校验商品是否存在 - validateProductExists(updateReqVO.getSpuId(), updateReqVO.getProducts()); - // 1.3 校验商品是否已经参加别的活动 - validatePointActivityProductConflicts(updateReqVO.getId(), updateReqVO.getProducts()); - - // 2.1 更新积分商城活动 - PointActivityDO updateObj = BeanUtils.toBean(updateReqVO, PointActivityDO.class) - .setStock(getSumValue(updateReqVO.getProducts(), PointProductSaveReqVO::getStock, Integer::sum)); - if (updateObj.getStock() > activity.getTotalStock()) { // 如果更新的库存大于原来的库存,则更新总库存 - updateObj.setTotalStock(updateObj.getStock()); - } - pointActivityMapper.updateById(updateObj); - // 2.2 更新商品 - updateSeckillProduct(updateObj, updateReqVO.getProducts()); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updatePointStockDecr(Long id, Long skuId, Integer count) { - // 1.1 校验活动库存是否充足 - PointActivityDO activity = validatePointActivityExists(id); - if (count > activity.getStock()) { - throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL); - } - // 1.2 校验商品库存是否充足 - PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(id, skuId); - if (product == null || count > product.getStock()) { - throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL); - } - - // 2.1 更新活动商品库存 - int updateCount = pointProductMapper.updateStockDecr(product.getId(), count); - if (updateCount == 0) { - throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL); - } - - // 2.2 更新活动库存 - updateCount = pointActivityMapper.updateStockDecr(id, count); - if (updateCount == 0) { - throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updatePointStockIncr(Long id, Long skuId, Integer count) { - PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(id, skuId); - // 更新活动商品库存 - pointProductMapper.updateStockIncr(product.getId(), count); - // 更新活动库存 - pointActivityMapper.updateStockIncr(id, count); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void closePointActivity(Long id) { - // 校验存在 - PointActivityDO pointActivity = validatePointActivityExists(id); - if (CommonStatusEnum.DISABLE.getStatus().equals(pointActivity.getStatus())) { - throw exception(POINT_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED); - } - - // 更新 - pointActivityMapper.updateById(new PointActivityDO().setId(id).setStatus(CommonStatusEnum.DISABLE.getStatus())); - // 更新活动商品状态 - pointProductMapper.updateByActivityId(new PointProductDO().setActivityId(id).setActivityStatus( - CommonStatusEnum.DISABLE.getStatus())); - } - - /** - * 更新秒杀商品 - * - * @param activity 秒杀活动 - * @param products 该活动的最新商品配置 - */ - private void updateSeckillProduct(PointActivityDO activity, List products) { - // 第一步,对比新老数据,获得添加、修改、删除的列表 - List newList = buildPointProductDO(activity, products); - List oldList = pointProductMapper.selectListByActivityId(activity.getId()); - List> diffList = diffList(oldList, newList, (oldVal, newVal) -> { - boolean same = ObjectUtil.equal(oldVal.getSkuId(), newVal.getSkuId()); - if (same) { - newVal.setId(oldVal.getId()); - } - return same; - }); - - // 第二步,批量添加、修改、删除 - if (isNotEmpty(diffList.get(0))) { - pointProductMapper.insertBatch(diffList.get(0)); - } - if (isNotEmpty(diffList.get(1))) { - pointProductMapper.updateBatch(diffList.get(1)); - } - if (isNotEmpty(diffList.get(2))) { - pointProductMapper.deleteByIds(convertList(diffList.get(2), PointProductDO::getId)); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deletePointActivity(Long id) { - // 校验存在 - PointActivityDO pointActivity = validatePointActivityExists(id); - if (CommonStatusEnum.ENABLE.getStatus().equals(pointActivity.getStatus())) { - throw exception(POINT_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END); - } - - // 删除商城活动 - pointActivityMapper.deleteById(id); - // 删除活动商品 - List products = pointProductMapper.selectListByActivityId(id); - pointProductMapper.deleteByIds(convertSet(products, PointProductDO::getId)); - } - - private PointActivityDO validatePointActivityExists(Long id) { - PointActivityDO pointActivityDO = pointActivityMapper.selectById(id); - if (pointActivityDO == null) { - throw exception(POINT_ACTIVITY_NOT_EXISTS); - } - return pointActivityDO; - } - - /** - * 校验秒杀商品是否都存在 - * - * @param spuId 商品 SPU 编号 - * @param products 秒杀商品 - */ - private void validateProductExists(Long spuId, List products) { - // 1. 校验商品 spu 是否存在 - ProductSpuRespDTO spu = productSpuApi.getSpu(spuId).getCheckedData(); - if (spu == null) { - throw exception(SPU_NOT_EXISTS); - } - - // 2. 校验商品 sku 都存在 - List skus = productSkuApi.getSkuListBySpuId(singletonList(spuId)).getCheckedData(); - Map skuMap = convertMap(skus, ProductSkuRespDTO::getId); - products.forEach(product -> { - if (!skuMap.containsKey(product.getSkuId())) { - throw exception(SKU_NOT_EXISTS); - } - }); - } - - /** - * 校验商品是否冲突 - * - * @param id 编号 - * @param products 商品列表 - */ - private void validatePointActivityProductConflicts(Long id, List products) { - // 1.1 查询所有开启的积分商城活动 - List activityList = pointActivityMapper.selectList(PointActivityDO::getStatus, - CommonStatusEnum.ENABLE.getStatus()); - if (id != null) { // 更新时排除自己 - activityList.removeIf(item -> ObjectUtil.equal(item.getId(), id)); - } - // 1.2 查询活动下的所有商品 - List productList = pointProductMapper.selectListByActivityId( - convertList(activityList, PointActivityDO::getId)); - Map> productListMap = convertMultiMap(productList, PointProductDO::getActivityId); - - // 2. 校验商品是否冲突 - activityList.forEach(item -> { - findAndThen(productListMap, item.getId(), discountProducts -> { - if (!intersectionDistinct(convertList(discountProducts, PointProductDO::getSpuId), - convertList(products, PointProductSaveReqVO::getSpuId)).isEmpty()) { - throw exception(POINT_ACTIVITY_SPU_CONFLICTS); - } - }); - }); - } - - @Override - public PointActivityDO getPointActivity(Long id) { - return pointActivityMapper.selectById(id); - } - - @Override - public PageResult getPointActivityPage(PointActivityPageReqVO pageReqVO) { - return pointActivityMapper.selectPage(pageReqVO); - } - - @Override - public List getPointActivityListByIds(Collection ids) { - return pointActivityMapper.selectList(PointActivityDO::getId, ids); - } - - @Override - public List getPointProductListByActivityIds(Collection activityIds) { - return pointProductMapper.selectListByActivityId(activityIds); - } - - @Override - public PointValidateJoinRespDTO validateJoinPointActivity(Long activityId, Long skuId, Integer count) { - // 1. 校验积分商城活动是否存在 - PointActivityDO activity = validatePointActivityExists(activityId); - if (CommonStatusEnum.isDisable(activity.getStatus())) { - throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_STATUS_CLOSED); - } - - // 2.1 校验积分商城商品是否存在 - PointProductDO product = pointProductMapper.selectListByActivityIdAndSkuId(activityId, skuId); - if (product == null) { - throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS); - } - // 2.2 超过单次购买限制 - if (count > product.getCount()) { - throw exception(POINT_ACTIVITY_JOIN_ACTIVITY_SINGLE_LIMIT_COUNT_EXCEED); - } - // 2.2 校验库存是否充足 - if (count > product.getStock()) { - throw exception(POINT_ACTIVITY_UPDATE_STOCK_FAIL); - } - return BeanUtils.toBean(product, PointValidateJoinRespDTO.class); - } - -} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java deleted file mode 100644 index f61b432fe..000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeProductSettlementRespVO.java +++ /dev/null @@ -1,81 +0,0 @@ -package cn.iocoder.yudao.module.trade.controller.app.order.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.io.Serializable; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -@Schema(description = "用户 App - 商品结算信息 Response VO") -@Data -public class AppTradeProductSettlementRespVO { - - @Schema(description = "SPU 商品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long spuId; - - @Schema(description = "SKU 价格信息数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private List skus; - - @Schema(description = "满减送活动信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private RewardActivity rewardActivity; - - @Schema(description = "SKU 价格信息") - @Data - public static class Sku implements Serializable { - - @Schema(description = "商品 SKU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "优惠后价格,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private Integer promotionPrice; - - @Schema(description = "营销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "4") - private Integer promotionType; // 对应 PromotionTypeEnum 枚举,目前只有 4 和 6 两种 - - @Schema(description = "营销编号", requiredMode = Schema.RequiredMode.REQUIRED) - private Long promotionId; // 目前只有限时折扣活动的编号 - - @Schema(description = "活动结束时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime promotionEndTime; - - } - - @Schema(description = "满减送活动信息") - @Data - public static class RewardActivity { - - @Schema(description = "满减活动编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "条件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer conditionType; - - @Schema(description = "优惠规则的数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List rules; - - } - - @Schema(description = "优惠规则") - @Data - public static class RewardActivityRule { - - @Schema(description = "优惠门槛", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") // 1. 满 N 元,单位:分; 2. 满 N 件 - private Integer limit; - - @Schema(description = "优惠价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private Integer discountPrice; - - @Schema(description = "是否包邮", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - private Boolean freeDelivery; - - @Schema(description = "赠送的积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private Integer point; - - @Schema(description = "赠送的优惠劵编号的数组") - private Map giveCouponTemplateCounts; - - } - -} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java deleted file mode 100644 index b38d8bbe3..000000000 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointActivityPriceCalculator.java +++ /dev/null @@ -1,95 +0,0 @@ -package cn.iocoder.yudao.module.trade.service.price.calculator; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.member.api.user.MemberUserApi; -import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; -import cn.iocoder.yudao.module.promotion.api.point.PointActivityApi; -import cn.iocoder.yudao.module.promotion.api.point.dto.PointValidateJoinRespDTO; -import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum; -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; -import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; -import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; -import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT; - -/** - * 积分商城的 {@link TradePriceCalculator} 实现类 - * - * @author owen - */ -@Component -@Order(TradePriceCalculator.ORDER_POINT_ACTIVITY) -@Slf4j -public class TradePointActivityPriceCalculator implements TradePriceCalculator { - - @Resource - private PointActivityApi pointActivityApi; - @Resource - private MemberUserApi memberUserApi; - - @Resource - private TradeOrderQueryService tradeOrderQueryService; - - @Override - public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { - // 1.1 判断订单类型是否为积分商城活动 - if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.POINT.getType())) { - return; - } - // 1.2 初始化积分 - MemberUserRespDTO user = memberUserApi.getUser(param.getUserId()).getCheckedData(); - result.setTotalPoint(user.getPoint()).setUsePoint(0); - - // 1.3 校验用户积分余额 - if (user.getPoint() == null || user.getPoint() <= 0) { - return; - } - - Assert.isTrue(param.getItems().size() == 1, "积分商城兑换商品时,只允许选择一个商品"); - // 2. 校验是否可以参与积分商城活动 - TradePriceCalculateRespBO.OrderItem orderItem = result.getItems().get(0); - PointValidateJoinRespDTO activity = validateJoinSeckill( - param.getUserId(), param.getPointActivityId(), - orderItem.getSkuId(), orderItem.getCount()); - - // 3.1 记录优惠明细 - int discountPrice = orderItem.getPayPrice(); // 情况一:单使用积分兑换 - Assert.isTrue(activity.getPoint() >= 1, "积分商城商品兑换积分必须大于 1"); - result.setUsePoint(activity.getPoint() * orderItem.getCount()); - orderItem.setUsePoint(activity.getPoint() * orderItem.getCount()); - if (activity.getPrice() != null && activity.getPrice() > 0) { // 情况二:积分 + 金额 - discountPrice = orderItem.getPayPrice() - activity.getPrice() * orderItem.getCount(); - } - // 3.2 记录优惠明细 - TradePriceCalculatorHelper.addPromotion(result, orderItem, - param.getPointActivityId(), "积分商城活动", PromotionTypeEnum.POINT.getType(), - StrUtil.format("积分商城活动:省 {} 元", TradePriceCalculatorHelper.formatPrice(discountPrice)), - discountPrice); - - // 3.3 更新 SKU 优惠金额 - orderItem.setDiscountPrice(orderItem.getDiscountPrice() + discountPrice); - TradePriceCalculatorHelper.recountPayPrice(orderItem); - TradePriceCalculatorHelper.recountAllPrice(result); - } - - private PointValidateJoinRespDTO validateJoinSeckill(Long userId, Long activityId, Long skuId, Integer count) { - // 1. 校验是否可以参与积分商城活动 - PointValidateJoinRespDTO pointValidateJoinRespDTO = pointActivityApi.validateJoinPointActivity(activityId, skuId, count).getCheckedData(); - // 2. 校验总限购数量,目前只有 trade 有具体下单的数据,需要交给 trade 价格计算使用 - int activityProductCount = tradeOrderQueryService.getSeckillProductCount(userId, activityId); - if (activityProductCount + count > pointValidateJoinRespDTO.getCount()) { - throw exception(PRICE_CALCULATE_POINT_TOTAL_LIMIT_COUNT); - } - return pointValidateJoinRespDTO; - } - -} diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/wallet/PayWalletUpdateBalanceReqVO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/wallet/PayWalletUpdateBalanceReqVO.java deleted file mode 100644 index 3fb7eae74..000000000 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/vo/wallet/PayWalletUpdateBalanceReqVO.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.pay.controller.admin.wallet.vo.wallet; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import javax.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 修改钱包余额 Request VO") -@Data -public class PayWalletUpdateBalanceReqVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23788") - @NotNull(message = "用户编号不能为空") - private Long userId; - - @Schema(description = "变动余额,正数为增加,负数为减少", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - @NotNull(message = "变动余额不能为空") - private Integer balance; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java deleted file mode 100644 index ca7fe33f8..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java +++ /dev/null @@ -1,127 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import com.google.common.collect.Lists; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.MockedStatic; - -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mockStatic; - -/** - * {@link HuaweiSmsClient} 的单元测试 - * - * @author scholar - */ -public class HuaweiSmsClientTest extends BaseMockitoUnitTest { - - private final SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错 - .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 - .setSignature("芋道源码"); - - @InjectMocks - private HuaweiSmsClient smsClient = new HuaweiSmsClient(properties); - - @Test - public void testDoSendSms_success() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString() + " " + randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"result\":[{\"originTo\":\"+86155****5678\",\"createTime\":\"2018-05-25T16:34:34Z\",\"from\":\"1069********0012\",\"smsMsgId\":\"d6e3cdd0-522b-4692-8304-a07553cdf591_8539659\",\"status\":\"000000\",\"countryId\":\"CN\",\"total\":2}],\"code\":\"000000\",\"description\":\"Success\"}\n"); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertTrue(result.getSuccess()); - assertEquals("d6e3cdd0-522b-4692-8304-a07553cdf591_8539659", result.getSerialNo()); - assertEquals("000000", result.getApiCode()); - } - } - - @Test - public void testDoSendSms_fail_01() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString() + " " + randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"result\":[{\"total\":1,\"originTo\":\"17321315478\",\"createTime\":\"2024-08-18T11:32:20Z\",\"from\":\"x8824060312575\",\"smsMsgId\":\"06e4b966-ad87-479f-8b74-f57fb7aafb60_304613461\",\"countryId\":\"CN\",\"status\":\"E200033\"}],\"code\":\"E000510\",\"description\":\"The SMS fails to be sent. For details, see status.\"}"); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertFalse(result.getSuccess()); - assertEquals("06e4b966-ad87-479f-8b74-f57fb7aafb60_304613461", result.getSerialNo()); - assertEquals("E200033", result.getApiCode()); - } - } - - @Test - public void testDoSendSms_fail_02() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString() + " " + randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"code\":\"E000102\",\"description\":\"Invalid app_key.\"}"); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertFalse(result.getSuccess()); - assertEquals("E000102", result.getApiCode()); - assertEquals("Invalid app_key.", result.getApiMsg()); - } - } - - @Test - public void testParseSmsReceiveStatus() { - // 准备参数 - String text = "sequence=1&total=1&statusDesc=%E7%94%A8%E6%88%B7%E5%B7%B2%E6%88%90%E5%8A%9F%E6%94%B6%E5%88%B0%E7%9F%AD%E4%BF%A1&updateTime=2024-08-15T03%3A00%3A34Z&source=2&smsMsgId=70207ed7-1d02-41b0-8537-bb25fd1c2364_143684459&status=DELIVRD&extend=176"; - - // 调用 - List statuses = smsClient.parseSmsReceiveStatus(text); - // 断言 - assertEquals(1, statuses.size()); - SmsReceiveRespDTO status = statuses.get(0); - assertTrue(status.getSuccess()); - assertEquals("DELIVRD", status.getErrorCode()); - assertEquals(LocalDateTime.of(2024, 8, 15, 3, 0, 34), status.getReceiveTime()); - assertEquals("70207ed7-1d02-41b0-8537-bb25fd1c2364_143684459", status.getSerialNo()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java deleted file mode 100644 index 29ce1e3fb..000000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java +++ /dev/null @@ -1,131 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import com.google.common.collect.Lists; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.MockedStatic; - -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mockStatic; - -/** - * {@link QiniuSmsClient} 的单元测试 - * - * @author scholar - */ -public class QiniuSmsClientTest extends BaseMockitoUnitTest { - - private final SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(randomString())// 随机一个 apiKey,避免构建报错 - .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 - .setSignature("芋道源码"); - - @InjectMocks - private QiniuSmsClient smsClient = new QiniuSmsClient(properties); - - @Test - public void testDoSendSms_success() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString() + " " + randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"message_id\":\"17245678901\"}"); - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertTrue(result.getSuccess()); - assertEquals("17245678901", result.getSerialNo()); - } - } - - @Test - public void testDoSendSms_fail() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString() + " " + randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"error\":\"BadToken\",\"message\":\"Your authorization token is invalid\",\"request_id\":\"etziWcJFo1C8Ne8X\"}"); - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertFalse(result.getSuccess()); - assertEquals("BadToken", result.getApiCode()); - assertEquals("Your authorization token is invalid", result.getApiMsg()); - assertEquals("etziWcJFo1C8Ne8X", result.getApiRequestId()); - } - } - - @Test - public void testGetSmsTemplate() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - String apiTemplateId = randomString(); - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.get(anyString(), anyMap())) - .thenReturn("{\"audit_status\":\"passed\",\"created_at\":1724231187,\"description\":\"\",\"disable_broadcast\":false,\"disable_broadcast_reason\":\"\",\"disable_reason\":\"\",\"disabled\":false,\"id\":\"1826184073773596672\",\"is_oversea\":false,\"name\":\"dd\",\"parameters\":[\"code\"],\"reject_reason\":\"\",\"signature_id\":\"1826099896017498112\",\"signature_text\":\"yudao\",\"template\":\"您的验证码为:${code}\",\"type\":\"verification\",\"uid\":1383022432,\"updated_at\":1724288561,\"variable_count\":0}"); - // 调用 - SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId); - // 断言 - assertEquals("1826184073773596672", result.getId()); - assertEquals("您的验证码为:${code}", result.getContent()); - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); - assertEquals("", result.getAuditReason()); - } - } - - @Test - public void testParseSmsReceiveStatus() { - // 准备参数 - String text = "{\"items\":[{\"mobile\":\"18881234567\",\"message_id\":\"10135515063508004167\",\"status\":\"DELIVRD\",\"delivrd_at\":1724591666,\"error\":\"DELIVRD\",\"seq\":\"123\"}]}"; - // 调用 - List statuses = smsClient.parseSmsReceiveStatus(text); - // 断言 - assertEquals(1, statuses.size()); - SmsReceiveRespDTO status = statuses.get(0); - assertTrue(status.getSuccess()); - assertEquals("DELIVRD", status.getErrorMsg()); - assertEquals(LocalDateTime.of(2024, 8, 25, 21, 14, 26), status.getReceiveTime()); - assertEquals("18881234567", status.getMobile()); - assertEquals("10135515063508004167", status.getSerialNo()); - assertEquals(123, status.getLogId()); - } - - @Test - public void testConvertSmsTemplateAuditStatus() { - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), - smsClient.convertSmsTemplateAuditStatus("passed")); - assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), - smsClient.convertSmsTemplateAuditStatus("reviewing")); - assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), - smsClient.convertSmsTemplateAuditStatus("rejected")); - assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus("unknown"), - "未知审核状态(3)"); - } -} \ No newline at end of file