getTenantIds() {
- CollUtil.sort(tenantIds, Long::compareTo);
- return tenantIds;
- }
-
-}
diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java
new file mode 100644
index 000000000..7950a2f96
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java
@@ -0,0 +1,313 @@
+package cn.iocoder.yudao.framework.mybatis.core.query;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.github.yulichang.toolkit.MPJWrappers;
+import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import org.springframework.util.StringUtils;
+
+import java.util.Collection;
+import java.util.function.Consumer;
+
+/**
+ * 拓展 MyBatis Plus Join QueryWrapper 类,主要增加如下功能:
+ *
+ * 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
+ *
+ * @param 数据类型
+ */
+public class MPJLambdaWrapperX extends MPJLambdaWrapper {
+
+ public MPJLambdaWrapperX likeIfPresent(SFunction column, String val) {
+ MPJWrappers.lambdaJoin().like(column, val);
+ if (StringUtils.hasText(val)) {
+ return (MPJLambdaWrapperX) super.like(column, val);
+ }
+ return this;
+ }
+
+ public MPJLambdaWrapperX inIfPresent(SFunction column, Collection> values) {
+ if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
+ return (MPJLambdaWrapperX) super.in(column, values);
+ }
+ return this;
+ }
+
+ public MPJLambdaWrapperX inIfPresent(SFunction column, Object... values) {
+ if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
+ return (MPJLambdaWrapperX) super.in(column, values);
+ }
+ return this;
+ }
+
+ public MPJLambdaWrapperX eqIfPresent(SFunction column, Object val) {
+ if (ObjectUtil.isNotEmpty(val)) {
+ return (MPJLambdaWrapperX) super.eq(column, val);
+ }
+ return this;
+ }
+
+ public MPJLambdaWrapperX neIfPresent(SFunction column, Object val) {
+ if (ObjectUtil.isNotEmpty(val)) {
+ return (MPJLambdaWrapperX) super.ne(column, val);
+ }
+ return this;
+ }
+
+ public MPJLambdaWrapperX gtIfPresent(SFunction column, Object val) {
+ if (val != null) {
+ return (MPJLambdaWrapperX) super.gt(column, val);
+ }
+ return this;
+ }
+
+ public MPJLambdaWrapperX geIfPresent(SFunction column, Object val) {
+ if (val != null) {
+ return (MPJLambdaWrapperX) super.ge(column, val);
+ }
+ return this;
+ }
+
+ public MPJLambdaWrapperX ltIfPresent(SFunction column, Object val) {
+ if (val != null) {
+ return (MPJLambdaWrapperX) super.lt(column, val);
+ }
+ return this;
+ }
+
+ public MPJLambdaWrapperX leIfPresent(SFunction column, Object val) {
+ if (val != null) {
+ return (MPJLambdaWrapperX) super.le(column, val);
+ }
+ return this;
+ }
+
+ public MPJLambdaWrapperX betweenIfPresent(SFunction column, Object val1, Object val2) {
+ if (val1 != null && val2 != null) {
+ return (MPJLambdaWrapperX) super.between(column, val1, val2);
+ }
+ if (val1 != null) {
+ return (MPJLambdaWrapperX) ge(column, val1);
+ }
+ if (val2 != null) {
+ return (MPJLambdaWrapperX) le(column, val2);
+ }
+ return this;
+ }
+
+ public MPJLambdaWrapperX betweenIfPresent(SFunction column, Object[] values) {
+ Object val1 = ArrayUtils.get(values, 0);
+ Object val2 = ArrayUtils.get(values, 1);
+ return betweenIfPresent(column, val1, val2);
+ }
+
+ // ========== 重写父类方法,方便链式调用 ==========
+
+ @Override
+ public MPJLambdaWrapperX eq(boolean condition, SFunction column, Object val) {
+ super.eq(condition, column, val);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX eq(SFunction column, Object val) {
+ super.eq(column, val);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX orderByDesc(SFunction column) {
+ //noinspection unchecked
+ super.orderByDesc(true, column);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX last(String lastSql) {
+ super.last(lastSql);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX in(SFunction column, Collection> coll) {
+ super.in(column, coll);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectAll(Class> clazz) {
+ super.selectAll(clazz);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectAll(Class> clazz, String prefix) {
+ super.selectAll(clazz, prefix);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectAs(SFunction column, String alias) {
+ super.selectAs(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectAs(String column, SFunction alias) {
+ super.selectAs(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectAs(SFunction column, SFunction alias) {
+ super.selectAs(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectAs(String index, SFunction column, SFunction alias) {
+ super.selectAs(index, column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectAsClass(Class source, Class> tag) {
+ super.selectAsClass(source, tag);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectSub(Class clazz, Consumer> consumer, SFunction alias) {
+ super.selectSub(clazz, consumer, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectSub(Class clazz, String st, Consumer> consumer, SFunction alias) {
+ super.selectSub(clazz, st, consumer, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectCount(SFunction column) {
+ super.selectCount(column);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectCount(Object column, String alias) {
+ super.selectCount(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectCount(Object column, SFunction alias) {
+ super.selectCount(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectCount(SFunction column, String alias) {
+ super.selectCount(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectCount(SFunction column, SFunction alias) {
+ super.selectCount(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectSum(SFunction column) {
+ super.selectSum(column);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectSum(SFunction column, String alias) {
+ super.selectSum(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectSum(SFunction column, SFunction alias) {
+ super.selectSum(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectMax(SFunction column) {
+ super.selectMax(column);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectMax(SFunction column, String alias) {
+ super.selectMax(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectMax(SFunction column, SFunction alias) {
+ super.selectMax(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectMin(SFunction column) {
+ super.selectMin(column);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectMin(SFunction column, String alias) {
+ super.selectMin(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectMin(SFunction column, SFunction alias) {
+ super.selectMin(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectAvg(SFunction column) {
+ super.selectAvg(column);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectAvg(SFunction column, String alias) {
+ super.selectAvg(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectAvg(SFunction column, SFunction alias) {
+ super.selectAvg(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectLen(SFunction column) {
+ super.selectLen(column);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectLen(SFunction column, String alias) {
+ super.selectLen(column, alias);
+ return this;
+ }
+
+ @Override
+ public MPJLambdaWrapperX selectLen(SFunction column, SFunction alias) {
+ super.selectLen(column, alias);
+ return this;
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java
index ffbeb39e1..7799c42f6 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java
@@ -5,7 +5,6 @@ import lombok.Data;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
-// TODO @小吉祥:搞个 job,清理 14 天外的访问日志;
/**
* API 访问日志
*
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java
index cae595a7c..087dd5d08 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java
@@ -5,7 +5,6 @@ import lombok.Data;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
-// TODO @小吉祥:搞个 job,清理 14 天外的异常日志;
/**
* API 错误日志
*
diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java
index b4809b83f..89d56b3c0 100644
--- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java
+++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java
@@ -10,26 +10,26 @@ import lombok.Getter;
@AllArgsConstructor
public enum BpmCommentTypeEnum {
- APPROVE(1, "通过"),
- REJECT(2, "不通过"),
- CANCEL(3, "已取消"),
-
- // TODO @海:18 行可以去掉哈;这个是之前为了 status 隔离用的;
- // ========== 流程任务独有的状态 ==========
-
- BACK(4, "退回"), // 退回
- DELEGATE(5, "委派"),
- ADD_SIGN(6, "加签"),
- SUB_SIGN(7,"减签"),
+ APPROVE(1, "通过", ""),
+ REJECT(2, "不通过", ""),
+ CANCEL(3, "已取消", ""),
+ BACK(4, "退回", ""),
+ DELEGATE(5, "委派", ""),
+ ADD_SIGN(6, "加签", "[{}]{}给了[{}],理由为:{}"),
+ SUB_SIGN(7, "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"),
;
- // TODO @海:字段叫 type 更合适噢
/**
- * 结果
+ * 操作类型
*/
- private final Integer result;
+ private final Integer type;
/**
- * 描述
+ * 操作名字
*/
- private final String desc;
+ private final String name;
+ /**
+ * 操作描述
+ */
+ private final String comment;
+
}
diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java
index aca732ba9..615416c73 100644
--- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java
+++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java
@@ -27,19 +27,27 @@ public enum BpmProcessInstanceResultEnum {
DELEGATE(6, "委派"),
/**
* 【加签】源任务已经审批完成,但是它使用了后加签,后加签的任务未完成,源任务就会是这个状态
+ * 相当于是 通过 APPROVE 的特殊状态
* 例如:A审批, A 后加签了 B,并且审批通过了任务,但是 B 还未审批,则当前任务状态为“待后加签任务完成”
*/
- ADD_SIGN_AFTER(7, "待后加签任务完成"), // TODO @海:这个定义,是不是 通过(待后加签任务完成),相当于是 APPROVE 的特殊状态
+ SIGN_AFTER(7, "待后加签任务完成"),
/**
* 【加签】源任务未审批,但是向前加签了,所以源任务状态变为“待前加签任务完成”
+ * 相当于是 处理中 PROCESS 的特殊状态
* 例如:A 审批, A 前加签了 B,B 还未审核
*/
- ADD_SIGN_BEFORE(8, "待前加签任务完成"), // TODO @海:这个定义,是不是 处理中(待前加签任务审批),相当于是 PROCESS 的特殊状态
+ SIGN_BEFORE(8, "待前加签任务完成"),
/**
* 【加签】后加签任务被创建时的初始状态
+ * 相当于是 处理中 PROCESS 的特殊状态
* 因为需要源任务先完成,才能到后加签的人来审批,所以加了一个状态区分
*/
- WAIT_BEFORE_TASK(9, "待前置任务完成"); // TODO @海:这个定义,是不是 处理中(待前置任务审批),相当于是 PROCESS 的特殊状态
+ WAIT_BEFORE_TASK(9, "待前置任务完成");
+
+ /**
+ * 能被减签的状态
+ */
+ public static final List CAN_SUB_SIGN_STATUS_LIST = Arrays.asList(PROCESS.result, WAIT_BEFORE_TASK.result);
/**
* 结果
@@ -63,13 +71,7 @@ public enum BpmProcessInstanceResultEnum {
public static boolean isEndResult(Integer result) {
return ObjectUtils.equalsAny(result, APPROVE.getResult(), REJECT.getResult(),
CANCEL.getResult(), BACK.getResult(),
- ADD_SIGN_AFTER.getResult());
+ SIGN_AFTER.getResult());
}
- // TODO @海:静态变量,需要放到成员变量前面;另外,如果是复数,可以加 S(ES) 或者 LIST
- /**
- * 能被减签的状态
- */
- public static final List CAN_SUB_SIGN_STATUS = Arrays.asList(PROCESS.result, WAIT_BEFORE_TASK.result);
-
}
diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskAddSignTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskAddSignTypeEnum.java
index 3bf41a153..42c212e28 100644
--- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskAddSignTypeEnum.java
+++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskAddSignTypeEnum.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.bpm.enums.task;
import lombok.AllArgsConstructor;
import lombok.Getter;
-
/**
* 流程任务 -- 加签类型枚举类型
*/
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
index 694b35da4..dcfab78ef 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
@@ -4,16 +4,15 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import io.swagger.v3.oas.annotations.Parameter;
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.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -75,7 +74,7 @@ public class BpmTaskController {
return success(true);
}
- @GetMapping("/get-return-list")
+ @GetMapping("/return-list")
@Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮")
@Parameter(name = "taskId", description = "当前任务ID", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
@@ -95,37 +94,32 @@ public class BpmTaskController {
@Operation(summary = "委派任务", description = "用于【流程详情】的【委派】按钮。和向前【加签】有点像,唯一区别是【委托】没有单独创立任务")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) {
- // TODO @海:, 后面要有空格
- taskService.delegateTask(reqVO,getLoginUserId());
+ taskService.delegateTask(getLoginUserId(), reqVO);
return success(true);
}
- // TODO @海:权限统一使用 bpm:task:update;是否可以加减签,可以交给后续的权限配置实现;
- @PutMapping("/add-sign")
+ @PutMapping("/create-sign")
@Operation(summary = "加签", description = "before 前加签,after 后加签")
- @PreAuthorize("@ss.hasPermission('bpm:task:add-sign')")
- public CommonResult addSign(@Valid @RequestBody BpmTaskAddSignReqVO reqVO) {
- // TODO @海:userId 建议作为第一个参数;一般是,谁做了什么操作;另外,addSignTask,保持风格统一哈;
- taskService.addSign(reqVO,getLoginUserId());
+ @PreAuthorize("@ss.hasPermission('bpm:task:update')")
+ public CommonResult createSignTask(@Valid @RequestBody BpmTaskAddSignReqVO reqVO) {
+ taskService.createSignTask(getLoginUserId(), reqVO);
return success(true);
}
- // TODO @海:权限统一使用 bpm:task:update;是否可以加减签,可以交给后续的权限配置实现;
- @PutMapping("/sub-sign")
+ @DeleteMapping("/delete-sign")
@Operation(summary = "减签")
- @PreAuthorize("@ss.hasPermission('bpm:task:sub-sign')")
- // TODO @海: @RequestBody BpmTaskSubSignReqVO 应该是一个空格;然后参数名可以简写成 reqVO;
- public CommonResult subSign(@Valid @RequestBody BpmTaskSubSignReqVO bpmTaskSubSignReqVO) {
- taskService.subSign(bpmTaskSubSignReqVO,getLoginUserId());
+ @PreAuthorize("@ss.hasPermission('bpm:task:update')")
+ public CommonResult deleteSignTask(@Valid @RequestBody BpmTaskSubSignReqVO reqVO) {
+ taskService.deleteSignTask(getLoginUserId(), reqVO);
return success(true);
}
- // TODO @海:是不是 url 和方法名,叫 get-child-task-list,更抽象和复用一些?
- @GetMapping("/get-sub-sign")
+ @GetMapping("children-list")
@Operation(summary = "获取能被减签的任务")
- @PreAuthorize("@ss.hasPermission('bpm:task:sub-sign')")
- public CommonResult> getChildrenTaskList(@RequestParam("taskId") String taskId) {
- return success(taskService.getChildrenTaskList(taskId));
+ @Parameter(name = "parentId", description = "父级任务 ID", required = true)
+ @PreAuthorize("@ss.hasPermission('bpm:task:update')")
+ public CommonResult> getChildrenTaskList(@RequestParam("parentId") String parentId) {
+ return success(taskService.getChildrenTaskList(parentId));
}
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskAddSignReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskAddSignReqVO.java
index f4b9ac0ac..cabb91be1 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskAddSignReqVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskAddSignReqVO.java
@@ -4,13 +4,17 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
-import java.util.List;
import java.util.Set;
+// TODO @海洋:类名,应该是 create 哈
@Schema(description = "管理后台 - 加签流程任务的 Request VO")
@Data
public class BpmTaskAddSignReqVO {
+ @Schema(description = "需要加签的任务 ID")
+ @NotEmpty(message = "任务编号不能为空")
+ private String id;
+
@Schema(description = "加签的用户 ID")
@NotEmpty(message = "加签用户 ID 不能为空")
private Set userIdList;
@@ -23,9 +27,4 @@ public class BpmTaskAddSignReqVO {
@NotEmpty(message = "加签原因不能为空")
private String reason;
- // TODO @海:重要参数,可以放到最前面哈;
- @Schema(description = "需要加签的任务 ID")
- @NotEmpty(message = "任务编号不能为空")
- private String id;
-
-}
\ 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/task/vo/task/BpmTaskSubSignReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.java
index 665a9aec8..731e4804a 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.java
@@ -5,6 +5,7 @@ import lombok.Data;
import javax.validation.constraints.NotEmpty;
+// TODO @海洋:类名,应该是 delete 哈
@Schema(description = "管理后台 - 减签流程任务的 Request VO")
@Data
public class BpmTaskSubSignReqVO {
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
index 9a0006e8d..60ce84021 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
@@ -1,9 +1,10 @@
package cn.iocoder.yudao.module.bpm.convert.task;
-import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
@@ -25,6 +26,9 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
+
/**
* Bpm 任务 Convert
*
@@ -147,7 +151,8 @@ public interface BpmTaskConvert {
.setDefinitionKey(element.getId()));
}
- // TODO @海:可以使用 mapstruct 映射么?
+ //此处不用 mapstruct 映射,因为 TaskEntityImpl 还有很多其他属性,这里我们只设置我们需要的
+ //使用 mapstruct 会将里面嵌套的各个属性值都设置进去,会出现意想不到的问题
default TaskEntityImpl convert(TaskEntityImpl task,TaskEntityImpl parentTask){
task.setCategory(parentTask.getCategory());
task.setDescription(parentTask.getDescription());
@@ -166,32 +171,31 @@ public interface BpmTaskConvert {
default List convertList(List bpmTaskExtDOList,
Map userMap,
Map idTaskMap){
- return CollectionUtils.convertList(bpmTaskExtDOList, task->{
- BpmTaskSubSignRespVO bpmTaskSubSignRespVO = new BpmTaskSubSignRespVO();
- bpmTaskSubSignRespVO.setName(task.getName());
- bpmTaskSubSignRespVO.setId(task.getTaskId());
- Task sourceTask = idTaskMap.get(task.getTaskId());
+ return CollectionUtils.convertList(bpmTaskExtDOList, task -> {
+ BpmTaskSubSignRespVO bpmTaskSubSignRespVO = new BpmTaskSubSignRespVO()
+ .setId(task.getTaskId()).setName(task.getName());
// 后加签任务不会直接设置 assignee ,所以不存在 assignee 的情况,则去取 owner
- String assignee = StrUtil.isNotEmpty(sourceTask.getAssignee()) ? sourceTask.getAssignee() : sourceTask.getOwner();
- AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(assignee));
- if (assignUser != null) {
- bpmTaskSubSignRespVO.setAssigneeUser(convert3(assignUser));
- }
+ Task sourceTask = idTaskMap.get(task.getTaskId());
+ String assignee = ObjectUtil.defaultIfBlank(sourceTask.getOwner(),sourceTask.getAssignee());
+ MapUtils.findAndThen(userMap,NumberUtils.parseLong(assignee),
+ assignUser-> bpmTaskSubSignRespVO.setAssigneeUser(convert3(assignUser)));
return bpmTaskSubSignRespVO;
});
}
/**
* 转换任务为父子级
- * @param result
- * @return
+ *
+ * @param sourceList 原始数据
+ * @return 转换后的父子级数组
*/
- default List convertChildrenList(List result){
- List childrenTaskList = CollectionUtils.filterList(result, r -> StrUtil.isNotEmpty(r.getParentTaskId()));
- Map> parentChildrenTaskListMap = CollectionUtils.convertMultiMap(childrenTaskList, BpmTaskRespVO::getParentTaskId);
- for (BpmTaskRespVO bpmTaskRespVO : result) {
+ default List convertChildrenList(List sourceList) {
+ List childrenTaskList = filterList(sourceList, r -> StrUtil.isNotEmpty(r.getParentTaskId()));
+ Map> parentChildrenTaskListMap = convertMultiMap(childrenTaskList, BpmTaskRespVO::getParentTaskId);
+ for (BpmTaskRespVO bpmTaskRespVO : sourceList) {
bpmTaskRespVO.setChildren(parentChildrenTaskListMap.get(bpmTaskRespVO.getId()));
}
- return CollectionUtils.filterList(result, r -> StrUtil.isEmpty(r.getParentTaskId()));
+ return filterList(sourceList, r -> StrUtil.isEmpty(r.getParentTaskId()));
}
+
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmTaskExtMapper.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmTaskExtMapper.java
index a9031b3b6..8108e613d 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmTaskExtMapper.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/task/BpmTaskExtMapper.java
@@ -21,13 +21,13 @@ public interface BpmTaskExtMapper extends BaseMapperX {
return selectList(BpmTaskExtDO::getTaskId, taskIds);
}
+ // TODO @海:BpmProcessInstanceResultEnum.CAN_SUB_SIGN_STATUS_LIST) 应该作为条件,mapper 不要有业务
default List selectProcessListByTaskIds(Collection taskIds) {
return selectList(new LambdaQueryWrapperX()
.in(BpmTaskExtDO::getTaskId, taskIds)
- .in(BpmTaskExtDO::getResult, BpmProcessInstanceResultEnum.CAN_SUB_SIGN_STATUS));
+ .in(BpmTaskExtDO::getResult, BpmProcessInstanceResultEnum.CAN_SUB_SIGN_STATUS_LIST));
}
-
default BpmTaskExtDO selectByTaskId(String taskId) {
return selectOne(BpmTaskExtDO::getTaskId, taskId);
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmSecurityConfiguration.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmSecurityConfiguration.java
deleted file mode 100644
index 2069f7d11..000000000
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/config/BpmSecurityConfiguration.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.module.bpm.framework.bpm.config;
-
-import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
-
-/**
- * @author kemengkai
- * @create 2022-05-07 08:15
- */
-@Configuration("bpmSecurityConfiguration")
-public class BpmSecurityConfiguration {
-
- @Bean("bpmAuthorizeRequestsCustomizer")
- public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
- return new AuthorizeRequestsCustomizer() {
-
- @Override
- public void customize(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry) {
- // 任务回退接口
- registry.antMatchers(buildAdminApi("/bpm/task/back")).permitAll();
- }
-
- };
- }
-}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
index 0addde074..03fc99e03 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
@@ -12,6 +12,7 @@ import javax.validation.Valid;
* @author yunlongn
*/
public interface BpmModelService {
+
/**
* 获得流程模型分页
*
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
index 81c437710..8f7e3996b 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
@@ -288,5 +288,4 @@ public class BpmModelServiceImpl implements BpmModelService {
processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), SuspensionState.SUSPENDED.getStateCode());
}
-
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
index 229e68d23..42be9260a 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
@@ -65,7 +65,8 @@ public interface BpmTaskService {
/**
- * 通过任务 ID 集合,获取任务扩展表信息集合 // TODO @海洋:方法注释,和下面的参数,需要空一行
+ * 通过任务 ID 集合,获取任务扩展表信息集合
+ *
* @param taskIdList 任务 ID 集合
* @return 任务列表
*/
@@ -143,41 +144,41 @@ public interface BpmTaskService {
* 将任务回退到指定的 targetDefinitionKey 位置
*
* @param userId 用户编号
- * @param reqVO 回退的任务key和当前所在的任务ID
+ * @param reqVO 回退的任务key和当前所在的任务ID
*/
void returnTask(Long userId, BpmTaskReturnReqVO reqVO);
- // TODO @海:userId 放前面
+
/**
* 将指定任务委派给其他人处理,等接收人处理后再回到原审批人手中审批
*
- * @param reqVO 被委派人和被委派的任务编号理由参数
* @param userId 用户编号
+ * @param reqVO 被委派人和被委派的任务编号理由参数
*/
- void delegateTask(BpmTaskDelegateReqVO reqVO, Long userId);
+ void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO);
/**
* 任务加签
*
- * @param reqVO 被加签的用户和任务 ID,加签类型
- * @param userId 当前用户 ID
+ * @param userId 被加签的用户和任务 ID,加签类型
+ * @param reqVO 当前用户 ID
*/
- void addSign(BpmTaskAddSignReqVO reqVO, Long userId);
+ void createSignTask(Long userId, BpmTaskAddSignReqVO reqVO);
/**
* 任务减签名
*
- * @param bpmTaskSubSignReqVO 被减签的任务 ID,理由
- * @param loginUserId 当前用户ID
+ * @param userId 当前用户ID
+ * @param reqVO 被减签的任务 ID,理由
*/
- void subSign(BpmTaskSubSignReqVO bpmTaskSubSignReqVO, Long loginUserId);
+ void deleteSignTask(Long userId, BpmTaskSubSignReqVO reqVO);
/**
* 获取指定任务的子任务和审批人信息
*
- * @param taskId 指定任务ID
+ * @param parentId 指定任务ID
* @return 子任务列表
*/
- List getChildrenTaskList(String taskId);
+ List getChildrenTaskList(String parentId);
}
diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
index 371c30d84..2b0e18e70 100644
--- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
+++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.bpm.service.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.enums.SymbolConstant;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
@@ -15,7 +15,10 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
-import cn.iocoder.yudao.module.bpm.enums.task.*;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskAddSignTypeEnum;
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
@@ -50,7 +53,6 @@ import javax.annotation.Resource;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.*;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -222,8 +224,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 情况二:后加签的任务
if (BpmTaskAddSignTypeEnum.AFTER.getType().equals(task.getScopeType())) {
// 后加签处理
- // TODO @海洋:这个是不是 approveAfterSignTask
- handleAfterSignTask(task, reqVO);
+ approveAfterSignTask(task, reqVO);
return;
}
@@ -238,18 +239,19 @@ public class BpmTaskServiceImpl implements BpmTaskService {
handleParentTask(task);
}
+
/**
* 审批通过存在“后加签”的任务。
+ *
+ * 注意:该任务不能马上完成,需要一个中间状态(SIGN_AFTER),并激活剩余所有子任务(PROCESS)为可审批处理
*
- * 注意:该任务不能马上完成,需要一个中间状态(ADD_SIGN_AFTER),并激活剩余所有子任务(PROCESS)为可审批处理
- *
- * @param task 当前任务
+ * @param task 当前任务
* @param reqVO 前端请求参数
*/
- private void handleAfterSignTask(Task task,BpmTaskApproveReqVO reqVO){
+ private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) {
// 1. 有向后加签,则该任务状态临时设置为 ADD_SIGN_AFTER 状态
taskExtMapper.updateByTaskId(
- new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.ADD_SIGN_AFTER.getResult())
+ new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.SIGN_AFTER.getResult())
.setReason(reqVO.getReason()).setEndTime(LocalDateTime.now()));
// 2. 激活子任务
@@ -257,118 +259,123 @@ public class BpmTaskServiceImpl implements BpmTaskService {
for (String childrenTaskId : childrenTaskIdList) {
taskService.resolveTask(childrenTaskId);
}
- // 更新任务扩展表中子任务为进行中
+ // 2.1 更新任务扩展表中子任务为进行中
taskExtMapper.updateBatchByTaskIdList(childrenTaskIdList,
new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()));
}
/**
- * 处理当前任务的父任务
+ * 处理当前任务的父任务,主要处理“加签”的情况
*
* @param task 当前任务
*/
private void handleParentTask(Task task) {
String parentTaskId = task.getParentTaskId();
- // TODO @ 海:if return 原则
- if (StrUtil.isNotBlank(parentTaskId)) {
- // 1. 判断当前任务的父任务是否还有子任务
- long subTaskCount = getSubTaskCount(parentTaskId);
- // TODO @ 海:if >= 0 return;这样括号又可以少一层;
- if (subTaskCount == 0) {
- // 2. 获取父任务
- Task parentTask = validateTaskExist(parentTaskId);
-
- // 3. 情况一:处理向前加签
- String scopeType = parentTask.getScopeType();
- if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(scopeType)) {
- // 3.1 如果是向前加签的任务,则调用 resolveTask 指派父任务,将 owner 重新赋值给父任务的 assignee
- taskService.resolveTask(parentTaskId);
- // 3.2 更新任务拓展表为处理中
- taskExtMapper.updateByTaskId(
- new BpmTaskExtDO().setTaskId(parentTask.getId()).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()));
- } else if (BpmTaskAddSignTypeEnum.AFTER.getType().equals(scopeType)) {
- // 3. 情况二:处理向后加签
- handleAfterSign(parentTask);
- }
-
- // 4. 子任务已处理完成,清空 scopeType 字段,修改 parentTask 信息,方便后续可以继续向前后向后加签
- // 再查询一次的原因是避免报错:Task was updated by another transaction concurrently
- // 因为前面处理后可能会导致 parentTask rev 字段被修改,需要重新获取最新的
- parentTask = getTask(parentTaskId);
- // TODO @ 海:if return 原则;
- if (parentTask != null) {
- // 为空的情况是:已经通过 handleAfterSign 方法将任务完成了,所以 ru_task 表会查不到数据
- clearTaskScopeTypeAndSave(parentTask);
- }
- }
+ if (StrUtil.isBlank(parentTaskId)) {
+ return;
}
+ // 1. 判断当前任务的父任务是否还有子任务
+ Long childrenTaskCount = getChildrenTaskCount(parentTaskId);
+ if (childrenTaskCount > 0) {
+ return;
+ }
+ // 2. 获取父任务
+ Task parentTask = validateTaskExist(parentTaskId);
+
+ // 3. 处理加签情况
+ String scopeType = parentTask.getScopeType();
+ if(!validateSignType(scopeType)){
+ return;
+ }
+ // 3.1 情况一:处理向前加签
+ if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(scopeType)) {
+ // 3.1.1 如果是向前加签的任务,则调用 resolveTask 指派父任务,将 owner 重新赋值给父任务的 assignee,这样它就可以被审批
+ taskService.resolveTask(parentTaskId);
+ // 3.1.2 更新任务拓展表为处理中
+ taskExtMapper.updateByTaskId(
+ new BpmTaskExtDO().setTaskId(parentTask.getId()).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()));
+ } else if (BpmTaskAddSignTypeEnum.AFTER.getType().equals(scopeType)) {
+ // 3.2 情况二:处理向后加签
+ handleParentTaskForAfterSign(parentTask);
+ }
+
+ // 4. 子任务已处理完成,清空 scopeType 字段,修改 parentTask 信息,方便后续可以继续向前后向后加签
+ // 再查询一次的原因是避免报错:Task was updated by another transaction concurrently
+ // 因为前面处理后可能会导致 parentTask rev 字段被修改,需要重新获取最新的
+ parentTask = getTask(parentTaskId);
+ if (parentTask == null) {
+ // 为空的情况是:已经通过 handleAfterSign 方法将任务完成了,所以 ru_task 表会查不到数据
+ return;
+ }
+ clearTaskScopeTypeAndSave(parentTask);
}
- // TODO @海:这个方法的 4.1 从 1 开始计数哈;另外;看看能不能用 if return 进一步简化代码的层级;
+
/**
* 处理后加签任务
*
- * @param parentTask 当前审批任务的父任务
+ * @param parentTask 当前审批任务的父任务
*/
- private void handleAfterSign(Task parentTask) {
- // TODO @海:这个方法在注释下。感觉整体是,先完成自己;然后递归处理父节点;但是完成机子被放到了 5.1,就感觉上有点割裂;
+ // TODO @海:这个逻辑,怎么感觉可以是 parentTask 的 parent,再去调用 handleParentTask 方法;可以微信聊下;
+ private void handleParentTaskForAfterSign(Task parentTask) {
String parentTaskId = parentTask.getId();
- //4.1 更新 parentTask 的任务拓展表为通过
+ // 1. 更新 parentTask 的任务拓展表为通过,并调用 complete 完成自己
BpmTaskExtDO currentTaskExt = taskExtMapper.selectByTaskId(parentTask.getId());
- BpmTaskExtDO currentTaskUpdateEntity = new BpmTaskExtDO().setTaskId(parentTask.getId())
+ BpmTaskExtDO currentTaskExtUpdateObj = new BpmTaskExtDO().setTaskId(parentTask.getId())
.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult());
if (currentTaskExt.getEndTime() == null) {
- // 有这个判断是因为
- // 4.2 以前没设置过结束时间,才去设置
- currentTaskUpdateEntity.setEndTime(LocalDateTime.now());
+ // 1.1 有这个判断是因为,以前没设置过结束时间,才去设置
+ currentTaskExtUpdateObj.setEndTime(LocalDateTime.now());
}
- taskExtMapper.updateByTaskId(currentTaskUpdateEntity);
-
- // 5. 继续往上处理,父任务继续往上查询
- // 5.1 先完成自己
+ taskExtMapper.updateByTaskId(currentTaskExtUpdateObj);
+ // 1.2 完成自己(因为它已经没有子任务,所以也可以完成)
taskService.complete(parentTaskId);
- // 5.2 如果有父级,递归查询上级任务是否都已经完成
- // TODO @海:这块待讨论,脑子略乱;感觉 handleAfterSign 的后半段,和 handleParentTask 有点重叠???
- if (StrUtil.isNotEmpty(parentTask.getParentTaskId())) {
- // 判断整条链路的任务是否完成
- // 例如从 A 任务加签了一个 B 任务,B 任务又加签了一个 C 任务,C 任务加签了 D 任务
- // 此时,D 任务完成,要一直往上找到祖先任务 A调用 complete 方法完成 A 任务
- boolean allChildrenTaskFinish = true;
- while (StrUtil.isNotBlank(parentTask.getParentTaskId())) {
- parentTask = validateTaskExist(parentTask.getParentTaskId());
- BpmTaskExtDO bpmTaskExtDO = taskExtMapper.selectByTaskId(parentTask.getId());
- if (bpmTaskExtDO == null) {
- break;
- }
- boolean currentTaskFinish = BpmProcessInstanceResultEnum.isEndResult(bpmTaskExtDO.getResult());
- // 5.3 如果 allChildrenTaskFinish 已经被赋值为 false ,则不会再赋值为 true,因为整个链路没有完成
- if (allChildrenTaskFinish) {
- allChildrenTaskFinish = currentTaskFinish;
- }
- if (!currentTaskFinish) {
- // 6 处理非完成状态的任务
- // 6.1 判断当前任务的父任务是否还有子任务
- Long subTaskCount = getSubTaskCount(bpmTaskExtDO.getTaskId());
- if (subTaskCount == 0) {
- // 6.2 没有子任务,判断当前任务状态是否为 ADD_SIGN_BEFORE 待前加签任务完成
- if (BpmProcessInstanceResultEnum.ADD_SIGN_BEFORE.getResult().equals(bpmTaskExtDO.getResult())) {
- // 6.3 需要修改该任务状态为处理中
- taskService.resolveTask(bpmTaskExtDO.getTaskId());
- bpmTaskExtDO.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
- taskExtMapper.updateByTaskId(bpmTaskExtDO);
- }
- // 6.4 清空 scopeType 字段,用于任务没有子任务时使用该方法,方便任务可以再次被不同的方式加签
- parentTask = getTask(bpmTaskExtDO.getTaskId());
- if (parentTask != null) {
- clearTaskScopeTypeAndSave(parentTask);
- }
- }
- }
+
+ // 2. 如果有父级,递归查询上级任务是否都已经完成
+ if (StrUtil.isEmpty(parentTask.getParentTaskId())) {
+ return;
+ }
+ // 2.1 判断整条链路的任务是否完成
+ // 例如从 A 任务加签了一个 B 任务,B 任务又加签了一个 C 任务,C 任务加签了 D 任务
+ // 此时,D 任务完成,要一直往上找到祖先任务 A调用 complete 方法完成 A 任务
+ boolean allChildrenTaskFinish = true;
+ while (StrUtil.isNotBlank(parentTask.getParentTaskId())) {
+ parentTask = validateTaskExist(parentTask.getParentTaskId());
+ BpmTaskExtDO parentTaskExt = taskExtMapper.selectByTaskId(parentTask.getId());
+ if (parentTaskExt == null) {
+ break;
}
+ boolean currentTaskFinish = BpmProcessInstanceResultEnum.isEndResult(parentTaskExt.getResult());
+ // 2.2 如果 allChildrenTaskFinish 已经被赋值为 false,则不会再赋值为 true,因为整个链路没有完成
if (allChildrenTaskFinish) {
- // 7. 完成最后的顶级祖先任务
- taskService.complete(parentTask.getId());
+ allChildrenTaskFinish = currentTaskFinish;
}
+ // 2.3 任务已完成则不处理
+ if (currentTaskFinish) {
+ continue;
+ }
+
+ // 3 处理非完成状态的任务
+ // 3.1 判断当前任务的父任务是否还有子任务
+ Long childrenTaskCount = getChildrenTaskCount(parentTaskExt.getTaskId());
+ if (childrenTaskCount > 0) {
+ continue;
+ }
+ // 3.2 没有子任务,判断当前任务状态是否为 ADD_SIGN_BEFORE 待前加签任务完成
+ if (BpmProcessInstanceResultEnum.SIGN_BEFORE.getResult().equals(parentTaskExt.getResult())) {
+ // 3.3 需要修改该任务状态为处理中
+ taskService.resolveTask(parentTaskExt.getTaskId());
+ parentTaskExt.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
+ taskExtMapper.updateByTaskId(parentTaskExt);
+ }
+ // 3.4 清空 scopeType 字段,用于任务没有子任务时使用该方法,方便任务可以再次被不同的方式加签
+ parentTask = validateTaskExist(parentTaskExt.getTaskId());
+ clearTaskScopeTypeAndSave(parentTask);
+ }
+
+ // 4. 完成最后的顶级祖先任务
+ if (allChildrenTaskFinish) {
+ taskService.complete(parentTask.getId());
}
}
@@ -383,14 +390,13 @@ public class BpmTaskServiceImpl implements BpmTaskService {
taskService.saveTask(task);
}
- // TODO @海:Sub 还有 Child 感觉整体用词不是很统一;是不是要统一下;然后 Sub 还可以指的减签;看看是不是名词确实得一致哈;
/**
* 获取子任务个数
*
* @param parentTaskId 父任务 ID
* @return 剩余子任务个数
*/
- private Long getSubTaskCount(String parentTaskId) {
+ private Long getChildrenTaskCount(String parentTaskId) {
String tableName = managementService.getTableName(TaskEntity.class);
String sql = "SELECT COUNT(1) from " + tableName + " WHERE PARENT_TASK_ID_=#{parentTaskId}";
return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count();
@@ -410,7 +416,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
String comment = StrUtil.format("[{}]完成委派任务,任务重新回到[{}]手中,审批意见为:{}", currentUser.getNickname(),
sourceApproveUser.getNickname(), reqVO.getReason());
taskService.addComment(reqVO.getId(), task.getProcessInstanceId(),
- BpmCommentTypeEnum.DELEGATE.getResult().toString(), comment);
+ BpmCommentTypeEnum.DELEGATE.getType().toString(), comment);
// 2.1 调用 resolveTask 完成任务。
// 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()):将 owner 设置为 assignee
@@ -528,7 +534,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
- if(StrUtil.isNotEmpty(task.getAssignee())){
+ if (StrUtil.isNotEmpty(task.getAssignee())) {
ProcessInstance processInstance =
processInstanceService.getProcessInstance(task.getProcessInstanceId());
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
@@ -539,8 +545,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
});
}
- private Task validateTaskExist(String id){
- Task task = taskService.createTaskQuery().taskId(id).singleResult();
+ private Task validateTaskExist(String id) {
+ Task task = getTask(id);
if (task == null) {
throw exception(TASK_NOT_EXISTS);
}
@@ -646,7 +652,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
return;
}
taskService.addComment(task.getId(), currentTask.getProcessInstanceId(),
- BpmCommentTypeEnum.BACK.getResult().toString(), reqVO.getReason());
+ BpmCommentTypeEnum.BACK.getType().toString(), reqVO.getReason());
});
// 3. 执行驳回
@@ -659,7 +665,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
@Override
@Transactional(rollbackFor = Exception.class)
- public void delegateTask(BpmTaskDelegateReqVO reqVO, Long userId) {
+ public void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO) {
// 1.1 校验任务
Task task = validateTaskCanDelegate(userId, reqVO);
// 1.2 校验目标用户存在
@@ -674,7 +680,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
delegateUser.getNickname(), reqVO.getReason());
String taskId = reqVO.getId();
taskService.addComment(taskId, task.getProcessInstanceId(),
- BpmCommentTypeEnum.DELEGATE.getResult().toString(), comment);
+ BpmCommentTypeEnum.DELEGATE.getType().toString(), comment);
// 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee)
taskService.setOwner(taskId, task.getAssignee());
@@ -705,23 +711,18 @@ public class BpmTaskServiceImpl implements BpmTaskService {
@Override
@Transactional(rollbackFor = Exception.class)
- public void addSign(BpmTaskAddSignReqVO reqVO, Long userId) {
- // TODO @海:// 后面要有个空格;中英文之间,也要有空格;例如说:// 1. 获取和校验任务
- //1.获取和校验任务
+ public void createSignTask(Long userId, BpmTaskAddSignReqVO reqVO) {
+ // 1. 获取和校验任务
TaskEntityImpl taskEntity = validateAddSign(userId, reqVO);
- // TODO @海:每个变量,以及相关逻辑,一定要和自己的逻辑块呆在一起。例如说这里的 currentUser 获取,应该在 4. 那块
- AdminUserRespDTO currentUser = adminUserApi.getUser(userId);
List userList = adminUserApi.getUserList(reqVO.getUserIdList());
if (CollUtil.isEmpty(userList)) {
throw exception(TASK_ADD_SIGN_USER_NOT_EXIST);
}
- // TODO @海:大的逻辑块之间,最好有一个空格。这样的目的,是避免逻辑堆砌在一起,方便流量;
- // 2.处理当前任务
- // 2.1 开启计数功能 // TODO @海:这个目的可以写下;
+
+ // 2. 处理当前任务
+ // 2.1 开启计数功能,主要用于为了让表 ACT_RU_TASK 中的 SUB_TASK_COUNT_ 字段记录下总共有多少子任务,后续可能有用
taskEntity.setCountEnabled(true);
- // TODO @海:可以直接 if,不搞一个变量哈
- boolean addSignToBefore = reqVO.getType().equals(BpmTaskAddSignTypeEnum.BEFORE.getType());
- if (addSignToBefore) {
+ if (reqVO.getType().equals(BpmTaskAddSignTypeEnum.BEFORE.getType())) {
// 2.2 向前加签,设置 owner,置空 assign。等子任务都完成后,再调用 resolveTask 重新将 owner 设置为 assign
// 原因是:不能和向前加签的子任务一起审批,需要等前面的子任务都完成才能审批
taskEntity.setOwner(taskEntity.getAssignee());
@@ -729,7 +730,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 2.3 更新扩展表状态
taskExtMapper.updateByTaskId(
new BpmTaskExtDO().setTaskId(taskEntity.getId())
- .setResult(BpmProcessInstanceResultEnum.ADD_SIGN_BEFORE.getResult())
+ .setResult(BpmProcessInstanceResultEnum.SIGN_BEFORE.getResult())
.setReason(reqVO.getReason()));
}
// 2.4 记录加签方式,完成任务时需要用到判断
@@ -738,20 +739,20 @@ public class BpmTaskServiceImpl implements BpmTaskService {
taskService.saveTask(taskEntity);
// 3. 创建加签任务
- createSignSubTasks(convertList(reqVO.getUserIdList(), String::valueOf), taskEntity);
+ createSignTask(convertList(reqVO.getUserIdList(), String::valueOf), taskEntity);
// 4. 记录加签 comment,拼接结果为: [当前用户]向前加签/向后加签给了[多个用户],理由为:reason
- // TODO @海:BpmCommentTypeEnum 可以加一个 comment 字段,作为评论模版,统一管理;
- String comment = StrUtil.format("[{}]{}给了[{}],理由为:{}", currentUser.getNickname(), BpmTaskAddSignTypeEnum.formatDesc(reqVO.getType()),
- String.join(SymbolConstant.D, convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason());
+ AdminUserRespDTO currentUser = adminUserApi.getUser(userId);
+ String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getComment(), currentUser.getNickname(),
+ BpmTaskAddSignTypeEnum.formatDesc(reqVO.getType()), String.join(",", convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason());
taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(),
- BpmCommentTypeEnum.ADD_SIGN.getResult().toString(), comment);
+ BpmCommentTypeEnum.ADD_SIGN.getType().toString(), comment);
}
/**
* 校验任务的加签是否一致
- *
+ *
* 1. 如果存在“向前加签”的任务,则不能“向后加签”
* 2. 如果存在“向后加签”的任务,则不能“向前加签”
*
@@ -763,25 +764,20 @@ public class BpmTaskServiceImpl implements BpmTaskService {
TaskEntityImpl taskEntity = (TaskEntityImpl) validateTask(userId, reqVO.getId());
// 向前加签和向后加签不能同时存在
if (StrUtil.isNotBlank(taskEntity.getScopeType())
- && !BpmTaskAddSignTypeEnum.AFTER_CHILDREN_TASK.getDesc().equals(taskEntity.getScopeType())) {
- // TODO @海:下面这个判断,是不是可以写上面这个判断后面?
- // TODO @海:一个小技巧,如果写不等于的时候,一般可以用 ObjectUtil.notEquals,这样少一层取反,理解起来简单点;
- if (!taskEntity.getScopeType().equals(reqVO.getType())) {
- throw exception(TASK_ADD_SIGN_TYPE_ERROR,
- BpmTaskAddSignTypeEnum.formatDesc(taskEntity.getScopeType()), BpmTaskAddSignTypeEnum.formatDesc(reqVO.getType()));
- }
+ && ObjectUtil.notEqual(BpmTaskAddSignTypeEnum.AFTER_CHILDREN_TASK.getType(), taskEntity.getScopeType())
+ && ObjectUtil.notEqual(taskEntity.getScopeType(), reqVO.getType())) {
+ throw exception(TASK_ADD_SIGN_TYPE_ERROR,
+ BpmTaskAddSignTypeEnum.formatDesc(taskEntity.getScopeType()), BpmTaskAddSignTypeEnum.formatDesc(reqVO.getType()));
}
// 同一个 key 的任务,审批人不重复
List taskList = taskService.createTaskQuery().processInstanceId(taskEntity.getProcessInstanceId())
.taskDefinitionKey(taskEntity.getTaskDefinitionKey()).list();
- // TODO @海:这里是不是 Task::getAssignee 解析成 List 下面会更简洁一点?
- List currentAssigneeList = convertList(taskList, Task::getAssignee);
- // TODO @海:frontAssigneeList 改成 addAssigneeList,新增的;避免和 front 前端这样界面耦合的名词哈;
- List frontAssigneeList = convertList(reqVO.getUserIdList(), String::valueOf);
- currentAssigneeList.retainAll(frontAssigneeList);
+ List currentAssigneeList = convertList(taskList, task -> NumberUtils.parseLong(task.getAssignee()));
+ // 保留交集在 currentAssigneeList 中
+ currentAssigneeList.retainAll(reqVO.getUserIdList());
if (CollUtil.isNotEmpty(currentAssigneeList)) {
- List userList = adminUserApi.getUserList(convertList(currentAssigneeList, NumberUtils::parseLong));
- throw exception(TASK_ADD_SIGN_USER_REPEAT, String.join(SymbolConstant.D, convertList(userList, AdminUserRespDTO::getNickname)));
+ List userList = adminUserApi.getUserList(currentAssigneeList);
+ throw exception(TASK_ADD_SIGN_USER_REPEAT, String.join(",", convertList(userList, AdminUserRespDTO::getNickname)));
}
return taskEntity;
}
@@ -792,32 +788,31 @@ public class BpmTaskServiceImpl implements BpmTaskService {
* @param addSingUserIdList 被加签的用户 ID
* @param taskEntity 被加签的任务
*/
- private void createSignSubTasks(List addSingUserIdList, TaskEntityImpl taskEntity) {
- // TODO @海:可以 if return;这样括号层级少一点;下面的 if (StrUtil.isNotBlank(addSignId)) { 也是类似;
- if (CollUtil.isNotEmpty(addSingUserIdList)) {
- // 创建加签人的新任务,全部基于 taskEntity 为父任务来创建
- addSingUserIdList.forEach(addSignId -> {
- if (StrUtil.isNotBlank(addSignId)) {
- createSubTask(taskEntity, addSignId);
- }
- });
+ private void createSignTask(List addSingUserIdList, TaskEntityImpl taskEntity) {
+ if (CollUtil.isEmpty(addSingUserIdList)) {
+ return;
+ }
+ // 创建加签人的新任务,全部基于 taskEntity 为父任务来创建
+ for (String addSignId : addSingUserIdList) {
+ if (StrUtil.isBlank(addSignId)) {
+ continue;
+ }
+ createSignTask(taskEntity, addSignId);
}
}
- // TODO @海:这个是不是命名上,要和 createSignSubTasks 保持一致?
/**
- * 创建子任务
+ * 创建加签子任务
*
* @param parentTask 父任务
* @param assignee 子任务的执行人
- * @return
*/
- private void createSubTask(TaskEntityImpl parentTask, String assignee) {
+ private void createSignTask(TaskEntityImpl parentTask, String assignee) {
// 1. 生成子任务
TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID());
- task = BpmTaskConvert.INSTANCE.convert(task,parentTask);
+ task = BpmTaskConvert.INSTANCE.convert(task, parentTask);
if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) {
- // 2.1 前加签,才设置审批人,否则设置 owner
+ // 2.1 前加签,设置审批人
task.setAssignee(assignee);
} else {
// 2.2.1 设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成
@@ -831,106 +826,144 @@ public class BpmTaskServiceImpl implements BpmTaskService {
@Override
@Transactional(rollbackFor = Exception.class)
- public void subSign(BpmTaskSubSignReqVO reqVO,Long userId) {
+ public void deleteSignTask(Long userId, BpmTaskSubSignReqVO reqVO) {
+ // 1.1 校验 task 可以被减签
Task task = validateSubSign(reqVO.getId());
- AdminUserRespDTO user = adminUserApi.getUser(userId);
+ // 1.2 校验取消人存在
AdminUserRespDTO cancelUser = null;
- if(StrUtil.isNotBlank(task.getAssignee())){
+ if (StrUtil.isNotBlank(task.getAssignee())) {
cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee()));
}
- if(cancelUser == null && StrUtil.isNotBlank(task.getOwner())){
+ if (cancelUser == null && StrUtil.isNotBlank(task.getOwner())) {
cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner()));
}
- Assert.notNull(cancelUser,"任务中没有所有者和审批人,数据错误");
- //1. 获取所有需要删除的任务 ID ,包含当前任务和所有子任务
- List allTaskIdList = getAllChildTaskIds(task.getId());
- //2. 删除任务和所有子任务
- taskService.deleteTasks(allTaskIdList);
- //3. 修改扩展表状态为取消
- taskExtMapper.updateBatchByTaskIdList(allTaskIdList,new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.CANCEL.getResult())
- .setReason(StrUtil.format("由于{}操作[减签],任务被取消",user.getNickname())));
- //4.记录日志到父任务中 先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录
- String comment = StrUtil.format("{}操作了【减签】,审批人{}的任务被取消",user.getNickname(),cancelUser.getNickname());
- taskService.addComment(task.getParentTaskId(),task.getProcessInstanceId(),
- BpmCommentTypeEnum.SUB_SIGN.getResult().toString(),comment);
- //5. 处理当前任务的父任务
- this.handleParentTask(task);
+ Assert.notNull(cancelUser, "任务中没有所有者和审批人,数据错误");
+ // 2. 删除任务和对应子任务
+ // 2.1 获取所有需要删除的任务 ID ,包含当前任务和所有子任务
+ List allTaskIdList = getAllChildTaskIds(task.getId());
+ // 2.2 删除任务和所有子任务
+ taskService.deleteTasks(allTaskIdList);
+ // 2.3 修改扩展表状态为取消
+ AdminUserRespDTO user = adminUserApi.getUser(userId);
+ taskExtMapper.updateBatchByTaskIdList(allTaskIdList, new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.CANCEL.getResult())
+ .setReason(StrUtil.format("由于{}操作[减签],任务被取消", user.getNickname())));
+
+ // 3. 记录日志到父任务中。先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录
+ String comment = StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getComment(), user.getNickname(), cancelUser.getNickname());
+ taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(),
+ BpmCommentTypeEnum.SUB_SIGN.getType().toString(), comment);
+
+ // 4. 处理当前任务的父任务
+ handleParentTask(task);
}
/**
* 校验任务是否能被减签
+ *
* @param id 任务ID
* @return 任务信息
*/
private Task validateSubSign(String id) {
Task task = validateTaskExist(id);
- //必须有parentId
- if(StrUtil.isEmpty(task.getParentTaskId())){
+
+ // 必须有 scopeType
+ String scopeType = task.getScopeType();
+ if (StrUtil.isEmpty(scopeType)) {
+ throw exception(TASK_SUB_SIGN_NO_PARENT);
+ }
+ // 并且值为 向前和向后加签
+ if (!validateSignType(scopeType)) {
throw exception(TASK_SUB_SIGN_NO_PARENT);
}
return task;
}
+ /**
+ * 判断当前类型是否为加签
+ * @param scopeType 任务的 scopeType
+ * @return 当前 scopeType 为加签则返回 true
+ */
+ private boolean validateSignType(String scopeType){
+ return StrUtil.equalsAny(scopeType,BpmTaskAddSignTypeEnum.BEFORE.getType(),scopeType, BpmTaskAddSignTypeEnum.AFTER.getType());
+ }
+
/**
* 获取所有要被取消的删除的任务 ID 集合
+ *
* @param parentTaskId 父级任务ID
* @return 所有任务ID
*/
public List getAllChildTaskIds(String parentTaskId) {
List allChildTaskIds = new ArrayList<>();
- //1. 先将自己放入
- allChildTaskIds.add(parentTaskId);
- //2. 递归获取子级
- recursiveGetChildTaskIds(parentTaskId, allChildTaskIds);
+ // 1. 递归获取子级
+ Stack stack = new Stack<>();
+ // 1.1 将根任务ID入栈
+ stack.push(parentTaskId);
+ //控制遍历的次数不超过 Byte.MAX_VALUE,避免脏数据造成死循环
+ int count = 0;
+ // TODO @海:< 的前后空格,要注意哈;
+ while (!stack.isEmpty() && count childrenTaskIdList = getChildrenTaskIdList(taskId);
+ if (CollUtil.isNotEmpty(childrenTaskIdList)) {
+ for (String childTaskId : childrenTaskIdList) {
+ // 1.5 将子任务ID入栈,以便后续处理
+ stack.push(childTaskId);
+ }
+ }
+ count++;
+ }
return allChildTaskIds;
}
/**
- * 递归处理子级任务
- * @param taskId 当前任务ID
- * @param taskIds 结果
+ * 获取指定父级任务的所有子任务 ID 集合
+ *
+ * @param parentTaskId 父任务 ID
+ * @return 所有子任务的 ID 集合
*/
- private void recursiveGetChildTaskIds(String taskId, List taskIds) {
- List childrenTaskIdList = getChildrenTaskIdList(taskId);
- for (String childTaskId : childrenTaskIdList) {
- taskIds.add(childTaskId); // 将子任务的ID添加到集合中
- recursiveGetChildTaskIds(childTaskId, taskIds); // 递归获取子任务的子任务
- }
+ private List getChildrenTaskIdList(String parentTaskId) {
+ return convertList(getChildrenTaskList0(parentTaskId), Task::getId);
}
/**
* 获取指定父级任务的所有子任务 ID 集合
+ *
* @param parentTaskId 父任务 ID
* @return 所有子任务的 ID 集合
*/
- private List getChildrenTaskIdList(String parentTaskId){
+ private List getChildrenTaskList0(String parentTaskId) {
String tableName = managementService.getTableName(TaskEntity.class);
- String sql = "select ID_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}";
- List childrenTaskList = taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list();
- return convertList(childrenTaskList, Task::getId);
+ // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询
+ String sql = "select ID_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}";
+ return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list();
}
+
@Override
- public List getChildrenTaskList(String taskId){
- List childrenTaskIdList = getChildrenTaskIdList(taskId);
- if(CollUtil.isEmpty(childrenTaskIdList)){
+ public List getChildrenTaskList(String parentId) {
+ // 1. 只查询进行中的任务 后加签的任务,可能不存在 assignee,所以还需要查询 owner
+ List taskList = getChildrenTaskList0(parentId);
+ if (CollUtil.isEmpty(taskList)) {
return Collections.emptyList();
}
- //1. 只查询进行中的任务
- List bpmTaskExtDOList = taskExtMapper.selectProcessListByTaskIds(childrenTaskIdList);
- //2. 后加签的任务,可能不存在 assignee,所以还需要查询 owner
- List taskList = taskService.createTaskQuery().taskIds(childrenTaskIdList).list();
- Map idTaskMap = convertMap(taskList, TaskInfo::getId);
- //3. 将 owner 和 assignee 统一到一个集合中
- List userIds = taskList.stream()
- .flatMap(control ->
- Stream.of(control.getAssignee(), control.getOwner())
- .filter(Objects::nonNull))
- .distinct()
- .map(NumberUtils::parseLong)
- .collect(Collectors.toList());
+ List childrenTaskIdList = convertList(taskList, Task::getId);
+
+ // 2.1 将 owner 和 assignee 统一到一个集合中
+ List userIds = convertListByFlatMap(taskList, control ->
+ Stream.of(NumberUtils.parseLong(control.getAssignee()), NumberUtils.parseLong(control.getOwner()))
+ .filter(Objects::nonNull));
+ // 2.2 组装数据
Map userMap = adminUserApi.getUserMap(userIds);
- return BpmTaskConvert.INSTANCE.convertList(bpmTaskExtDOList,userMap,idTaskMap);
+ List taskExtList = taskExtMapper.selectProcessListByTaskIds(childrenTaskIdList);
+ Map idTaskMap = convertMap(taskList, TaskInfo::getId);
+ return BpmTaskConvert.INSTANCE.convertList(taskExtList, userMap, idTaskMap);
}
+
}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java
new file mode 100644
index 000000000..0e412ee9b
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/DictTypeConstants.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.crm.enums;
+
+/**
+ * CRM 字典类型的枚举类
+ *
+ * @author 芋道源码
+ */
+public interface DictTypeConstants {
+
+ // ========== CRM 模块 ==========
+ String CRM_CUSTOMER_INDUSTRY = "crm_customer_industry"; // CRM 客户所属行业
+ String CRM_CUSTOMER_LEVEL = "crm_customer_level"; // CRM 客户等级
+ String CRM_CUSTOMER_SOURCE = "crm_customer_source"; // CRM 客户来源
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
index 3efc2dcef..d411c236b 100644
--- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -11,12 +11,16 @@ public interface ErrorCodeConstants {
// ========== 合同管理 1-020-000-000 ==========
ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
+ ErrorCode CONTRACT_TRANSFER_FAIL_PERMISSION_DENIED = new ErrorCode(1_020_000_001, "合同转移失败,原因:没有转移权限");
+ ErrorCode CONTRACT_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS = new ErrorCode(1_020_000_002, "合同转移失败,原因:负责人不存在");
// ========== 线索管理 1-020-001-000 ==========
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
// ========== 商机管理 1-020-002-000 ==========
ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机不存在");
+ ErrorCode BUSINESS_TRANSFER_FAIL_PERMISSION_DENIED = new ErrorCode(1_020_002_001, "商机转移失败,原因:没有转移权限");
+ ErrorCode BUSINESS_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS = new ErrorCode(1_020_002_002, "商机转移失败,原因:负责人不存在");
// TODO @lilleo:商机状态、商机类型,都单独错误码段
@@ -25,6 +29,8 @@ public interface ErrorCodeConstants {
// ========== 联系人管理 1-020-003-000 ==========
ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
+ ErrorCode CONTACT_TRANSFER_FAIL_PERMISSION_DENIED = new ErrorCode(1_020_003_001, "联系人转移失败,原因:没有转移权限");
+ ErrorCode CONTACT_TRANSFER_FAIL_OWNER_USER_NOT_EXISTS = new ErrorCode(1_020_003_002, "联系人转移失败,原因:负责人不存在");
// ========== 回款管理 1-020-004-000 ==========
ErrorCode RECEIVABLE_NOT_EXISTS = new ErrorCode(1_020_004_000, "回款管理不存在");
diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java
new file mode 100644
index 000000000..f58028314
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/customer/CrmCustomerLevelEnum.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.crm.enums.customer;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * CRM 客户等级
+ *
+ * @author Wanwan
+ */
+@Getter
+@AllArgsConstructor
+public enum CrmCustomerLevelEnum implements IntArrayValuable {
+
+ IMPORTANT(1, "A(重点客户)"),
+ GENERAL(2, "B(普通客户)"),
+ LOW_PRIORITY(3, "C(非优先客户)");
+
+ public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerLevelEnum::getStatus).toArray();
+
+ // TODO @wanwan:这里的 status 字段,可以考虑改成 level
+ /**
+ * 状态
+ */
+ private final Integer status;
+ /**
+ * 状态名
+ */
+ private final String name;
+
+ @Override
+ public int[] array() {
+ return ARRAYS;
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
index d517a9f91..8096176c9 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
@@ -23,6 +23,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 商机")
@RestController
@@ -80,11 +81,19 @@ public class CrmBusinessController {
@PreAuthorize("@ss.hasPermission('crm:business:export')")
@OperateLog(type = EXPORT)
public void exportBusinessExcel(@Valid CrmBusinessExportReqVO exportReqVO,
- HttpServletResponse response) throws IOException {
+ HttpServletResponse response) throws IOException {
List list = businessService.getBusinessList(exportReqVO);
// 导出 Excel
List datas = CrmBusinessConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "商机.xls", "数据", CrmBusinessExcelVO.class, datas);
}
+ @PutMapping("/transfer")
+ @Operation(summary = "商机转移")
+ @PreAuthorize("@ss.hasPermission('crm:business:update')")
+ public CommonResult transfer(@Valid @RequestBody CrmBusinessTransferReqVO reqVO) {
+ businessService.businessTransfer(reqVO, getLoginUserId());
+ return success(true);
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessCreateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessCreateReqVO.java
index f743c8469..968a105c8 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessCreateReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessCreateReqVO.java
@@ -5,12 +5,21 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
+import java.util.Set;
+
@Schema(description = "管理后台 - 商机创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmBusinessCreateReqVO extends CrmBusinessBaseVO {
+ @Schema(description = "只读权限的用户编号数组")
+ private Set roUserIds;
+
+ @Schema(description = "读写权限的用户编号数组")
+ private Set rwUserIds;
+
+
// TODO @ljileo:新建的时候,应该可以传递添加的产品;
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessTransferReqVO.java
new file mode 100644
index 000000000..993e4750e
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessTransferReqVO.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.crm.controller.admin.business.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 商机转移 Request VO")
+@Data
+public class CrmBusinessTransferReqVO {
+
+ @Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "联系人编号不能为空")
+ private Long id;
+
+ @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "新负责人的用户编号不能为空")
+ private Long ownerUserId; // 新的负责人
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessUpdateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessUpdateReqVO.java
index f137d4c5b..2929535cf 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessUpdateReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/CrmBusinessUpdateReqVO.java
@@ -6,6 +6,7 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
+import java.util.Set;
@Schema(description = "管理后台 - 商机更新 Request VO")
@Data
@@ -17,6 +18,13 @@ public class CrmBusinessUpdateReqVO extends CrmBusinessBaseVO {
@NotNull(message = "主键不能为空")
private Long id;
+ @Schema(description = "只读权限的用户编号数组")
+ private Set roUserIds;
+
+ @Schema(description = "读写权限的用户编号数组")
+ private Set rwUserIds;
+
+
// TODO @ljileo:修改的时候,应该可以传递添加的产品;
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/ContactController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/ContactController.java
index bafec03c1..f28d27ef8 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/ContactController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/ContactController.java
@@ -1,32 +1,30 @@
package cn.iocoder.yudao.module.crm.controller.admin.contact;
-import org.springframework.web.bind.annotation.*;
-import javax.annotation.Resource;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.security.access.prepost.PreAuthorize;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.Operation;
-
-import javax.validation.constraints.*;
-import javax.validation.*;
-import javax.servlet.http.*;
-import java.util.*;
-import java.io.IOException;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
-
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
-import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*;
-
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
-import cn.iocoder.yudao.module.crm.dal.dataobject.contact.ContactDO;
import cn.iocoder.yudao.module.crm.convert.contact.ContactConvert;
+import cn.iocoder.yudao.module.crm.dal.dataobject.contact.ContactDO;
import cn.iocoder.yudao.module.crm.service.contact.ContactService;
+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.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - crm联系人")
@RestController
@@ -99,4 +97,12 @@ public class ContactController {
ExcelUtils.write(response, "crm联系人.xls", "数据", ContactExcelVO.class, datas);
}
+ @PutMapping("/transfer")
+ @Operation(summary = "联系人转移")
+ @PreAuthorize("@ss.hasPermission('crm:contact:update')")
+ public CommonResult transfer(@Valid @RequestBody CrmContactTransferReqVO reqVO) {
+ contactService.contactTransfer(reqVO, getLoginUserId());
+ return success(true);
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactCreateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactCreateReqVO.java
index 424d945dc..64ea71084 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactCreateReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactCreateReqVO.java
@@ -1,9 +1,11 @@
package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
-import lombok.*;
-import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
-import javax.validation.constraints.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.Set;
@Schema(description = "管理后台 - crm联系人创建 Request VO")
@Data
@@ -11,4 +13,10 @@ import javax.validation.constraints.*;
@ToString(callSuper = true)
public class ContactCreateReqVO extends ContactBaseVO {
+ @Schema(description = "只读权限的用户编号数组")
+ private Set roUserIds;
+
+ @Schema(description = "读写权限的用户编号数组")
+ private Set rwUserIds;
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactUpdateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactUpdateReqVO.java
index 19db67297..60afd06f2 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactUpdateReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/ContactUpdateReqVO.java
@@ -1,9 +1,11 @@
package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-import java.util.*;
-import javax.validation.constraints.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.Set;
@Schema(description = "管理后台 - crm联系人更新 Request VO")
@Data
@@ -14,4 +16,10 @@ public class ContactUpdateReqVO extends ContactBaseVO {
@Schema(description = "主键", example = "23210")
private Long id;
+ @Schema(description = "只读权限的用户编号数组")
+ private Set roUserIds;
+
+ @Schema(description = "读写权限的用户编号数组")
+ private Set rwUserIds;
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java
new file mode 100644
index 000000000..90acc5645
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactTransferReqVO.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contact.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 联系人转移 Request VO")
+@Data
+public class CrmContactTransferReqVO {
+
+ @Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "联系人编号不能为空")
+ private Long id;
+
+ @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "新负责人的用户编号不能为空")
+ private Long ownerUserId; // 新的负责人
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/ContractController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/ContractController.java
index fd8b14afd..4898eb6c1 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/ContractController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/ContractController.java
@@ -23,6 +23,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 合同")
@RestController
@@ -86,4 +87,13 @@ public class ContractController {
ExcelUtils.write(response, "合同.xls", "数据", ContractExcelVO.class, datas);
}
+ @PutMapping("/transfer")
+ @Operation(summary = "合同转移")
+ @PreAuthorize("@ss.hasPermission('crm:contract:update')")
+ public CommonResult transfer(@Valid @RequestBody CrmContractTransferReqVO reqVO) {
+ contractService.contractTransfer(reqVO, getLoginUserId());
+ return success(true);
+ }
+
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractCreateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractCreateReqVO.java
index 38c95d23f..7b8c561b0 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractCreateReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractCreateReqVO.java
@@ -5,6 +5,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
+import java.util.Set;
+
@Schema(description = "管理后台 - 合同创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@@ -12,9 +14,9 @@ import lombok.ToString;
public class ContractCreateReqVO extends ContractBaseVO {
@Schema(description = "只读权限的用户编号数组")
- private String roUserIds;
+ private Set roUserIds;
@Schema(description = "读写权限的用户编号数组")
- private String rwUserIds;
+ private Set rwUserIds;
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractUpdateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractUpdateReqVO.java
index f38ac7677..e6f305ce4 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractUpdateReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/ContractUpdateReqVO.java
@@ -6,6 +6,7 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
+import java.util.Set;
@Schema(description = "管理后台 - 合同更新 Request VO")
@Data
@@ -18,9 +19,9 @@ public class ContractUpdateReqVO extends ContractBaseVO {
private Long id;
@Schema(description = "只读权限的用户编号数组")
- private String roUserIds;
+ private Set roUserIds;
@Schema(description = "读写权限的用户编号数组")
- private String rwUserIds;
+ private Set rwUserIds;
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractTransferReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractTransferReqVO.java
new file mode 100644
index 000000000..5dfa5fe0a
--- /dev/null
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractTransferReqVO.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.crm.controller.admin.contract.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Schema(description = "管理后台 - 合同转移 Request VO")
+@Data
+public class CrmContractTransferReqVO {
+
+ @Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "合同编号不能为空")
+ private Long id;
+
+ @Schema(description = "新负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10430")
+ @NotNull(message = "新负责人的用户编号不能为空")
+ private Long ownerUserId; // 新的负责人
+
+}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
index 7c91b4c1a..78ae1a20d 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -19,7 +19,6 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
-import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java
index 0db54fa1f..0e4d4463d 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerBaseVO.java
@@ -1,13 +1,16 @@
package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.framework.common.validation.Telephone;
+import cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLevelEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
+import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
-import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@@ -23,15 +26,15 @@ public class CrmCustomerBaseVO {
@NotEmpty(message = "客户名称不能为空")
private String name;
- // TODO wanwan:这个字段应该只有 RespVO 会有;创建和修改不传递;
- @Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
- @NotNull(message = "跟进状态不能为空")
- private Boolean followUpStatus;
+ @Schema(description = "所属行业", example = "1")
+ private Integer industryId;
- // TODO wanwan:这个字段应该只有 RespVO 会有;创建和修改不传递;
- @Schema(description = "锁定状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
- @NotNull(message = "锁定状态不能为空")
- private Boolean lockStatus;
+ @Schema(description = "客户等级", example = "2")
+ @InEnum(CrmCustomerLevelEnum.class)
+ private Integer level;
+
+ @Schema(description = "客户来源", example = "3")
+ private Integer source;
@Schema(description = "手机", example = "18000000000")
@Mobile
@@ -44,32 +47,32 @@ public class CrmCustomerBaseVO {
@Schema(description = "网址", example = "https://www.baidu.com")
private String website;
+ @Schema(description = "QQ", example = "123456789")
+ @Size(max = 20, message = "QQ长度不能超过 20 个字符")
+ private String qq;
+
+ @Schema(description = "wechat", example = "123456789")
+ @Size(max = 255, message = "微信长度不能超过 255 个字符")
+ private String wechat;
+
+ @Schema(description = "email", example = "123456789@qq.com")
+ @Email(message = "邮箱格式不正确")
+ @Size(max = 255, message = "邮箱长度不能超过 255 个字符")
+ private String email;
+
+ @Schema(description = "客户描述", example = "任意文字")
+ @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
+ private String description;
+
@Schema(description = "备注", example = "随便")
private String remark;
- // TODO wanwan:这个字段应该只有 RespVO 会有;创建和修改不传递;因为它会在“移交”里面做哈
- @Schema(description = "负责人的用户编号", example = "25682")
- @NotNull(message = "负责人不能为空")
- private Long ownerUserId;
-
@Schema(description = "地区编号", example = "20158")
private Long areaId;
@Schema(description = "详细地址", example = "北京市海淀区")
private String detailAddress;
- // TODO @芋艿:longitude、latitude 这两个字段删除;
- @Schema(description = "地理位置经度", example = "116.40341")
- private String longitude;
-
- @Schema(description = "地理位置维度", example = "39.92409")
- private String latitude;
-
- // TODO wanwan:这个字段应该只有 RespVO 会有;创建和修改不传递;
- @Schema(description = "最后跟进时间")
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- private LocalDateTime contactLastTime;
-
@Schema(description = "下次联系时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime contactNextTime;
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerCreateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerCreateReqVO.java
index a3ed7ef13..84462ddfa 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerCreateReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerCreateReqVO.java
@@ -11,12 +11,6 @@ import lombok.ToString;
@ToString(callSuper = true)
public class CrmCustomerCreateReqVO extends CrmCustomerBaseVO {
- // TODO @wanwan:类型应该是传递 List; 不过这个字段,默认新建的时候不传递,在“移交”功能里管理
- @Schema(description = "只读权限的用户编号数组")
- private String roUserIds;
-
- // TODO @wanwan:类型应该是传递 List; 不过这个字段,默认新建的时候不传递,在“移交”功能里管理
- @Schema(description = "读写权限的用户编号数组")
- private String rwUserIds;
+ // TODO @wanwan:负责人
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExcelVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExcelVO.java
index 3e37c30e7..0fee3df4c 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExcelVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExcelVO.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@@ -35,6 +36,18 @@ public class CrmCustomerExcelVO {
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean dealStatus;
+ @ExcelProperty(value = "所属行业", converter = DictConvert.class)
+ @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY)
+ private Integer industryId;
+
+ @ExcelProperty(value = "客户等级", converter = DictConvert.class)
+ @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL)
+ private Integer level;
+
+ @ExcelProperty(value = "客户来源", converter = DictConvert.class)
+ @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE)
+ private Integer source;
+
@ExcelProperty("手机")
private String mobile;
@@ -44,6 +57,18 @@ public class CrmCustomerExcelVO {
@ExcelProperty("网址")
private String website;
+ @ExcelProperty("QQ")
+ private String qq;
+
+ @ExcelProperty("wechat")
+ private String wechat;
+
+ @ExcelProperty("email")
+ private String email;
+
+ @ExcelProperty("客户描述")
+ private String description;
+
@ExcelProperty("备注")
private String remark;
@@ -56,12 +81,6 @@ public class CrmCustomerExcelVO {
@ExcelProperty("详细地址")
private String detailAddress;
- @ExcelProperty("地理位置经度")
- private String longitude;
-
- @ExcelProperty("地理位置维度")
- private String latitude;
-
@ExcelProperty("最后跟进时间")
private LocalDateTime contactLastTime;
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExportReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExportReqVO.java
index 15776aa42..03a3f2d4d 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExportReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerExportReqVO.java
@@ -14,10 +14,4 @@ public class CrmCustomerExportReqVO {
@Schema(description = "手机", example = "18000000000")
private String mobile;
- @Schema(description = "电话", example = "18000000000")
- private String telephone;
-
- @Schema(description = "网址", example = "https://www.baidu.com")
- private String website;
-
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java
index 7cb89adbc..8bf2018f1 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java
@@ -18,13 +18,5 @@ public class CrmCustomerPageReqVO extends PageParam {
@Schema(description = "手机", example = "18000000000")
private String mobile;
- // TODO @wanwan:这个字段不需要哈
- @Schema(description = "电话", example = "18000000000")
- private String telephone;
-
- // TODO @wanwan:这个字段不需要哈
- @Schema(description = "网址", example = "https://www.baidu.com")
- private String website;
-
// TODO @芋艿:场景;
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java
index b5c9850bc..bdcf28f2c 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java
@@ -4,9 +4,12 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
@Schema(description = "管理后台 - 客户 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@@ -16,9 +19,22 @@ public class CrmCustomerRespVO extends CrmCustomerBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
private Long id;
+ @Schema(description = "跟进状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ private Boolean followUpStatus;
+
+ @Schema(description = "锁定状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ private Boolean lockStatus;
+
@Schema(description = "成交状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean dealStatus;
+ @Schema(description = "负责人的用户编号", example = "25682")
+ private Long ownerUserId;
+
+ @Schema(description = "最后跟进时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime contactLastTime;
+
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerUpdateReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerUpdateReqVO.java
index 59d440a35..545643fd9 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerUpdateReqVO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerUpdateReqVO.java
@@ -17,11 +17,4 @@ public class CrmCustomerUpdateReqVO extends CrmCustomerBaseVO {
@NotNull(message = "编号不能为空")
private Long id;
- // TODO @wanwan:下面两个字段,同 CrmCustomerCreateReqVO
- @Schema(description = "只读权限的用户编号数组")
- private String roUserIds;
-
- @Schema(description = "读写权限的用户编号数组")
- private String rwUserIds;
-
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java
index 72aad68de..2eabf7d4d 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/business/CrmBusinessConvert.java
@@ -1,13 +1,14 @@
package cn.iocoder.yudao.module.crm.convert.business;
-import java.util.*;
-
+import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Set;
/**
* 商机 Convert
@@ -29,4 +30,12 @@ public interface CrmBusinessConvert {
List convertList02(List list);
+ default CrmBusinessDO convert(CrmBusinessDO business, CrmBusinessTransferReqVO reqVO, Long userId) {
+ Set rwUserIds = business.getRwUserIds();
+ rwUserIds.removeIf(item -> ObjUtil.equal(item, userId)); // 移除老负责人
+ rwUserIds.add(reqVO.getOwnerUserId()); // 读写权限加入新的负人
+ return new CrmBusinessDO().setId(business.getId()).setOwnerUserId(reqVO.getOwnerUserId()) // 设置新负责人
+ .setRwUserIds(rwUserIds);
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/ContactConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/ContactConvert.java
index 3892806c0..0d1329819 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/ContactConvert.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contact/ContactConvert.java
@@ -1,13 +1,14 @@
package cn.iocoder.yudao.module.crm.convert.contact;
-import java.util.*;
-
+import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.crm.controller.admin.contact.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.ContactDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+import java.util.Set;
/**
* crm联系人 Convert
@@ -31,4 +32,12 @@ public interface ContactConvert {
List convertList02(List list);
+ default ContactDO convert(ContactDO contact, CrmContactTransferReqVO reqVO, Long userId) {
+ Set rwUserIds = contact.getRwUserIds();
+ rwUserIds.removeIf(item -> ObjUtil.equal(item, userId)); // 移除老负责人
+ rwUserIds.add(reqVO.getOwnerUserId()); // 读写权限加入新的负人
+ return new ContactDO().setId(contact.getId()).setOwnerUserId(reqVO.getOwnerUserId()) // 设置新负责人
+ .setRwUserIds(rwUserIds);
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/ContractConvert.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/ContractConvert.java
index 906dd7562..d7547d4b8 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/ContractConvert.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/contract/ContractConvert.java
@@ -1,15 +1,14 @@
package cn.iocoder.yudao.module.crm.convert.contract;
+import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.ContractCreateReqVO;
-import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.ContractExcelVO;
-import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.ContractRespVO;
-import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.ContractUpdateReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.ContractDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
+import java.util.Set;
/**
* 合同 Convert
@@ -33,4 +32,12 @@ public interface ContractConvert {
List convertList02(List list);
+ default ContractDO convert(ContractDO contract, CrmContractTransferReqVO reqVO, Long userId) {
+ Set rwUserIds = contract.getRwUserIds();
+ rwUserIds.removeIf(item -> ObjUtil.equal(item, userId)); // 移除老负责人
+ rwUserIds.add(reqVO.getOwnerUserId()); // 读写权限加入新的负人
+ return new ContractDO().setId(contract.getId()).setOwnerUserId(reqVO.getOwnerUserId()) // 设置新负责人
+ .setRwUserIds(rwUserIds);
+ }
+
}
diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java
index 2d6454838..23d7ddb9a 100644
--- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java
+++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java
@@ -1,14 +1,17 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.business;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler;
import cn.iocoder.yudao.module.crm.dal.dataobject.businessstatus.CrmBusinessStatusDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.businessstatustype.CrmBusinessStatusTypeDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
+import java.util.Set;
/**
* 商机 DO
@@ -85,16 +88,14 @@ public class CrmBusinessDO extends BaseDO {
private Long ownerUserId;
/**
* 只读权限的用户编号数组
- *
- * TODO @lijie:应该是 List