Commit 28f547915db96993795ee18dc8e510fe3d6d9ca3
1 parent
ca36970e
feat(bpm): 支持流程评论接口
- 新增流程评论创建、列表查询接口 - 封装 BpmCommentService 统一复用 Flowable Comment - 评论列表返回评论人和对应任务信息 - 新增 BPM 评论类型字典与评论枚举 - 补充任务校验与历史任务 Map 查询能力
Showing
10 changed files
with
314 additions
and
29 deletions
sql/mysql/ruoyi-vue-pro.sql
| ... | ... | @@ -462,7 +462,7 @@ CREATE TABLE `system_dict_data` ( |
| 462 | 462 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 463 | 463 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 464 | 464 | PRIMARY KEY (`id`) USING BTREE |
| 465 | -) ENGINE = InnoDB AUTO_INCREMENT = 1061137 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; | |
| 465 | +) ENGINE = InnoDB AUTO_INCREMENT = 1061147 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; | |
| 466 | 466 | |
| 467 | 467 | -- ---------------------------- |
| 468 | 468 | -- Records of system_dict_data |
| ... | ... | @@ -814,6 +814,16 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st |
| 814 | 814 | INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1512, 6, '委派中', '6', 'bpm_task_status', 0, 'primary', '', '', '1', '2024-03-17 10:06:22', '1', '2024-03-08 22:41:40', b'0'); |
| 815 | 815 | INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1513, 7, '审批通过中', '7', 'bpm_task_status', 0, 'success', '', '', '1', '2024-03-17 10:06:47', '1', '2024-03-08 22:41:41', b'0'); |
| 816 | 816 | INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1514, 0, '待审批', '0', 'bpm_task_status', 0, 'info', '', '', '1', '2024-03-17 10:07:11', '1', '2024-03-08 22:41:42', b'0'); |
| 817 | +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061137, 0, '评论', '0', 'bpm_comment_type', 0, 'primary', '', '流程评论类型 - 评论', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0'); | |
| 818 | +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061138, 1, '审批通过', '1', 'bpm_comment_type', 0, 'success', '', '流程评论类型 - 审批通过', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0'); | |
| 819 | +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061139, 2, '不通过', '2', 'bpm_comment_type', 0, 'danger', '', '流程评论类型 - 不通过', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0'); | |
| 820 | +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061140, 3, '已取消', '3', 'bpm_comment_type', 0, 'info', '', '流程评论类型 - 已取消', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0'); | |
| 821 | +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061141, 4, '退回', '4', 'bpm_comment_type', 0, 'warning', '', '流程评论类型 - 退回', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0'); | |
| 822 | +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061142, 5, '委派发起', '5', 'bpm_comment_type', 0, 'primary', '', '流程评论类型 - 委派发起', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0'); | |
| 823 | +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061143, 6, '委派完成', '6', 'bpm_comment_type', 0, 'primary', '', '流程评论类型 - 委派完成', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0'); | |
| 824 | +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061144, 7, '转派', '7', 'bpm_comment_type', 0, 'warning', '', '流程评论类型 - 转派', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0'); | |
| 825 | +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061145, 8, '加签', '8', 'bpm_comment_type', 0, 'primary', '', '流程评论类型 - 加签', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0'); | |
| 826 | +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1061146, 9, '减签', '9', 'bpm_comment_type', 0, 'info', '', '流程评论类型 - 减签', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0'); | |
| 817 | 827 | INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1515, 35, '发起人自选', '35', 'bpm_task_candidate_strategy', 0, '', '', '', '1', '2024-03-22 19:45:16', '1', '2024-03-22 19:45:16', b'0'); |
| 818 | 828 | INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1516, 1, '执行监听器', 'execution', 'bpm_process_listener_type', 0, 'primary', '', '', '1', '2024-03-23 12:54:03', '1', '2024-03-23 19:14:19', b'0'); |
| 819 | 829 | INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1517, 1, '任务监听器', 'task', 'bpm_process_listener_type', 0, 'success', '', '', '1', '2024-03-23 12:54:13', '1', '2024-03-23 19:14:24', b'0'); |
| ... | ... | @@ -1513,7 +1523,7 @@ CREATE TABLE `system_dict_type` ( |
| 1513 | 1523 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 1514 | 1524 | `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间', |
| 1515 | 1525 | PRIMARY KEY (`id`) USING BTREE |
| 1516 | -) ENGINE = InnoDB AUTO_INCREMENT = 1061099 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; | |
| 1526 | +) ENGINE = InnoDB AUTO_INCREMENT = 1061100 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; | |
| 1517 | 1527 | |
| 1518 | 1528 | -- ---------------------------- |
| 1519 | 1529 | -- Records of system_dict_type |
| ... | ... | @@ -1726,6 +1736,7 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat |
| 1726 | 1736 | INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061096, 'IM 通话参与角色', 'im_rtc_participant_role', 0, '1=发起人;2=被邀请者;3=主动加入者', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', b'0', NULL); |
| 1727 | 1737 | INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061097, 'IM 通话参与状态', 'im_rtc_participant_status', 0, '10=邀请中;20=已加入;30=已拒绝;40=未应答;50=已离开', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', b'0', NULL); |
| 1728 | 1738 | INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061098, 'IM 频道素材内容类型', 'im_channel_material_type', 0, '1=站内富文本 / 2=外链', '1', '2026-05-19 14:09:25', '1', '2026-05-19 14:09:25', b'0', NULL); |
| 1739 | +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061099, '流程评论类型', 'bpm_comment_type', 0, 'Flowable 评论类型', '1', '2026-07-02 09:20:00', '1', '2026-07-02 09:20:00', b'0', NULL); | |
| 1729 | 1740 | COMMIT; |
| 1730 | 1741 | |
| 1731 | 1742 | -- ---------------------------- | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/comment/BpmCommentController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.controller.admin.comment; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.yudao.framework.common.util.date.DateUtils; | |
| 6 | +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | |
| 7 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 8 | +import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; | |
| 9 | +import cn.iocoder.yudao.module.bpm.controller.admin.comment.vo.BpmCommentCreateReqVO; | |
| 10 | +import cn.iocoder.yudao.module.bpm.controller.admin.comment.vo.BpmCommentRespVO; | |
| 11 | +import cn.iocoder.yudao.module.bpm.service.comment.BpmCommentService; | |
| 12 | +import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; | |
| 13 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 14 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 15 | +import io.swagger.v3.oas.annotations.Operation; | |
| 16 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 17 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 18 | +import jakarta.annotation.Resource; | |
| 19 | +import jakarta.validation.Valid; | |
| 20 | +import org.flowable.engine.task.Comment; | |
| 21 | +import org.flowable.task.api.history.HistoricTaskInstance; | |
| 22 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 23 | +import org.springframework.validation.annotation.Validated; | |
| 24 | +import org.springframework.web.bind.annotation.*; | |
| 25 | + | |
| 26 | +import java.util.Collections; | |
| 27 | +import java.util.List; | |
| 28 | +import java.util.Map; | |
| 29 | + | |
| 30 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 31 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 32 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 33 | + | |
| 34 | +@Tag(name = "管理后台 - 流程评论") | |
| 35 | +@RestController | |
| 36 | +@RequestMapping("/bpm/comment") | |
| 37 | +@Validated | |
| 38 | +public class BpmCommentController { | |
| 39 | + | |
| 40 | + @Resource | |
| 41 | + private BpmCommentService commentService; | |
| 42 | + @Resource | |
| 43 | + private BpmTaskService taskService; | |
| 44 | + | |
| 45 | + @Resource | |
| 46 | + private AdminUserApi adminUserApi; | |
| 47 | + | |
| 48 | + @GetMapping("/list-by-process-instance-id") | |
| 49 | + @Operation(summary = "获得指定流程实例的评论列表") | |
| 50 | + @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true) | |
| 51 | + @PreAuthorize("@ss.hasPermission('bpm:task:query')") | |
| 52 | + public CommonResult<List<BpmCommentRespVO>> getCommentListByProcessInstanceId( | |
| 53 | + @RequestParam("processInstanceId") String processInstanceId) { | |
| 54 | + // 获得评论列表 | |
| 55 | + List<Comment> commentList = commentService.getCommentListByProcessInstanceId(processInstanceId); | |
| 56 | + if (CollUtil.isEmpty(commentList)) { | |
| 57 | + return success(Collections.emptyList()); | |
| 58 | + } | |
| 59 | + | |
| 60 | + // 拼接 VO | |
| 61 | + Map<String, HistoricTaskInstance> taskMap = taskService.getHistoricTaskMap( | |
| 62 | + convertSet(commentList, Comment::getTaskId)); | |
| 63 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | |
| 64 | + convertSet(commentList, comment -> NumberUtils.parseLong(comment.getUserId()))); | |
| 65 | + return success(convertList(commentList, comment -> BeanUtils.toBean(comment, BpmCommentRespVO.class, commentVO -> { | |
| 66 | + commentVO.setMessage(comment.getFullMessage()) | |
| 67 | + .setCreateTime(DateUtils.of(comment.getTime())) | |
| 68 | + .setTask(BeanUtils.toBean(taskMap.get(comment.getTaskId()), BpmCommentRespVO.Task.class)) | |
| 69 | + .setUser(BeanUtils.toBean(userMap.get(NumberUtils.parseLong(comment.getUserId())), UserSimpleBaseVO.class)); | |
| 70 | + }))); | |
| 71 | + } | |
| 72 | + | |
| 73 | + @PostMapping("/create") | |
| 74 | + @Operation(summary = "创建流程评论") | |
| 75 | + @PreAuthorize("@ss.hasPermission('bpm:task:update')") | |
| 76 | + public CommonResult<Boolean> createComment(@Valid @RequestBody BpmCommentCreateReqVO reqVO) { | |
| 77 | + commentService.createComment(reqVO); | |
| 78 | + return success(true); | |
| 79 | + } | |
| 80 | + | |
| 81 | +} | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/comment/vo/BpmCommentCreateReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.controller.admin.comment.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import jakarta.validation.constraints.NotBlank; | |
| 5 | +import jakarta.validation.constraints.NotEmpty; | |
| 6 | +import jakarta.validation.constraints.Size; | |
| 7 | +import lombok.Data; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - 流程评论创建 Request VO") | |
| 10 | +@Data | |
| 11 | +public class BpmCommentCreateReqVO { | |
| 12 | + | |
| 13 | + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") | |
| 14 | + @NotEmpty(message = "任务编号不能为空") | |
| 15 | + private String taskId; | |
| 16 | + | |
| 17 | + @Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "请关注报销发票附件") | |
| 18 | + @NotBlank(message = "评论内容不能为空") | |
| 19 | + @Size(max = 500, message = "评论内容不能超过 500 个字符") | |
| 20 | + private String message; | |
| 21 | + | |
| 22 | +} | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/comment/vo/BpmCommentRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.controller.admin.comment.vo; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | + | |
| 9 | +@Schema(description = "管理后台 - 流程评论 Response VO") | |
| 10 | +@Data | |
| 11 | +public class BpmCommentRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "评论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | |
| 14 | + private String id; | |
| 15 | + | |
| 16 | + @Schema(description = "任务编号", example = "2048") | |
| 17 | + private String taskId; | |
| 18 | + | |
| 19 | + @Schema(description = "任务") | |
| 20 | + private Task task; | |
| 21 | + | |
| 22 | + @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096") | |
| 23 | + private String processInstanceId; | |
| 24 | + | |
| 25 | + @Schema(description = "评论类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 26 | + private String type; | |
| 27 | + | |
| 28 | + @Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "请关注报销发票附件") | |
| 29 | + private String message; | |
| 30 | + | |
| 31 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 32 | + private LocalDateTime createTime; | |
| 33 | + | |
| 34 | + @Schema(description = "创建人") | |
| 35 | + private UserSimpleBaseVO user; | |
| 36 | + | |
| 37 | + @Schema(description = "任务") | |
| 38 | + @Data | |
| 39 | + public static class Task { | |
| 40 | + | |
| 41 | + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") | |
| 42 | + private String id; | |
| 43 | + | |
| 44 | + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "经理审批") | |
| 45 | + private String name; | |
| 46 | + | |
| 47 | + @Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one") | |
| 48 | + private String taskDefinitionKey; | |
| 49 | + | |
| 50 | + } | |
| 51 | + | |
| 52 | +} | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/DictTypeConstants.java
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java
| ... | ... | @@ -13,6 +13,7 @@ import lombok.Getter; |
| 13 | 13 | @AllArgsConstructor |
| 14 | 14 | public enum BpmCommentTypeEnum { |
| 15 | 15 | |
| 16 | + COMMENT("0", "评论", "{}"), | |
| 16 | 17 | APPROVE("1", "审批通过", "审批通过,原因是:{}"), |
| 17 | 18 | REJECT("2", "不通过", "审批不通过:原因是:{}"), |
| 18 | 19 | CANCEL("3", "已取消", "系统自动取消,原因是:{}"), |
| ... | ... | @@ -40,7 +41,7 @@ public enum BpmCommentTypeEnum { |
| 40 | 41 | private final String comment; |
| 41 | 42 | |
| 42 | 43 | public String formatComment(Object... params) { |
| 43 | - return StrUtil.format(comment, params); | |
| 44 | + return StrUtil.format(comment, params); | |
| 44 | 45 | } |
| 45 | 46 | |
| 46 | 47 | } | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/comment/BpmCommentService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.service.comment; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.bpm.controller.admin.comment.vo.BpmCommentCreateReqVO; | |
| 4 | +import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; | |
| 5 | +import jakarta.validation.Valid; | |
| 6 | +import org.flowable.engine.task.Comment; | |
| 7 | + | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 流程评论 Service 接口 | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +public interface BpmCommentService { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 获得指定流程实例的评论列表 | |
| 19 | + * | |
| 20 | + * @param processInstanceId 流程实例的编号 | |
| 21 | + * @return 评论列表 | |
| 22 | + */ | |
| 23 | + List<Comment> getCommentListByProcessInstanceId(String processInstanceId); | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 创建流程评论 | |
| 27 | + * | |
| 28 | + * @param reqVO 评论请求 | |
| 29 | + */ | |
| 30 | + void createComment(@Valid BpmCommentCreateReqVO reqVO); | |
| 31 | + | |
| 32 | + /** | |
| 33 | + * 创建流程评论 | |
| 34 | + * | |
| 35 | + * @param taskId 任务编号 | |
| 36 | + * @param processInstanceId 流程实例编号 | |
| 37 | + * @param type 评论类型 | |
| 38 | + * @param params 评论模板参数 | |
| 39 | + */ | |
| 40 | + void createComment(String taskId, String processInstanceId, BpmCommentTypeEnum type, Object... params); | |
| 41 | + | |
| 42 | +} | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/comment/BpmCommentServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.service.comment; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.bpm.controller.admin.comment.vo.BpmCommentCreateReqVO; | |
| 4 | +import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; | |
| 5 | +import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; | |
| 6 | +import jakarta.annotation.Resource; | |
| 7 | +import jakarta.validation.Valid; | |
| 8 | +import org.flowable.engine.TaskService; | |
| 9 | +import org.flowable.engine.task.Comment; | |
| 10 | +import org.flowable.task.api.Task; | |
| 11 | +import org.springframework.context.annotation.Lazy; | |
| 12 | +import org.springframework.stereotype.Service; | |
| 13 | +import org.springframework.transaction.annotation.Transactional; | |
| 14 | + | |
| 15 | +import java.util.List; | |
| 16 | + | |
| 17 | +/** | |
| 18 | + * 流程评论 Service 实现类 | |
| 19 | + * | |
| 20 | + * @author 芋道源码 | |
| 21 | + */ | |
| 22 | +@Service | |
| 23 | +public class BpmCommentServiceImpl implements BpmCommentService { | |
| 24 | + | |
| 25 | + @Resource | |
| 26 | + private TaskService taskService; | |
| 27 | + @Resource | |
| 28 | + @Lazy // 延迟加载,避免循环依赖 | |
| 29 | + private BpmTaskService bpmTaskService; | |
| 30 | + | |
| 31 | + @Override | |
| 32 | + public List<Comment> getCommentListByProcessInstanceId(String processInstanceId) { | |
| 33 | + return taskService.getProcessInstanceComments(processInstanceId); | |
| 34 | + } | |
| 35 | + | |
| 36 | + @Override | |
| 37 | + @Transactional(rollbackFor = Exception.class) | |
| 38 | + public void createComment(@Valid BpmCommentCreateReqVO reqVO) { | |
| 39 | + Task task = bpmTaskService.validateTaskExists(reqVO.getTaskId()); | |
| 40 | + createComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.COMMENT, reqVO.getMessage()); | |
| 41 | + } | |
| 42 | + | |
| 43 | + @Override | |
| 44 | + public void createComment(String taskId, String processInstanceId, BpmCommentTypeEnum type, Object... params) { | |
| 45 | + taskService.addComment(taskId, processInstanceId, type.getType(), type.formatComment(params)); | |
| 46 | + } | |
| 47 | + | |
| 48 | +} | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
| ... | ... | @@ -104,6 +104,14 @@ public interface BpmTaskService { |
| 104 | 104 | Task validateTask(Long userId, String taskId); |
| 105 | 105 | |
| 106 | 106 | /** |
| 107 | + * 校验任务是否存在 | |
| 108 | + * | |
| 109 | + * @param id 任务编号 | |
| 110 | + * @return 任务 | |
| 111 | + */ | |
| 112 | + Task validateTaskExists(String id); | |
| 113 | + | |
| 114 | + /** | |
| 107 | 115 | * 获取任务 |
| 108 | 116 | * |
| 109 | 117 | * @param id 任务编号 |
| ... | ... | @@ -128,6 +136,16 @@ public interface BpmTaskService { |
| 128 | 136 | List<HistoricTaskInstance> getHistoricTasks(Collection<String> taskIds); |
| 129 | 137 | |
| 130 | 138 | /** |
| 139 | + * 获取历史任务 Map | |
| 140 | + * | |
| 141 | + * @param taskIds 任务编号集合 | |
| 142 | + * @return 历史任务 Map | |
| 143 | + */ | |
| 144 | + default Map<String, HistoricTaskInstance> getHistoricTaskMap(Collection<String> taskIds) { | |
| 145 | + return CollectionUtils.convertMap(getHistoricTasks(taskIds), HistoricTaskInstance::getId); | |
| 146 | + } | |
| 147 | + | |
| 148 | + /** | |
| 131 | 149 | * 根据条件查询正在进行中的任务 |
| 132 | 150 | * |
| 133 | 151 | * @param processInstanceId 流程实例编号,不允许为空 | ... | ... |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| ... | ... | @@ -27,6 +27,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableCon |
| 27 | 27 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; |
| 28 | 28 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; |
| 29 | 29 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; |
| 30 | +import cn.iocoder.yudao.module.bpm.service.comment.BpmCommentService; | |
| 30 | 31 | import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService; |
| 31 | 32 | import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; |
| 32 | 33 | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| ... | ... | @@ -57,6 +58,7 @@ import org.flowable.task.api.history.HistoricTaskInstance; |
| 57 | 58 | import org.flowable.task.api.history.HistoricTaskInstanceQuery; |
| 58 | 59 | import org.flowable.task.service.impl.persistence.entity.TaskEntity; |
| 59 | 60 | import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; |
| 61 | +import org.springframework.context.annotation.Lazy; | |
| 60 | 62 | import org.springframework.stereotype.Service; |
| 61 | 63 | import org.springframework.transaction.annotation.Transactional; |
| 62 | 64 | import org.springframework.transaction.support.TransactionSynchronization; |
| ... | ... | @@ -97,6 +99,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 97 | 99 | @Resource |
| 98 | 100 | private BpmProcessInstanceCopyService processInstanceCopyService; |
| 99 | 101 | @Resource |
| 102 | + @Lazy // 延迟加载,避免循环依赖 | |
| 103 | + private BpmCommentService commentService; | |
| 104 | + @Resource | |
| 100 | 105 | private BpmModelService modelService; |
| 101 | 106 | @Resource |
| 102 | 107 | private BpmMessageService messageService; |
| ... | ... | @@ -310,7 +315,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 310 | 315 | |
| 311 | 316 | @Override |
| 312 | 317 | public Task validateTask(Long userId, String taskId) { |
| 313 | - Task task = validateTaskExist(taskId); | |
| 318 | + Task task = validateTaskExists(taskId); | |
| 314 | 319 | // 为什么判断 assignee 非空的情况下? |
| 315 | 320 | // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过 |
| 316 | 321 | if (StrUtil.isNotBlank(task.getAssignee()) |
| ... | ... | @@ -320,7 +325,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 320 | 325 | return task; |
| 321 | 326 | } |
| 322 | 327 | |
| 323 | - private Task validateTaskExist(String id) { | |
| 328 | + @Override | |
| 329 | + public Task validateTaskExists(String id) { | |
| 324 | 330 | Task task = getTask(id); |
| 325 | 331 | if (task == null) { |
| 326 | 332 | throw exception(TASK_NOT_EXISTS); |
| ... | ... | @@ -340,6 +346,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 340 | 346 | |
| 341 | 347 | @Override |
| 342 | 348 | public List<HistoricTaskInstance> getHistoricTasks(Collection<String> taskIds) { |
| 349 | + if (CollUtil.isEmpty(taskIds)) { | |
| 350 | + return Collections.emptyList(); | |
| 351 | + } | |
| 343 | 352 | return historyService.createHistoricTaskInstanceQuery().taskIds(taskIds).includeTaskLocalVariables().list(); |
| 344 | 353 | } |
| 345 | 354 | |
| ... | ... | @@ -362,7 +371,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 362 | 371 | @Override |
| 363 | 372 | public List<UserTask> getUserTaskListByReturn(String id) { |
| 364 | 373 | // 1.1 校验当前任务 task 存在 |
| 365 | - Task task = validateTaskExist(id); | |
| 374 | + Task task = validateTaskExists(id); | |
| 366 | 375 | // 1.2 根据流程定义获取流程模型信息 |
| 367 | 376 | BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); |
| 368 | 377 | FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); |
| ... | ... | @@ -602,8 +611,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 602 | 611 | taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_SIGN_PIC_URL, reqVO.getSignPicUrl()); |
| 603 | 612 | } |
| 604 | 613 | // 2.2 添加评论 |
| 605 | - taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), | |
| 606 | - BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); | |
| 614 | + commentService.createComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE, | |
| 615 | + reqVO.getReason()); | |
| 607 | 616 | // 2.3 添加附件 |
| 608 | 617 | if (CollUtil.isNotEmpty(reqVO.getAttachments())) { |
| 609 | 618 | reqVO.getAttachments().forEach(attachment -> taskService.createAttachment(BpmAttachmentTypeEnum.TASK_ATTACHMENT.getType(), |
| ... | ... | @@ -767,7 +776,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 767 | 776 | return; |
| 768 | 777 | } |
| 769 | 778 | // 1.2 只处理加签的父任务 |
| 770 | - Task parentTask = validateTaskExist(parentTaskId); | |
| 779 | + Task parentTask = validateTaskExists(parentTaskId); | |
| 771 | 780 | String scopeType = parentTask.getScopeType(); |
| 772 | 781 | if (BpmTaskSignTypeEnum.of(scopeType) == null) { |
| 773 | 782 | return; |
| ... | ... | @@ -812,8 +821,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 812 | 821 | AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId()); |
| 813 | 822 | AdminUserRespDTO ownerUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())); // 发起委托的用户 |
| 814 | 823 | Assert.notNull(ownerUser, "委派任务找不到原审批人,需要检查数据"); |
| 815 | - taskService.addComment(reqVO.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_END.getType(), | |
| 816 | - BpmCommentTypeEnum.DELEGATE_END.formatComment(currentUser.getNickname(), ownerUser.getNickname(), reqVO.getReason())); | |
| 824 | + commentService.createComment(reqVO.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_END, | |
| 825 | + currentUser.getNickname(), ownerUser.getNickname(), reqVO.getReason()); | |
| 817 | 826 | |
| 818 | 827 | // 2.1 调用 resolveTask 完成任务。 |
| 819 | 828 | // 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()):将 owner 设置为 assignee |
| ... | ... | @@ -837,8 +846,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 837 | 846 | // 2.1 更新流程任务为不通过 |
| 838 | 847 | updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason()); |
| 839 | 848 | // 2.2 添加流程评论 |
| 840 | - taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), | |
| 841 | - BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); | |
| 849 | + commentService.createComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT, | |
| 850 | + reqVO.getReason()); | |
| 842 | 851 | // 2.3 添加附件 |
| 843 | 852 | if (CollUtil.isNotEmpty(reqVO.getAttachments())) { |
| 844 | 853 | reqVO.getAttachments().forEach(attachment -> taskService.createAttachment(BpmAttachmentTypeEnum.TASK_ATTACHMENT.getType(), |
| ... | ... | @@ -852,8 +861,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 852 | 861 | String rootParentId = getTaskRootParentId(task); |
| 853 | 862 | updateTaskStatusAndReason(rootParentId, BpmTaskStatusEnum.REJECT.getStatus(), |
| 854 | 863 | BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); |
| 855 | - taskService.addComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), | |
| 856 | - BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); | |
| 864 | + commentService.createComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT, | |
| 865 | + "加签任务不通过"); | |
| 857 | 866 | } |
| 858 | 867 | |
| 859 | 868 | // 3. 根据不同的 RejectHandler 处理策略 |
| ... | ... | @@ -966,8 +975,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 966 | 975 | // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 |
| 967 | 976 | if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 |
| 968 | 977 | // 2.1.1 添加评论 |
| 969 | - taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(), | |
| 970 | - BpmCommentTypeEnum.RETURN.formatComment(reqVO.getReason())); | |
| 978 | + commentService.createComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN, | |
| 979 | + reqVO.getReason()); | |
| 971 | 980 | // 2.1.2 更新 task 状态 + 原因 |
| 972 | 981 | updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason()); |
| 973 | 982 | } else { // 情况二:别人的任务,进行 CANCEL 标记 |
| ... | ... | @@ -1045,8 +1054,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 1045 | 1054 | |
| 1046 | 1055 | // 2. 添加委托意见 |
| 1047 | 1056 | AdminUserRespDTO currentUser = adminUserApi.getUser(userId); |
| 1048 | - taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_START.getType(), | |
| 1049 | - BpmCommentTypeEnum.DELEGATE_START.formatComment(currentUser.getNickname(), delegateUser.getNickname(), reqVO.getReason())); | |
| 1057 | + commentService.createComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_START, | |
| 1058 | + currentUser.getNickname(), delegateUser.getNickname(), reqVO.getReason()); | |
| 1050 | 1059 | |
| 1051 | 1060 | // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) |
| 1052 | 1061 | // 特殊:如果已经被委派(owner 非空),则不需要更新 owner:https://gitee.com/zhijiantianya/yudao-cloud/issues/ICJ153 |
| ... | ... | @@ -1076,8 +1085,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 1076 | 1085 | |
| 1077 | 1086 | // 2. 添加委托意见 |
| 1078 | 1087 | AdminUserRespDTO currentUser = adminUserApi.getUser(userId); |
| 1079 | - taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.TRANSFER.getType(), | |
| 1080 | - BpmCommentTypeEnum.TRANSFER.formatComment(currentUser.getNickname(), assigneeUser.getNickname(), reqVO.getReason())); | |
| 1088 | + commentService.createComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.TRANSFER, | |
| 1089 | + currentUser.getNickname(), assigneeUser.getNickname(), reqVO.getReason()); | |
| 1081 | 1090 | |
| 1082 | 1091 | // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) |
| 1083 | 1092 | // 特殊:如果已经被转派(owner 非空),则不需要更新 owner:https://gitee.com/zhijiantianya/yudao-cloud/issues/ICJ153 |
| ... | ... | @@ -1162,10 +1171,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 1162 | 1171 | |
| 1163 | 1172 | // 4. 记录加签的评论到 task 任务 |
| 1164 | 1173 | AdminUserRespDTO currentUser = adminUserApi.getUser(userId); |
| 1165 | - String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getComment(), | |
| 1174 | + commentService.createComment(reqVO.getId(), taskEntity.getProcessInstanceId(), BpmCommentTypeEnum.ADD_SIGN, | |
| 1166 | 1175 | currentUser.getNickname(), BpmTaskSignTypeEnum.nameOfType(reqVO.getType()), |
| 1167 | 1176 | String.join(",", convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason()); |
| 1168 | - taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), BpmCommentTypeEnum.ADD_SIGN.getType(), comment); | |
| 1169 | 1177 | } |
| 1170 | 1178 | |
| 1171 | 1179 | /** |
| ... | ... | @@ -1273,8 +1281,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 1273 | 1281 | |
| 1274 | 1282 | // 3. 记录日志到父任务中。先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录 |
| 1275 | 1283 | AdminUserRespDTO user = adminUserApi.getUser(userId); |
| 1276 | - taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), BpmCommentTypeEnum.SUB_SIGN.getType(), | |
| 1277 | - StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getComment(), user.getNickname(), cancelUser.getNickname())); | |
| 1284 | + commentService.createComment(task.getParentTaskId(), task.getProcessInstanceId(), BpmCommentTypeEnum.SUB_SIGN, | |
| 1285 | + user.getNickname(), cancelUser.getNickname()); | |
| 1278 | 1286 | |
| 1279 | 1287 | // 4. 处理当前任务的父任务 |
| 1280 | 1288 | handleParentTaskIfSign(task.getParentTaskId()); |
| ... | ... | @@ -1331,8 +1339,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 1331 | 1339 | List<String> withdrawExecutionIds = new ArrayList<>(); |
| 1332 | 1340 | for (Task task : runningTasks) { |
| 1333 | 1341 | // 标记撤回任务为取消 |
| 1334 | - taskService.addComment(task.getId(), taskInstance.getProcessInstanceId(), BpmCommentTypeEnum.CANCEL.getType(), | |
| 1335 | - BpmCommentTypeEnum.CANCEL.formatComment("前一节点撤回")); | |
| 1342 | + commentService.createComment(task.getId(), taskInstance.getProcessInstanceId(), BpmCommentTypeEnum.CANCEL, | |
| 1343 | + "前一节点撤回"); | |
| 1336 | 1344 | updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_WITHDRAW.getReason()); |
| 1337 | 1345 | withdrawExecutionIds.add(task.getExecutionId()); |
| 1338 | 1346 | } |
| ... | ... | @@ -1350,7 +1358,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 1350 | 1358 | * @return 任务信息 |
| 1351 | 1359 | */ |
| 1352 | 1360 | private Task validateTaskCanSignDelete(String id) { |
| 1353 | - Task task = validateTaskExist(id); | |
| 1361 | + Task task = validateTaskExists(id); | |
| 1354 | 1362 | if (task.getParentTaskId() == null) { |
| 1355 | 1363 | throw exception(TASK_SIGN_DELETE_NO_PARENT); |
| 1356 | 1364 | } |
| ... | ... | @@ -1468,7 +1476,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 1468 | 1476 | return; |
| 1469 | 1477 | } |
| 1470 | 1478 | updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); |
| 1471 | - // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 | |
| 1479 | + // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 Flowable Comment 添加理由,所以无法存储具体的取消理由 | |
| 1472 | 1480 | } |
| 1473 | 1481 | |
| 1474 | 1482 | @Override | ... | ... |