Commit 28f547915db96993795ee18dc8e510fe3d6d9ca3

Authored by YunaiV
1 parent ca36970e

feat(bpm): 支持流程评论接口

- 新增流程评论创建、列表查询接口
- 封装 BpmCommentService 统一复用 Flowable Comment
- 评论列表返回评论人和对应任务信息
- 新增 BPM 评论类型字典与评论枚举
- 补充任务校验与历史任务 Map 查询能力
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
... ... @@ -7,4 +7,6 @@ package cn.iocoder.yudao.module.bpm.enums;
7 7 */
8 8 public interface DictTypeConstants {
9 9  
  10 + String COMMENT_TYPE = "bpm_comment_type";
  11 +
10 12 }
... ...
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
... ...