diff --git a/jeecg-boot-module/jeecg-module-flow/pom.xml b/jeecg-boot-module/jeecg-module-flow/pom.xml
new file mode 100644
index 0000000..608b87b
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ jeecg-boot-module
+ org.jeecgframework.boot
+ ${jeecgProjectVersion}
+
+
+ jeecg-module-flow
+
+
+ 21
+ 21
+ UTF-8
+
+
+
+
+ org.jeecgframework.boot
+ jeecg-boot-base-core
+
+
+ org.jeecgframework.boot
+ jeecg-system-local-api
+
+
+ org.jeecgframework.boot
+ jeecg-boot-module-bpm-flowable
+ 3.8.0
+ compile
+
+
+ org.jeecgframework.boot
+ jeecg-system-biz
+
+
+
+
\ No newline at end of file
diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/Main.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/Main.java
new file mode 100644
index 0000000..28f7a57
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/Main.java
@@ -0,0 +1,17 @@
+package org.jeecg;
+
+//TIP 要运行代码,请按 或
+// 点击装订区域中的 图标。
+public class Main {
+ public static void main(String[] args) {
+ //TIP 当文本光标位于高亮显示的文本处时按
+ // 查看 IntelliJ IDEA 建议如何修正。
+ System.out.printf("Hello and welcome!");
+
+ for (int i = 1; i <= 5; i++) {
+ //TIP 按 开始调试代码。我们已经设置了一个 断点
+ // 但您始终可以通过按 添加更多断点。
+ System.out.println("i = " + i);
+ }
+ }
+}
\ No newline at end of file
diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakConfig.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakConfig.java
new file mode 100644
index 0000000..d9369ef
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakConfig.java
@@ -0,0 +1,33 @@
+package org.jeecg.xispeak;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "flow-biz")
+public class XiSpeakConfig {
+ /**
+ * 对应 yml 中的 xi-speak 节点
+ * Spring 会自动将中划线命名映射为驼峰命名
+ */
+ private XiSpeakProperties xiSpeak;
+
+
+ @Data
+ public static class XiSpeakProperties {
+ /** 对应 bg-dept-id */
+ private String bgDeptId;
+ /** 对应 ld-role-id */
+ private String ldRoleId;
+ /** 对应 need-sdw-approve-code */
+ private String needSdwApproveCode;
+ /** 对应 json-data-key */
+ private String jsonDataKey;
+ /** 对应 impl-dept-key */
+ private String implDeptKey;
+ /** 对应impl-dept-collection-used*/
+ private String implDeptCollectionUsedKey;
+ }
+}
diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakFlow.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakFlow.java
new file mode 100644
index 0000000..dd147da
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakFlow.java
@@ -0,0 +1,129 @@
+package org.jeecg.xispeak;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.util.StringUtils;
+import org.flowable.engine.RuntimeService;
+import org.jeecg.common.system.api.ISysBaseAPI;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Component("xiSpeakFlow")
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class XiSpeakFlow {
+
+ private final XiSpeakConfig xiSpeakConfig;
+ private final ISysBaseAPI iSysBaseAPI;
+ private final RuntimeService runtimeService;
+
+ public List getBgDeptLdUserIdList() {
+ // 1. 安全获取配置,防止 NPE
+ XiSpeakConfig.XiSpeakProperties props = xiSpeakConfig.getXiSpeak();
+ if (props == null || !StringUtils.hasText(props.getBgDeptId())) {
+ log.error("流程配置缺失: [flow-biz.xi-speak.bg-dept-id] 未在 YAML 中定义");
+ return Collections.emptyList();
+ }
+
+ // 2. 调用接口获取数据
+ log.info("正在查询部门: {} 下的角色: {} 的用户列表", props.getBgDeptId(), props.getLdRoleId());
+ List userList = iSysBaseAPI.getUsersListByDeptIdAndRoleIdLocalApi(
+ props.getBgDeptId(),
+ props.getLdRoleId()
+ );
+ log.info("查询到的用户为: {}", userList);
+ return userList;
+ }
+
+ public Integer getNeedSdwApproval(Object jsonData) {
+ XiSpeakConfig.XiSpeakProperties props = xiSpeakConfig.getXiSpeak();
+ if (jsonData == null || org.apache.commons.lang.StringUtils.isBlank(props.getNeedSdwApproveCode())) {
+ return 0;
+ }
+ String codeName = props.getNeedSdwApproveCode();
+ if (org.apache.commons.lang.StringUtils.isBlank(codeName)) return 0;
+ try {
+ JSONObject data;
+ if (jsonData instanceof JSONObject) {
+ data = (JSONObject) jsonData;
+ } else if (jsonData instanceof String) {
+ if (org.apache.commons.lang.StringUtils.isBlank((String) jsonData)) {
+ return 0;
+ }
+ data = JSONObject.parseObject((String) jsonData);
+ } else {
+ data = JSONObject.parseObject(JSONObject.toJSONString(jsonData));
+ }
+ log.info("needSdwApproval结果为{}", data.getInteger(codeName));
+ return data == null ? 0 : data.getInteger(codeName);
+ } catch (Exception e) {
+ log.warn("getJsondata parse failed, codeName={}, jsonData={}", codeName, jsonData, e);
+ return 0;
+ }
+ }
+
+ public List getImplDeptLdsList(String deptId) {
+ return iSysBaseAPI.getUsersListByDeptIdAndRoleIdLocalApi(deptId, xiSpeakConfig.getXiSpeak().getLdRoleId());
+ }
+
+ public int getListSize(String jsonData){
+ if (org.apache.commons.lang3.StringUtils.isEmpty(jsonData)) {
+ return 0;
+ }
+ return Math.toIntExact(Arrays.stream(jsonData.split(","))
+ .map(String::trim)
+ .filter(org.apache.commons.lang3.StringUtils::isNotEmpty)
+ .count());
+ }
+
+// List getImplDeptWorker(String JG_LOCAL_PROCESS_ID,String deptId){
+// runtimeService.get
+// }
+
+ public Integer getImplDeptNums(String jsonData) {
+ String codeName = xiSpeakConfig.getXiSpeak().getImplDeptKey();
+ if (jsonData == null || org.apache.commons.lang.StringUtils.isEmpty(codeName)) {
+ return 0;
+ }
+
+ // 1. 统一转成 JSONObject
+ JSONObject json;
+ json = JSON.parseObject((String) jsonData);
+
+ if (json == null || !json.containsKey(codeName)) {
+ return 0;
+ }
+
+ // 2. 获取原始对象进行兼容性处理
+ Object value = json.get(codeName);
+ if (value == null) {
+ return 0;
+ }
+
+ List resultList = new ArrayList<>();
+
+ if (value instanceof Collection) {
+ // 情况 A: 本身就是集合/JSON数组
+ resultList.addAll(json.getJSONArray(codeName).toJavaList(String.class));
+ } else if (value instanceof String) {
+ // 情况 B: 是逗号分隔的字符串 "ID1,ID2,ID3"
+ String str = (String) value;
+ if (org.apache.commons.lang.StringUtils.isNotEmpty(str)) {
+ // 使用 split 拆分,并过滤掉空格和空字符串
+ String[] split = str.split(",");
+ for (String s : split) {
+ if (org.apache.commons.lang.StringUtils.isNotEmpty(s.trim())) {
+ resultList.add(s.trim());
+ }
+ }
+ }
+ }
+
+ return resultList.size();
+ }
+}
diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/listener/AfterSDWApproveHqListener.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/listener/AfterSDWApproveHqListener.java
new file mode 100644
index 0000000..9d13a44
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/listener/AfterSDWApproveHqListener.java
@@ -0,0 +1,118 @@
+package org.jeecg.modules.extbpm.listener.execution;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.modules.extbpm.process.common.expression.FlowNodeExpression;
+import lombok.RequiredArgsConstructor;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.delegate.ExecutionListener;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
+import org.jeecg.xispeak.XiSpeakConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Component("AfterSDWApproveHqListener")
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class AfterSDWApproveHqListener implements ExecutionListener {
+
+ private static final long serialVersionUID = 1L;
+
+ private final FlowNodeExpression flowNodeExpression;
+ @Autowired
+ private RuntimeService runtimeService;
+
+ private final XiSpeakConfig xiSpeakConfig;
+
+ @Override
+ public void notify(DelegateExecution execution) {
+
+ XiSpeakConfig.XiSpeakProperties props = xiSpeakConfig.getXiSpeak();
+ //RuntimeService runtimeService = SpringContextUtils.getBean(RuntimeService.class);
+ String mainProcessId = (String)execution.getProcessInstanceId();
+ String url = (String)execution.getVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL);
+
+ // 打印主流程里所有的变量名,看看有没有 json_data
+ Map variables = runtimeService.getVariables(execution.getRootProcessInstanceId());
+ log.info("主流程中的所有变量名: " + variables.keySet());
+
+// 打印当前执行流的所有变量名
+ log.info("当前执行流的所有变量名: " + execution.getVariables().keySet());
+
+ String bizTitle = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_BIZ_TITLE);
+
+ if(org.apache.commons.lang.StringUtil.isEmpty(url)) {
+ url = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_CONTENT_URL);
+ String mobileUrl = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE);
+ execution.setVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL, url);
+ execution.setVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE, mobileUrl);
+ }
+
+ // 1. 获取主流程中的 json_data 对象(可能是 String 或 JSONObject)
+ Object jsonDataObj = (String)runtimeService.getVariable(mainProcessId,props.getJsonDataKey());
+
+ if (oConvertUtils.isNotEmpty(jsonDataObj)) {
+ String json_data = jsonDataObj.toString();
+ try {
+ // 2. 调用表达式解析工具获取特定字段的字符串内容
+ String jsonStr = flowNodeExpression.getJsondatastring(json_data, props.getImplDeptKey());
+
+ // 3. 只有当字段存在且内容不为空时,才进行解析
+ if (oConvertUtils.isNotEmpty(jsonStr)) {
+ List deptIds = Arrays.stream(jsonStr.split(","))
+ .map(String::trim) // 去掉可能存在的空格
+ .filter(s -> !s.isEmpty()) // 过滤掉空字符串
+ .collect(Collectors.toList());
+
+ if (deptIds != null && !deptIds.isEmpty()) {
+ // 4. 将提取出的 List 存入子流程变量(供多实例 Collection 使用)
+ execution.setVariable( props.getImplDeptKey(), deptIds);
+ log.info("子流程变量 deptIdList 注入成功: " + deptIds);
+ }
+ }
+ } catch (Exception e) {
+ log.error("解析 JSON 字段 pending_depts_id 失败: ", e);
+ }
+ }
+
+ //获取主表数据id
+ String businessKey = (String)runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DATA_ID);
+ //获取主表的设计表单数据ID
+ String BPM_DES_DATA_ID = oConvertUtils.getString(runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DES_DATA_ID));
+
+ //获取主表表名
+ String tableName = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_KEY);
+ execution.setVariable(WorkFlowGlobals.BPM_FORM_KEY, tableName);
+
+ //--update--begin------author:scott-----date:20210512-----for:出差借款子流程,加载表单数据为空问题---------
+ //log.info("----------传入子流程的数据ID--------------: "+execution.getVariable(WorkFlowGlobals.DATA_ID));
+ if(oConvertUtils.isNotEmpty(execution.getVariable(WorkFlowGlobals.DATA_ID))){
+ execution.setVariable(WorkFlowGlobals.BPM_DATA_ID, execution.getVariable(WorkFlowGlobals.DATA_ID));
+ //如果是设计器表单,则还需要设置设计表单数据ID
+ if(oConvertUtils.isNotEmpty(BPM_DES_DATA_ID)){
+ execution.setVariable(WorkFlowGlobals.BPM_DES_DATA_ID, execution.getVariable(WorkFlowGlobals.DATA_ID));
+ }
+ }else{
+ //未传入id值,则获取主表数据id给子流程【online表单需要】
+ execution.setVariable(WorkFlowGlobals.BPM_DATA_ID, businessKey);
+ //如果是设计器表单,则还需要设置设计表单数据ID
+ if(oConvertUtils.isNotEmpty(BPM_DES_DATA_ID)){
+ execution.setVariable(WorkFlowGlobals.BPM_DES_DATA_ID, BPM_DES_DATA_ID);
+ }
+ }
+ //--update--end------author:scott-----date:20210512-----for:出差借款子流程,加载表单数据为空问题---------
+
+ //子流程实例set业务号和主流程保持一致
+ runtimeService.updateBusinessKey(execution.getProcessInstanceId(), businessKey);
+ }
+
+
+
+
+}
diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/resources/application-flow.yml b/jeecg-boot-module/jeecg-module-flow/src/main/resources/application-flow.yml
new file mode 100644
index 0000000..0feb5e8
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/src/main/resources/application-flow.yml
@@ -0,0 +1,8 @@
+flow-biz:
+ xi-speak:
+ bg-dept-id: "2044677604785229826"
+ ld-role-id: "2044680793306591234"
+ need-sdw-approve-code: "needSdwApproval"
+ json-data-key: "json_data"
+ impl-dept-key: "implDept"
+ impl-dept-collection-used-key: "impl_dept_id"
diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/EmbeddedSubProcessHqStartListener.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/EmbeddedSubProcessHqStartListener.java
new file mode 100644
index 0000000..8027061
--- /dev/null
+++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/EmbeddedSubProcessHqStartListener.java
@@ -0,0 +1,117 @@
+package org.jeecg.modules.extbpm.listener.execution;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtil;
+import org.jeecg.modules.extbpm.process.common.expression.FlowNodeExpression;
+import com.alibaba.fastjson.JSON;
+import lombok.RequiredArgsConstructor;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.delegate.ExecutionListener;
+import org.jeecg.common.util.SpringContextUtils;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Component("embeddedSubProcessHqStartListener")
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class EmbeddedSubProcessHqStartListener implements ExecutionListener {
+
+ private static final long serialVersionUID = 1L;
+
+ private final FlowNodeExpression flowNodeExpression;
+ @Autowired
+ private RuntimeService runtimeService;
+
+ @Override
+ public void notify(DelegateExecution execution) {
+
+ //RuntimeService runtimeService = SpringContextUtils.getBean(RuntimeService.class);
+ String mainProcessId = (String)execution.getProcessInstanceId();
+ String url = (String)execution.getVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL);
+
+ // 打印主流程里所有的变量名,看看有没有 json_data
+ Map variables = runtimeService.getVariables(execution.getRootProcessInstanceId());
+ log.info("主流程中的所有变量名: " + variables.keySet());
+
+// 打印当前执行流的所有变量名
+ log.info("当前执行流的所有变量名: " + execution.getVariables().keySet());
+
+ String bizTitle = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_BIZ_TITLE);
+
+ if(org.apache.commons.lang.StringUtil.isEmpty(url)) {
+ url = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_CONTENT_URL);
+ String mobileUrl = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE);
+ execution.setVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL, url);
+ execution.setVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE, mobileUrl);
+ }
+
+ // 1. 获取主流程中的 json_data 对象(可能是 String 或 JSONObject)
+ Object jsonDataObj = (String)runtimeService.getVariable(mainProcessId,"json_data");
+
+ if (oConvertUtils.isNotEmpty(jsonDataObj)) {
+ String json_data = jsonDataObj.toString();
+ try {
+ // 2. 调用表达式解析工具获取特定字段的字符串内容
+ String jsonStr = flowNodeExpression.getJsondatastring(json_data, "implDept");
+
+ // 3. 只有当字段存在且内容不为空时,才进行解析
+ if (oConvertUtils.isNotEmpty(jsonStr)) {
+ List deptIds = Arrays.stream(jsonStr.split(","))
+ .map(String::trim) // 去掉可能存在的空格
+ .filter(s -> !s.isEmpty()) // 过滤掉空字符串
+ .collect(Collectors.toList());
+
+ if (deptIds != null && !deptIds.isEmpty()) {
+ // 4. 将提取出的 List 存入子流程变量(供多实例 Collection 使用)
+ execution.setVariable("pending_depts_id", deptIds);
+ log.info("子流程变量 deptIdList 注入成功: " + deptIds);
+ }
+ }
+ } catch (Exception e) {
+ log.error("解析 JSON 字段 pending_depts_id 失败: ", e);
+ }
+ }
+
+ //获取主表数据id
+ String businessKey = (String)runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DATA_ID);
+ //获取主表的设计表单数据ID
+ String BPM_DES_DATA_ID = oConvertUtils.getString(runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DES_DATA_ID));
+
+ //获取主表表名
+ String tableName = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_KEY);
+ execution.setVariable(WorkFlowGlobals.BPM_FORM_KEY, tableName);
+
+ //--update--begin------author:scott-----date:20210512-----for:出差借款子流程,加载表单数据为空问题---------
+ //log.info("----------传入子流程的数据ID--------------: "+execution.getVariable(WorkFlowGlobals.DATA_ID));
+ if(oConvertUtils.isNotEmpty(execution.getVariable(WorkFlowGlobals.DATA_ID))){
+ execution.setVariable(WorkFlowGlobals.BPM_DATA_ID, execution.getVariable(WorkFlowGlobals.DATA_ID));
+ //如果是设计器表单,则还需要设置设计表单数据ID
+ if(oConvertUtils.isNotEmpty(BPM_DES_DATA_ID)){
+ execution.setVariable(WorkFlowGlobals.BPM_DES_DATA_ID, execution.getVariable(WorkFlowGlobals.DATA_ID));
+ }
+ }else{
+ //未传入id值,则获取主表数据id给子流程【online表单需要】
+ execution.setVariable(WorkFlowGlobals.BPM_DATA_ID, businessKey);
+ //如果是设计器表单,则还需要设置设计表单数据ID
+ if(oConvertUtils.isNotEmpty(BPM_DES_DATA_ID)){
+ execution.setVariable(WorkFlowGlobals.BPM_DES_DATA_ID, BPM_DES_DATA_ID);
+ }
+ }
+ //--update--end------author:scott-----date:20210512-----for:出差借款子流程,加载表单数据为空问题---------
+
+ //子流程实例set业务号和主流程保持一致
+ runtimeService.updateBusinessKey(execution.getProcessInstanceId(), businessKey);
+ }
+
+
+
+
+}
diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/SubProcessSelectUserListener.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/SubProcessSelectUserListener.java
new file mode 100644
index 0000000..7a57642
--- /dev/null
+++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/SubProcessSelectUserListener.java
@@ -0,0 +1,61 @@
+package org.jeecg.modules.extbpm.listener.execution;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.delegate.DelegateExecution;
+import org.flowable.engine.delegate.ExecutionListener;
+import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
+import org.jeecg.modules.extbpm.process.common.expression.FlowNodeExpression;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component("subProcessSelectUserListener")
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class SubProcessSelectUserListener implements ExecutionListener {
+
+ private final FlowNodeExpression flowNodeExpression;
+
+ @Autowired
+ private RuntimeService runtimeService;
+
+ @Override
+ public void notify(DelegateExecution execution) {
+
+ String mainProcessId = (String) execution.getProcessInstanceId();
+ String url = (String)execution.getVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL);
+
+ // 打印主流程里所有的变量名,看看有没有 json_data
+ Map variables = runtimeService.getVariables(execution.getRootProcessInstanceId());
+ log.info("主流程中的所有变量名: " + variables.keySet());
+
+ // 打印当前执行流的所有变量名
+ log.info("当前执行流的所有变量名: " + execution.getVariables().keySet());
+
+ // 1. 获取原生组件选中的处理人 (假设 Key 为 assigneeUserIdList)
+ Object selectedValue = execution.getVariable("assigneeUserIdList");
+
+ List list = new ArrayList<>();
+ if (selectedValue instanceof String) {
+ // 如果是逗号分隔的字符串,拆分
+ list = Arrays.asList(((String) selectedValue).split(","));
+ } else if (selectedValue instanceof List) {
+ list = (List) selectedValue;
+ }
+
+ // 2. 如果没选人,给个默认值防止报错,或者抛出业务异常
+ if (list.isEmpty()) {
+ throw new RuntimeException("请选择下一步处理人!");
+ }
+
+ // 3. 关键:将这个 List 存入局部变量,供下一节点的 Multi-instance 读取
+ // 使用 setVariableLocal 确保并行子流程互不干扰
+ execution.setVariableLocal("internal_collection", list);
+ }
+}
diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/WorkFlowGlobals.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/WorkFlowGlobals.java
index 5c01be9..ca0ec91 100644
--- a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/WorkFlowGlobals.java
+++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/WorkFlowGlobals.java
@@ -201,4 +201,10 @@ public final class WorkFlowGlobals {
* 流程信号启动类型:按钮触发
*/
public static String START_BUTTON_EVENT = "buttonEvent";
+
+ /**
+ * 用于加载json_data字段的键名
+ */
+ public static String JSON_DATA = "json_data";
+ public static String PENDING_DEPTS_ID = "pending_depts_id";
}
diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/expression/FlowNodeExpression.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/expression/FlowNodeExpression.java
index 49a65ca..7f131af 100644
--- a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/expression/FlowNodeExpression.java
+++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/expression/FlowNodeExpression.java
@@ -1,6 +1,7 @@
package org.jeecg.modules.extbpm.process.common.expression;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -81,6 +82,144 @@ public class FlowNodeExpression {
}
}
+ /**
+ * 根据字段名称在json_data内查询对应list或set的长度
+ * ${flowNodeExpression.getJsonDataSizeByCodeName(json_data,"pending_depts_id")}
+ *
+ * @param jsonData
+ * @param codeName
+ * @return
+ */
+ public Integer getJsonDataSizeByCodeNameStr(Object jsonData, String codeName){
+ if (jsonData == null || StringUtils.isEmpty(codeName)) {
+ return 0;
+ }
+
+ // 1. 统一转成 JSONObject
+ JSONObject json;
+ if (jsonData instanceof String) {
+ json = JSON.parseObject((String) jsonData);
+ } else if (jsonData instanceof JSONObject) {
+ json = (JSONObject) jsonData;
+ } else {
+ return 0;
+ }
+
+ if (json == null || !json.containsKey(codeName)) {
+ return 0;
+ }
+
+ // 2. 获取原始对象进行兼容性处理
+ Object value = json.get(codeName);
+ if (value == null) {
+ return 0;
+ }
+
+ List resultList = new ArrayList<>();
+
+ if (value instanceof Collection) {
+ // 情况 A: 本身就是集合/JSON数组
+ resultList.addAll(json.getJSONArray(codeName).toJavaList(String.class));
+ } else if (value instanceof String) {
+ // 情况 B: 是逗号分隔的字符串 "ID1,ID2,ID3"
+ String str = (String) value;
+ if (StringUtils.isNotEmpty(str)) {
+ // 使用 split 拆分,并过滤掉空格和空字符串
+ String[] split = str.split(",");
+ for (String s : split) {
+ if (StringUtils.isNotEmpty(s.trim())) {
+ resultList.add(s.trim());
+ }
+ }
+ }
+ }
+
+ return resultList.size();
+ }
+
+ /**
+ * 根据字段名称在json_data内查询对应list或set的长度
+ * ${flowNodeExpression.getJsonDataSizeByCodeName(json_data,"pending_depts_id")}
+ *
+ * @param jsonData
+ * @param codeName
+ * @return
+ */
+ public Integer getJsonDataSizeByCodeNameObj(Object jsonData, String codeName){
+ if (jsonData == null || StringUtils.isEmpty(codeName)) {
+ return 0;
+ }
+
+ // 1. 统一转成 JSONObject
+ JSONObject json;
+ if (jsonData instanceof String) {
+ json = JSON.parseObject((String) jsonData);
+ } else if (jsonData instanceof JSONObject) {
+ json = (JSONObject) jsonData;
+ } else {
+ return 0;
+ }
+
+ if (json == null || !json.containsKey(codeName)) {
+ return 0;
+ }
+
+ // 2. 获取原始对象进行兼容性处理
+ Object value = json.get(codeName);
+ if (value == null) {
+ return 0;
+ }
+
+ List resultList = new ArrayList<>();
+
+ if (value instanceof Collection) {
+ // 情况 A: 本身就是集合/JSON数组
+ resultList.addAll(json.getJSONArray(codeName).toJavaList(String.class));
+ } else if (value instanceof String) {
+ // 情况 B: 是逗号分隔的字符串 "ID1,ID2,ID3"
+ String str = (String) value;
+ if (StringUtils.isNotEmpty(str)) {
+ // 使用 split 拆分,并过滤掉空格和空字符串
+ String[] split = str.split(",");
+ for (String s : split) {
+ if (StringUtils.isNotEmpty(s.trim())) {
+ resultList.add(s.trim());
+ }
+ }
+ }
+ }
+
+ return resultList.size();
+ }
+
+ /**
+ * 优雅地从 JSON 对象中提取 List
+ */
+ private List getListFromJsonByCodeName(JSONObject json, String key) {
+ if (json == null || !json.containsKey(key)) {
+ return Collections.emptyList();
+ }
+ Object value = json.get(key);
+ if (value == null) {
+ return Collections.emptyList();
+ }
+
+ // 如果本身就是 JSONArray(Collection 子类)
+ if (value instanceof Collection) {
+ return ((JSONArray) value).toJavaList(String.class);
+ }
+
+ // 如果是逗号分隔的字符串
+ if (value instanceof String && StringUtils.isNotBlank((String) value)) {
+ return Arrays.stream(((String) value).split(","))
+ .map(String::trim)
+ .filter(StringUtils::isNotBlank)
+ .collect(Collectors.toList());
+ }
+
+ return Collections.emptyList();
+ }
+
/**
* 根据字段名称取json_data查询对应业务表单的数据
* ${flowNodeExpression.getJsondatastring(jsonData,'supDeptleaderid')}
@@ -90,7 +229,19 @@ public class FlowNodeExpression {
* @return roleId
*/
public List getUsersListByJson(Object jsonData, String deptKeyName, String roleId) {
- return ISysBaseAPI.getUsersListByJsonLocalAPI(jsonData,deptKeyName,roleId);
+ return ISysBaseAPI.getUsersListByJsonLocalAPI(jsonData, deptKeyName, roleId);
+ }
+
+ /**
+ * 根据部门id角色id获取用户列表
+ * ${flowNodeExpression.getUsersListByDeptIdAndRoleId(deptId,roleId)}
+ *
+ * @param deptId
+ * @param roleId
+ * @return roleId
+ */
+ public List getUsersListByDeptIdAndRoleId(String deptId, String roleId) {
+ return sysbase.getUsersListByDeptIdAndRoleIdLocalApi(deptId, roleId);
}
/**
diff --git a/jeecg-module-supervision/pom.xml b/jeecg-module-supervision/pom.xml
index 75ddea8..d8a8670 100644
--- a/jeecg-module-supervision/pom.xml
+++ b/jeecg-module-supervision/pom.xml
@@ -21,6 +21,10 @@
org.jeecgframework.boot
jeecg-boot-module-bpm-flowable
+
+ org.jeecgframework.boot
+ jeecg-system-local-api
+
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/Main.java b/jeecg-module-supervision/src/main/java/org/jeecg/Main.java
new file mode 100644
index 0000000..28f7a57
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/Main.java
@@ -0,0 +1,17 @@
+package org.jeecg;
+
+//TIP 要运行代码,请按 或
+// 点击装订区域中的 图标。
+public class Main {
+ public static void main(String[] args) {
+ //TIP 当文本光标位于高亮显示的文本处时按
+ // 查看 IntelliJ IDEA 建议如何修正。
+ System.out.printf("Hello and welcome!");
+
+ for (int i = 1; i <= 5; i++) {
+ //TIP 按 开始调试代码。我们已经设置了一个 断点
+ // 但您始终可以通过按 添加更多断点。
+ System.out.println("i = " + i);
+ }
+ }
+}
\ No newline at end of file
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/BgXiSpeakController.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/BgXiSpeakController.java
index 0ea5505..522c319 100644
--- a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/BgXiSpeakController.java
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/BgXiSpeakController.java
@@ -21,6 +21,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
+import org.jeecg.modules.demo.bgpartymatter.entity.BgPartymatter;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/feedback/BgXiSpeakFeedbackController.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/feedback/BgXiSpeakFeedbackController.java
new file mode 100644
index 0000000..0492c00
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/controller/feedback/BgXiSpeakFeedbackController.java
@@ -0,0 +1,168 @@
+package org.jeecg.modules.bg.xispeak.controller.feedback;
+
+import java.util.Arrays;
+import java.util.List;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.bg.xispeak.entity.BgXiSpeakFeedback;
+import org.jeecg.modules.bg.xispeak.service.IBgXiSpeakFeedbackService;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.extern.slf4j.Slf4j;
+
+import org.jeecgframework.poi.excel.ExcelImportUtil;
+import org.jeecgframework.poi.excel.def.NormalExcelConstants;
+import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.ImportParams;
+import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
+import org.jeecg.common.system.base.controller.JeecgController;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.servlet.ModelAndView;
+import com.alibaba.fastjson.JSON;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+
+/**
+ * @Description: 习总书记重要讲话反馈表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-28
+ * @Version: V1.0
+ */
+@Tag(name="习总书记重要讲话反馈表")
+@RestController
+@RequestMapping("/bg/xispeak/bgXiSpeak")
+@Slf4j
+public class BgXiSpeakFeedbackController extends JeecgController {
+ @Autowired
+ private IBgXiSpeakFeedbackService bgXiSpeakFeedbackService;
+
+ /**
+ * 分页列表查询
+ *
+ * @param bgXiSpeakFeedback
+ * @param pageNo
+ * @param pageSize
+ * @param req
+ * @return
+ */
+ @Operation(summary="习总书记重要讲话反馈表-分页列表查询")
+ @GetMapping(value = "/listBgXiSpeakFeedbackByMainId")
+ public Result> queryPageList(BgXiSpeakFeedback bgXiSpeakFeedback,
+ @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
+ @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
+ HttpServletRequest req) {
+ QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(bgXiSpeakFeedback, req.getParameterMap());
+ Page page = new Page(pageNo, pageSize);
+ IPage pageList = bgXiSpeakFeedbackService.page(page, queryWrapper);
+ return Result.OK(pageList);
+ }
+
+ /**
+ * 添加
+ *
+ * @param bgXiSpeakFeedback
+ * @return
+ */
+ @AutoLog(value = "习总书记重要讲话反馈表-添加")
+ @Operation(summary="习总书记重要讲话反馈表-添加")
+ @PostMapping(value = "/addBgXiSpeakFeedback")
+ public Result add(@RequestBody BgXiSpeakFeedback bgXiSpeakFeedback) {
+ bgXiSpeakFeedbackService.save(bgXiSpeakFeedback);
+ return Result.OK("添加成功!");
+ }
+
+ /**
+ * 编辑
+ *
+ * @param bgXiSpeakFeedback
+ * @return
+ */
+ @AutoLog(value = "习总书记重要讲话反馈表-编辑")
+ @Operation(summary="习总书记重要讲话反馈表-编辑")
+ @PostMapping(value = "/editBgXiSpeakFeedback")
+ public Result edit(@RequestBody BgXiSpeakFeedback bgXiSpeakFeedback) {
+ bgXiSpeakFeedbackService.updateById(bgXiSpeakFeedback);
+ return Result.OK("编辑成功!");
+ }
+
+ /**
+ * 通过id删除
+ *
+ * @param id
+ * @return
+ */
+ @AutoLog(value = "习总书记重要讲话反馈表-通过id删除")
+ @Operation(summary="习总书记重要讲话反馈表-通过id删除")
+ @DeleteMapping(value = "/deleteBgXiSpeakFeedback")
+ public Result delete(@RequestParam(name="id",required=true) String id) {
+ bgXiSpeakFeedbackService.removeById(id);
+ return Result.OK("删除成功!");
+ }
+
+ /**
+ * 批量删除
+ *
+ * @param ids
+ * @return
+ */
+ @AutoLog(value = "习总书记重要讲话反馈表-批量删除")
+ @Operation(summary="习总书记重要讲话反馈表-批量删除")
+ @DeleteMapping(value = "/deleteBatchBgXiSpeakFeedback")
+ public Result deleteBatch(@RequestParam(name="ids",required=true) String ids) {
+ this.bgXiSpeakFeedbackService.removeByIds(Arrays.asList(ids.split(",")));
+ return Result.OK("批量删除成功!");
+ }
+
+ /**
+ * 通过id查询
+ *
+ * @param id
+ * @return
+ */
+ @Operation(summary="习总书记重要讲话反馈表-通过id查询")
+ @GetMapping(value = "/queryBgXiSpeakFeedbackById")
+ public Result queryById(@RequestParam(name="id",required=true) String id) {
+ BgXiSpeakFeedback bgXiSpeakFeedback = bgXiSpeakFeedbackService.getById(id);
+ if(bgXiSpeakFeedback==null) {
+ return Result.error("未找到对应数据");
+ }
+ return Result.OK(bgXiSpeakFeedback);
+ }
+
+ /**
+ * 导出excel
+ *
+ * @param request
+ * @param bgXiSpeakFeedback
+ */
+ @RequestMapping(value = "/exportBgXiSpeakFeedback")
+ public ModelAndView exportXls(HttpServletRequest request, BgXiSpeakFeedback bgXiSpeakFeedback) {
+ return super.exportXls(request, bgXiSpeakFeedback, BgXiSpeakFeedback.class, "习总书记重要讲话反馈表");
+ }
+
+ /**
+ * 通过excel导入数据
+ *
+ * @param request
+ * @param response
+ * @return
+ */
+ @RequestMapping(value = "/importBgXiSpeakFeedback", method = RequestMethod.POST)
+ public Result> importExcel(HttpServletRequest request, HttpServletResponse response, @PathVariable("mainId") String mainId) {
+ return super.importExcel(request, response, BgXiSpeakFeedback.class);
+ }
+
+}
\ No newline at end of file
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/entity/BgXiSpeak.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/entity/BgXiSpeak.java
index 9a2e4aa..511c5b8 100644
--- a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/entity/BgXiSpeak.java
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/entity/BgXiSpeak.java
@@ -157,4 +157,12 @@ public class BgXiSpeak implements Serializable {
@Excel(name = "督办次数", width = 15)
@Schema(description = "督办次数")
private java.lang.Integer supervisionCount;
+ /**dw领导userId字段*/
+ @Excel(name = "领导userId字段", width = 15)
+ @Schema(description = "领导userId字段")
+ private java.lang.String sdwLeaderList;
+ /**dw领导userId字段*/
+ @Excel(name = "是否需要sdw审批", width = 15)
+ @Schema(description = "是否需要sdw审批")
+ private java.lang.Integer needSdwApproval;
}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/entity/BgXiSpeakFeedback.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/entity/BgXiSpeakFeedback.java
new file mode 100644
index 0000000..2fe8368
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/entity/BgXiSpeakFeedback.java
@@ -0,0 +1,84 @@
+package org.jeecg.modules.bg.xispeak.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * @Description: 习总书记重要讲话反馈表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-28
+ * @Version: V1.0
+ */
+@Data
+@TableName("bg_xi_speak_feedback")
+@Schema(description="习总书记重要讲话反馈表")
+public class BgXiSpeakFeedback implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**主键*/
+ @TableId(type = IdType.ASSIGN_ID)
+ @Schema(description = "主键")
+ private java.lang.String id;
+ /**创建人*/
+ @Schema(description = "创建人")
+ private java.lang.String createBy;
+ /**创建日期*/
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "创建日期")
+ private java.util.Date createTime;
+ /**更新人*/
+ @Schema(description = "更新人")
+ private java.lang.String updateBy;
+ /**更新日期*/
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "更新日期")
+ private java.util.Date updateTime;
+ /**所属部门*/
+ @Schema(description = "所属部门")
+ private java.lang.String sysOrgCode;
+ /**反馈部门id*/
+ @Excel(name = "反馈部门id", width = 15)
+ @Schema(description = "反馈部门id")
+ private java.lang.String responDeptid;
+ /**反馈部门名称*/
+ @Excel(name = "反馈部门名称", width = 15)
+ @Schema(description = "反馈部门名称")
+ private java.lang.String responDeptname;
+ /**反馈人id*/
+ @Excel(name = "反馈人id", width = 15)
+ @Schema(description = "反馈人id")
+ private java.lang.String responPersonid;
+ /**反馈人姓名*/
+ @Excel(name = "反馈人姓名", width = 15)
+ @Schema(description = "反馈人姓名")
+ private java.lang.String responPersonname;
+ /**反馈时间*/
+ @Excel(name = "反馈时间", width = 15, format = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "反馈时间")
+ private java.util.Date responTime;
+ /**落实情况*/
+ @Excel(name = "落实情况", width = 15)
+ @Schema(description = "落实情况")
+ private java.lang.String implStatus;
+ /**完成状态*/
+ @Excel(name = "完成状态", width = 15)
+ @Schema(description = "完成状态")
+ private java.lang.String completionStatus;
+ /**外键*/
+ @Excel(name = "外键", width = 15)
+ @Schema(description = "外键")
+ private java.lang.String mainId;
+}
\ No newline at end of file
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/mapper/BgXiSpeakFeedbackMapper.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/mapper/BgXiSpeakFeedbackMapper.java
new file mode 100644
index 0000000..93d2b74
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/mapper/BgXiSpeakFeedbackMapper.java
@@ -0,0 +1,16 @@
+package org.jeecg.modules.bg.xispeak.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.jeecg.modules.bg.xispeak.entity.BgXiSpeakFeedback;
+
+/**
+ * @Description: 习总书记重要讲话反馈表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-28
+ * @Version: V1.0
+ */
+@Mapper
+public interface BgXiSpeakFeedbackMapper extends BaseMapper {
+
+}
\ No newline at end of file
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/service/IBgXiSpeakFeedbackService.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/service/IBgXiSpeakFeedbackService.java
new file mode 100644
index 0000000..3861ba1
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/service/IBgXiSpeakFeedbackService.java
@@ -0,0 +1,14 @@
+package org.jeecg.modules.bg.xispeak.service;
+
+import org.jeecg.modules.bg.xispeak.entity.BgXiSpeakFeedback;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * @Description: 习总书记重要讲话反馈表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-28
+ * @Version: V1.0
+ */
+public interface IBgXiSpeakFeedbackService extends IService {
+
+}
\ No newline at end of file
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/service/impl/BgXiSpeakFeedbackServiceImpl.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/service/impl/BgXiSpeakFeedbackServiceImpl.java
new file mode 100644
index 0000000..d68a1e7
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/bg/xispeak/service/impl/BgXiSpeakFeedbackServiceImpl.java
@@ -0,0 +1,19 @@
+package org.jeecg.modules.bg.xispeak.service.impl;
+
+import org.jeecg.modules.bg.xispeak.entity.BgXiSpeakFeedback;
+import org.jeecg.modules.bg.xispeak.mapper.BgXiSpeakFeedbackMapper;
+import org.jeecg.modules.bg.xispeak.service.IBgXiSpeakFeedbackService;
+import org.springframework.stereotype.Service;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+
+/**
+ * @Description: 习总书记重要讲话反馈表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-28
+ * @Version: V1.0
+ */
+@Service
+public class BgXiSpeakFeedbackServiceImpl extends ServiceImpl implements IBgXiSpeakFeedbackService {
+
+}
\ No newline at end of file
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/demo/bgpartymatter/service/impl/BgPartymatterServiceImpl.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/demo/bgpartymatter/service/impl/BgPartymatterServiceImpl.java
index 262ed29..43c2c68 100644
--- a/jeecg-module-supervision/src/main/java/org/jeecg/modules/demo/bgpartymatter/service/impl/BgPartymatterServiceImpl.java
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/demo/bgpartymatter/service/impl/BgPartymatterServiceImpl.java
@@ -7,7 +7,6 @@ import org.jeecg.modules.demo.bgpartymatter.entity.BgPartymatterFeedback;
import org.jeecg.modules.demo.bgpartymatter.mapper.BgPartymatterFeedbackMapper;
import org.jeecg.modules.demo.bgpartymatter.mapper.BgPartymatterMapper;
import org.jeecg.modules.demo.bgpartymatter.service.IBgPartymatterService;
-import org.jeecg.modules.test.mapper.TestSonTableMapper;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/controller/TaskListController.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/controller/TaskListController.java
new file mode 100644
index 0000000..09af75d
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/controller/TaskListController.java
@@ -0,0 +1,546 @@
+package org.jeecg.modules.demo.tasklist.controller;
+
+import java.io.UnsupportedEncodingException;
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.HashMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.jeecgframework.poi.excel.ExcelImportUtil;
+import org.jeecgframework.poi.excel.def.NormalExcelConstants;
+import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.ImportParams;
+import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
+import org.jeecg.common.system.vo.LoginUser;
+import org.apache.shiro.SecurityUtils;
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.common.system.query.QueryRuleEnum;
+import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.modules.demo.tasklist.entity.TaskListDetial;
+import org.jeecg.modules.demo.tasklist.entity.TaskListPermission;
+import org.jeecg.modules.demo.tasklist.entity.TaskListFavorite;
+import org.jeecg.modules.demo.tasklist.entity.TaskList;
+import org.jeecg.modules.demo.tasklist.vo.TaskListPage;
+import org.jeecg.modules.demo.tasklist.vo.CreateTaskListReq;
+import org.jeecg.modules.demo.tasklist.vo.CreateTaskListGroupReq;
+import org.jeecg.modules.demo.tasklist.vo.MoveTaskListReq;
+import org.jeecg.modules.demo.tasklist.vo.AddCollaboratorReq;
+import org.jeecg.modules.demo.tasklist.vo.CollaboratorVO;
+import org.jeecg.modules.demo.tasklist.service.ITaskListService;
+import org.jeecg.modules.demo.tasklist.service.ITaskListDetialService;
+import org.jeecg.modules.demo.tasklist.service.ITaskListPermissionService;
+import org.jeecg.modules.demo.tasklist.service.ITaskListFavoriteService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.extern.slf4j.Slf4j;
+import com.alibaba.fastjson.JSON;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+
+
+ /**
+ * @Description: 任务清单表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+@Tag(name="任务清单表")
+@RestController
+@RequestMapping("/tasklist/taskList")
+@Slf4j
+public class TaskListController {
+ @Autowired
+ private ITaskListService taskListService;
+ @Autowired
+ private ITaskListDetialService taskListDetialService;
+ @Autowired
+ private ITaskListPermissionService taskListPermissionService;
+ @Autowired
+ private ITaskListFavoriteService taskListFavoriteService;
+
+ /**
+ * 分页列表查询
+ *
+ * @param taskList
+ * @param pageNo
+ * @param pageSize
+ * @param req
+ * @return
+ */
+ //@AutoLog(value = "任务清单表-分页列表查询")
+ @Operation(summary="任务清单表-分页列表查询")
+ @GetMapping(value = "/list")
+ public Result> queryPageList(TaskList taskList,
+ @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
+ @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
+ HttpServletRequest req) {
+ QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(taskList, req.getParameterMap());
+ Page page = new Page(pageNo, pageSize);
+ IPage pageList = taskListService.page(page, queryWrapper);
+ return Result.OK(pageList);
+ }
+
+ /**
+ * 添加
+ *
+ * @param taskListPage
+ * @return
+ */
+ @AutoLog(value = "任务清单表-添加")
+ @Operation(summary="任务清单表-添加")
+ @RequiresPermissions("tasklist:task_list:add")
+ @PostMapping(value = "/add")
+ public Result add(@RequestBody TaskListPage taskListPage) {
+ TaskList taskList = new TaskList();
+ BeanUtils.copyProperties(taskListPage, taskList);
+ taskListService.saveMain(taskList, taskListPage.getTaskListDetialList(),taskListPage.getTaskListPermissionList(),taskListPage.getTaskListFavoriteList());
+ return Result.OK("添加成功!");
+ }
+
+ /**
+ * 编辑
+ *
+ * @param taskListPage
+ * @return
+ */
+ @AutoLog(value = "任务清单表-编辑")
+ @Operation(summary="任务清单表-编辑")
+ @RequiresPermissions("tasklist:task_list:edit")
+ @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
+ public Result edit(@RequestBody TaskListPage taskListPage) {
+ TaskList taskList = new TaskList();
+ BeanUtils.copyProperties(taskListPage, taskList);
+ TaskList taskListEntity = taskListService.getById(taskList.getId());
+ if(taskListEntity==null) {
+ return Result.error("未找到对应数据");
+ }
+ taskListService.updateMain(taskList, taskListPage.getTaskListDetialList(),taskListPage.getTaskListPermissionList(),taskListPage.getTaskListFavoriteList());
+ return Result.OK("编辑成功!");
+ }
+
+ /**
+ * 通过id删除
+ *
+ * @param id
+ * @return
+ */
+ @AutoLog(value = "任务清单表-通过id删除")
+ @Operation(summary="任务清单表-通过id删除")
+ @RequiresPermissions("tasklist:task_list:delete")
+ @DeleteMapping(value = "/delete")
+ public Result delete(@RequestParam(name="id",required=true) String id) {
+ taskListService.delMain(id);
+ return Result.OK("删除成功!");
+ }
+
+ /**
+ * 批量删除
+ *
+ * @param ids
+ * @return
+ */
+ @AutoLog(value = "任务清单表-批量删除")
+ @Operation(summary="任务清单表-批量删除")
+ @RequiresPermissions("tasklist:task_list:deleteBatch")
+ @DeleteMapping(value = "/deleteBatch")
+ public Result deleteBatch(@RequestParam(name="ids",required=true) String ids) {
+ this.taskListService.delBatchMain(Arrays.asList(ids.split(",")));
+ return Result.OK("批量删除成功!");
+ }
+
+ /**
+ * 通过id查询
+ *
+ * @param id
+ * @return
+ */
+ //@AutoLog(value = "任务清单表-通过id查询")
+ @Operation(summary="任务清单表-通过id查询")
+ @GetMapping(value = "/queryById")
+ public Result queryById(@RequestParam(name="id",required=true) String id) {
+ TaskList taskList = taskListService.getById(id);
+ if(taskList==null) {
+ return Result.error("未找到对应数据");
+ }
+ return Result.OK(taskList);
+
+ }
+
+ @AutoLog(value = "任务清单表-新建任务清单")
+ @Operation(summary = "新建任务清单")
+ @PostMapping(value = "/addTaskList")
+ public Result addTaskList(@RequestBody CreateTaskListReq req) {
+ try {
+ String id = taskListService.createTaskList(req);
+ return Result.OK("创建成功!", id);
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-新建任务清单分组")
+ @Operation(summary = "新建任务清单分组")
+ @PostMapping(value = "/addTaskListGroup")
+ public Result addTaskListGroup(@RequestBody CreateTaskListGroupReq req) {
+ try {
+ String id = taskListService.createTaskListGroup(req);
+ return Result.OK("创建成功!", id);
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-拖拽移动任务清单")
+ @Operation(summary = "拖拽移动任务清单")
+ @PostMapping(value = "/moveTaskList")
+ public Result moveTaskList(@RequestBody MoveTaskListReq req) {
+ try {
+ taskListService.moveTaskList(req);
+ return Result.OK("移动成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-拖拽移动分组")
+ @Operation(summary = "拖拽移动分组")
+ @PostMapping(value = "/moveGroup")
+ public Result moveGroup(@RequestBody Map params) {
+ try {
+ String groupId = (String) params.get("groupId");
+ Integer sortOrder = params.get("sortOrder") != null ? ((Number) params.get("sortOrder")).intValue() : null;
+ taskListService.moveGroup(groupId, sortOrder);
+ return Result.OK("移动成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-获取当前用户收藏列表")
+ @Operation(summary = "获取当前用户收藏列表")
+ @GetMapping(value = "/myFavorites")
+ public Result> myFavorites() {
+ List list = taskListService.getMyFavorites();
+ return Result.OK(list);
+ }
+
+ @AutoLog(value = "任务清单表-删除任务清单")
+ @Operation(summary = "删除任务清单(所有者操作)")
+ @PostMapping(value = "/deleteTaskList")
+ public Result deleteTaskList(@RequestBody Map params) {
+ try {
+ taskListService.deleteTaskList(params.get("id"));
+ return Result.OK("删除成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-移除收藏")
+ @Operation(summary = "从收藏栏移除")
+ @PostMapping(value = "/removeFavorite")
+ public Result removeFavorite(@RequestBody Map params) {
+ try {
+ taskListService.removeFavorite(params.get("favoriteId"));
+ return Result.OK("移除成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-添加到收藏")
+ @Operation(summary = "添加清单到收藏栏")
+ @PostMapping(value = "/addToFavorites")
+ public Result addToFavorites(@RequestBody Map params) {
+ try {
+ taskListService.addToFavorites(params.get("taskListId"), params.get("pid"));
+ return Result.OK("添加成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-移除分组")
+ @Operation(summary = "从收藏栏移除分组及子项")
+ @PostMapping(value = "/removeFavoriteGroup")
+ public Result removeFavoriteGroup(@RequestBody Map params) {
+ try {
+ taskListService.removeFavoriteGroup(params.get("groupId"));
+ return Result.OK("移除成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-重命名分组")
+ @Operation(summary = "重命名任务清单分组")
+ @PostMapping(value = "/renameGroup")
+ public Result renameGroup(@RequestBody Map params) {
+ try {
+ taskListService.renameGroup(params.get("groupId"), params.get("newName"));
+ return Result.OK("重命名成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-重命名清单")
+ @Operation(summary = "重命名任务清单")
+ @PostMapping(value = "/renameTaskList")
+ public Result renameTaskList(@RequestBody Map params) {
+ try {
+ taskListService.renameTaskList(params.get("taskListId"), params.get("newName"));
+ return Result.OK("重命名成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-获取我所有的清单")
+ @Operation(summary = "获取我所有的清单")
+ @GetMapping(value = "/myOwnLists")
+ public Result> myOwnLists() {
+ List list = taskListService.getMyOwnLists();
+ return Result.OK(list);
+ }
+
+ @AutoLog(value = "任务清单表-获取所有清单")
+ @Operation(summary = "获取所有清单")
+ @GetMapping(value = "/allLists")
+ public Result> getAllLists() {
+ return Result.OK(taskListService.getAllLists());
+ }
+
+ @AutoLog(value = "任务清单表-获取我协作的清单")
+ @Operation(summary = "获取我协作的清单")
+ @GetMapping(value = "/myCollabLists")
+ public Result> myCollabLists() {
+ List list = taskListService.getMyCollabLists();
+ return Result.OK(list);
+ }
+
+ @AutoLog(value = "任务清单表-获取协作人列表")
+ @Operation(summary = "获取清单协作人列表")
+ @GetMapping(value = "/getCollaborators")
+ public Result> getCollaborators(@RequestParam(name="taskListId", required=true) String taskListId) {
+ List list = taskListService.getCollaborators(taskListId);
+ return Result.OK(list);
+ }
+
+ @AutoLog(value = "任务清单表-添加协作人")
+ @Operation(summary = "添加协作人")
+ @PostMapping(value = "/addCollaborator")
+ public Result addCollaborator(@RequestBody AddCollaboratorReq req) {
+ try {
+ taskListService.addCollaborator(req);
+ return Result.OK("添加成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-移除协作人")
+ @Operation(summary = "移除协作人")
+ @PostMapping(value = "/removeCollaborator")
+ public Result removeCollaborator(@RequestBody Map params) {
+ try {
+ taskListService.removeCollaborator(params.get("permissionId"));
+ return Result.OK("移除成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-修改协作人权限")
+ @Operation(summary = "修改协作人权限")
+ @PostMapping(value = "/updateCollaboratorPermission")
+ public Result updateCollaboratorPermission(@RequestBody Map params) {
+ try {
+ taskListService.updateCollaboratorPermission(params.get("permissionId"), params.get("permission"));
+ return Result.OK("修改成功!");
+ } catch (RuntimeException e) {
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @AutoLog(value = "任务清单表-获取当前用户对清单的权限")
+ @Operation(summary = "获取当前用户对清单的权限")
+ @GetMapping(value = "/getMyPermission")
+ public Result getMyPermission(@RequestParam(name="taskListId", required=true) String taskListId) {
+ String permission = taskListService.getMyPermission(taskListId);
+ return Result.OK("查询成功", permission);
+ }
+
+ /**
+ * 通过id查询
+ *
+ * @param id
+ * @return
+ */
+ //@AutoLog(value = "任务清单详情表通过主表ID查询")
+ @Operation(summary="任务清单详情表主表ID查询")
+ @GetMapping(value = "/queryTaskListDetialByMainId")
+ public Result> queryTaskListDetialListByMainId(@RequestParam(name="id",required=true) String id) {
+ List taskListDetialList = taskListDetialService.selectByMainId(id);
+ return Result.OK(taskListDetialList);
+ }
+ /**
+ * 通过id查询
+ *
+ * @param id
+ * @return
+ */
+ //@AutoLog(value = "任务清单权限表通过主表ID查询")
+ @Operation(summary="任务清单权限表主表ID查询")
+ @GetMapping(value = "/queryTaskListPermissionByMainId")
+ public Result> queryTaskListPermissionListByMainId(@RequestParam(name="id",required=true) String id) {
+ List taskListPermissionList = taskListPermissionService.selectByMainId(id);
+ return Result.OK(taskListPermissionList);
+ }
+ /**
+ * 通过id查询
+ *
+ * @param id
+ * @return
+ */
+ //@AutoLog(value = "任务清单收藏表通过主表ID查询")
+ @Operation(summary="任务清单收藏表主表ID查询")
+ @GetMapping(value = "/queryTaskListFavoriteByMainId")
+ public Result> queryTaskListFavoriteListByMainId(@RequestParam(name="id",required=true) String id) {
+ List taskListFavoriteList = taskListFavoriteService.selectByMainId(id);
+ return Result.OK(taskListFavoriteList);
+ }
+
+ /**
+ * 导出excel
+ *
+ * @param request
+ * @param taskList
+ */
+ @RequiresPermissions("tasklist:task_list:exportXls")
+ @RequestMapping(value = "/exportXls")
+ public ModelAndView exportXls(HttpServletRequest request, TaskList taskList) {
+
+ // Step.1 组装查询条件查询数据
+ QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(taskList, request.getParameterMap());
+ LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
+ //配置选中数据查询条件
+ String selections = request.getParameter("selections");
+ if(oConvertUtils.isNotEmpty(selections)) {
+ List selectionList = Arrays.asList(selections.split(","));
+ queryWrapper.in("id",selectionList);
+ }
+ //Step.2 获取导出数据
+ List taskListList = taskListService.list(queryWrapper);
+
+ // Step.3 组装pageList
+ List pageList = new ArrayList();
+ for (TaskList main : taskListList) {
+ TaskListPage vo = new TaskListPage();
+ BeanUtils.copyProperties(main, vo);
+ List taskListDetialList = taskListDetialService.selectByMainId(main.getId());
+ vo.setTaskListDetialList(taskListDetialList);
+ List taskListPermissionList = taskListPermissionService.selectByMainId(main.getId());
+ vo.setTaskListPermissionList(taskListPermissionList);
+ List taskListFavoriteList = taskListFavoriteService.selectByMainId(main.getId());
+ vo.setTaskListFavoriteList(taskListFavoriteList);
+ pageList.add(vo);
+ }
+
+ // Step.4 AutoPoi 导出Excel
+ ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
+ mv.addObject(NormalExcelConstants.FILE_NAME, "任务清单表列表");
+ mv.addObject(NormalExcelConstants.CLASS, TaskListPage.class);
+ mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("任务清单表数据", "导出人:"+sysUser.getRealname(), "任务清单表"));
+ mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
+ return mv;
+ }
+
+ /**
+ * 通过excel导入数据
+ *
+ * @param request
+ * @param response
+ * @return
+ */
+ @RequiresPermissions("tasklist:task_list:importExcel")
+ @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
+ public Result> importExcel(HttpServletRequest request, HttpServletResponse response) {
+ MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
+ Map fileMap = multipartRequest.getFileMap();
+ for (Map.Entry entity : fileMap.entrySet()) {
+ // 获取上传文件对象
+ MultipartFile file = entity.getValue();
+ ImportParams params = new ImportParams();
+ params.setTitleRows(2);
+ params.setHeadRows(1);
+ params.setNeedSave(true);
+ try {
+ List list = ExcelImportUtil.importExcel(file.getInputStream(), TaskListPage.class, params);
+ for (TaskListPage page : list) {
+ TaskList po = new TaskList();
+ BeanUtils.copyProperties(page, po);
+ taskListService.saveMain(po, page.getTaskListDetialList(),page.getTaskListPermissionList(),page.getTaskListFavoriteList());
+ }
+ return Result.OK("文件导入成功!数据行数:" + list.size());
+ } catch (Exception e) {
+ log.error(e.getMessage(),e);
+ return Result.error("文件导入失败:"+e.getMessage());
+ } finally {
+ try {
+ file.getInputStream().close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return Result.OK("文件导入失败!");
+ }
+
+ @AutoLog(value = "任务清单表-我负责的任务")
+ @Operation(summary = "获取当前用户负责的任务")
+ @GetMapping(value = "/myResponsibleTasks")
+ public Result> myResponsibleTasks() {
+ List list = taskListService.myResponsibleTasks();
+ return Result.OK(list);
+ }
+
+ @AutoLog(value = "任务清单表-我关注的任务")
+ @Operation(summary = "获取当前用户关注的任务")
+ @GetMapping(value = "/myFollowedTasks")
+ public Result> myFollowedTasks() {
+ List list = taskListService.myFollowedTasks();
+ return Result.OK(list);
+ }
+
+ @AutoLog(value = "任务清单表-获取当前用户密级")
+ @Operation(summary = "获取当前用户密级")
+ @GetMapping(value = "/getCurrentUserSecurityLevel")
+ public Result getCurrentUserSecurityLevel() {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ Integer level = loginUser.getUserSecurityLevel();
+ if (level == null) {
+ level = 3;
+ }
+ return Result.OK("查询成功", level);
+ }
+
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/controller/TaskListDetialController.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/controller/TaskListDetialController.java
new file mode 100644
index 0000000..1e862c2
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/controller/TaskListDetialController.java
@@ -0,0 +1,135 @@
+package org.jeecg.modules.demo.tasklist.controller;
+
+import org.jeecg.common.api.vo.Result;
+import org.jeecg.modules.demo.tasklist.entity.TaskListDetial;
+import org.jeecg.modules.demo.tasklist.service.ITaskListDetialService;
+import org.jeecg.modules.demo.tasklist.vo.CreateTaskReq;
+import org.jeecg.modules.demo.tasklist.vo.MoveTaskReq;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import lombok.extern.slf4j.Slf4j;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@RestController
+@RequestMapping("/tasklist/taskListDetial")
+public class TaskListDetialController {
+
+ @Autowired
+ private ITaskListDetialService taskListDetialService;
+
+ @PostMapping(value = "/add")
+ public Result add(@RequestBody CreateTaskReq req) {
+ try {
+ TaskListDetial result = taskListDetialService.createTask(req);
+ return Result.OK(result);
+ } catch (RuntimeException e) {
+ log.error(e.getMessage(), e);
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @PostMapping(value = "/edit")
+ public Result> edit(@RequestBody TaskListDetial task) {
+ try {
+ taskListDetialService.editTask(task);
+ return Result.OK("编辑成功");
+ } catch (RuntimeException e) {
+ log.error(e.getMessage(), e);
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @DeleteMapping(value = "/delete")
+ public Result> delete(@RequestParam(name = "id") String id) {
+ try {
+ taskListDetialService.deleteTask(id);
+ return Result.OK("删除成功");
+ } catch (RuntimeException e) {
+ log.error(e.getMessage(), e);
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @GetMapping(value = "/listByMainId")
+ public Result> listByMainId(@RequestParam(name = "mainId") String mainId) {
+ List list = taskListDetialService.queryTopLevelByMainId(mainId);
+ return Result.OK(list);
+ }
+
+ @GetMapping(value = "/listAllByMainId")
+ public Result> listAllByMainId(@RequestParam(name = "mainId") String mainId) {
+ List list = taskListDetialService.queryAllByMainId(mainId);
+ return Result.OK(list);
+ }
+
+ @PostMapping(value = "/toggleStatus")
+ public Result> toggleStatus(@RequestBody Map body) {
+ try {
+ String id = body.get("id");
+ taskListDetialService.toggleStatus(id);
+ return Result.OK("操作成功");
+ } catch (RuntimeException e) {
+ log.error(e.getMessage(), e);
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @PostMapping(value = "/moveTask")
+ public Result> moveTask(@RequestBody MoveTaskReq req) {
+ try {
+ taskListDetialService.moveTask(req);
+ return Result.OK("移动成功");
+ } catch (RuntimeException e) {
+ log.error(e.getMessage(), e);
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @PostMapping(value = "/moveTaskGroup")
+ public Result> moveTaskGroup(@RequestBody Map params) {
+ try {
+ String taskGroupId = (String) params.get("taskGroupId");
+ Integer targetSortOrder = params.get("targetSortOrder") != null ? ((Number) params.get("targetSortOrder")).intValue() : null;
+ taskListDetialService.moveTaskGroup(taskGroupId, targetSortOrder);
+ return Result.OK("移动成功");
+ } catch (RuntimeException e) {
+ log.error(e.getMessage(), e);
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @PostMapping(value = "/follow")
+ public Result> follow(@RequestBody Map body) {
+ try {
+ String id = body.get("id");
+ taskListDetialService.followTask(id);
+ return Result.OK("关注成功");
+ } catch (RuntimeException e) {
+ log.error(e.getMessage(), e);
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @PostMapping(value = "/unfollow")
+ public Result> unfollow(@RequestBody Map body) {
+ try {
+ String id = body.get("id");
+ taskListDetialService.unfollowTask(id);
+ return Result.OK("取消关注成功");
+ } catch (RuntimeException e) {
+ log.error(e.getMessage(), e);
+ return Result.error(e.getMessage());
+ }
+ }
+
+ @GetMapping(value = "/loadSubTasks")
+ public Result> loadSubTasks(
+ @RequestParam(name = "parentTaskId") String parentTaskId,
+ @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
+ @RequestParam(name = "pageSize", defaultValue = "20") Integer pageSize) {
+ List list = taskListDetialService.loadSubTasks(parentTaskId, pageNo, pageSize);
+ return Result.OK(list);
+ }
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskList.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskList.java
new file mode 100644
index 0000000..9cf95b5
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskList.java
@@ -0,0 +1,87 @@
+package org.jeecg.modules.demo.tasklist.entity;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableField;
+import org.jeecg.common.constant.ProvinceCityArea;
+import org.jeecg.common.util.SpringContextUtils;
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.jeecg.common.aspect.annotation.Dict;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * @Description: 任务清单表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+@Schema(description="任务清单表")
+@Data
+@TableName("task_list")
+public class TaskList implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**主键*/
+ @TableId(type = IdType.ASSIGN_ID)
+ @Schema(description = "主键")
+ private java.lang.String id;
+ /**创建人*/
+ @Schema(description = "创建人")
+ private java.lang.String createBy;
+ /**创建日期*/
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "创建日期")
+ private java.util.Date createTime;
+ /**更新人*/
+ @Schema(description = "更新人")
+ private java.lang.String updateBy;
+ /**更新日期*/
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "更新日期")
+ private java.util.Date updateTime;
+ /**所属部门*/
+ @Schema(description = "所属部门")
+ private java.lang.String sysOrgCode;
+ /**清单名称*/
+ @Excel(name = "清单名称", width = 15)
+ @Schema(description = "清单名称")
+ private java.lang.String tasklistName;
+ /**删除标识*/
+ @Excel(name = "删除标识", width = 15)
+ @Schema(description = "删除标识")
+ @TableLogic
+ private java.lang.String delFlag;
+
+ @TableField(exist = false)
+ @Schema(description = "所有者名称")
+ private java.lang.String ownerName;
+
+ @TableField(exist = false)
+ @Schema(description = "协作者名称")
+ private java.lang.String collaboratorNames;
+
+ /**密级: 1=非密, 2=内部, 3=秘密, 4=机密*/
+ @Excel(name = "密级", width = 15)
+ @Schema(description = "密级:1=非密,2=内部,3=秘密,4=机密")
+ private java.lang.Integer secretLevel;
+
+ /**密级文本*/
+ @Excel(name = "密级文本", width = 15)
+ @Schema(description = "密级文本")
+ private java.lang.String secretText;
+
+ @TableField(exist = false)
+ @Schema(description = "创建时间字符串")
+ private java.lang.String createTimeStr;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskListDetial.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskListDetial.java
new file mode 100644
index 0000000..22c0933
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskListDetial.java
@@ -0,0 +1,172 @@
+package org.jeecg.modules.demo.tasklist.entity;
+
+import java.io.Serializable;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableField;
+import org.jeecg.common.constant.ProvinceCityArea;
+import org.jeecg.common.util.SpringContextUtils;
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import java.util.Date;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @Description: 任务清单详情表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+@Schema(description="任务清单详情表")
+@Data
+@TableName("task_list_detial")
+public class TaskListDetial implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**主键*/
+ @TableId(type = IdType.ASSIGN_ID)
+ @Schema(description = "主键")
+ private java.lang.String id;
+ /**创建人*/
+ @Schema(description = "创建人")
+ private java.lang.String createBy;
+
+ @TableField(exist = false)
+ @Schema(description = "创建人名称")
+ private java.lang.String createByName;
+ /**创建日期*/
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "创建日期")
+ private java.util.Date createTime;
+ /**更新人*/
+ @Schema(description = "更新人")
+ private java.lang.String updateBy;
+ /**更新日期*/
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "更新日期")
+ private java.util.Date updateTime;
+ /**所属部门*/
+ @Schema(description = "所属部门")
+ private java.lang.String sysOrgCode;
+ /**主表ID*/
+ @Schema(description = "主表ID")
+ private java.lang.String mainId;
+ /**父节点ID*/
+ @Excel(name = "父节点ID", width = 15)
+ @Schema(description = "父节点ID")
+ private java.lang.String pid;
+ /**是否有子节点*/
+ @Excel(name = "是否有子节点", width = 15)
+ @Schema(description = "是否有子节点")
+ private java.lang.String hasChild;
+ /**排序号*/
+ @Excel(name = "排序号", width = 15)
+ @Schema(description = "排序号")
+ private java.lang.Integer sortOrder;
+ /**任务名称*/
+ @Excel(name = "任务名称", width = 15)
+ @Schema(description = "任务名称")
+ private java.lang.String taskName;
+ /**任务描述*/
+ @Excel(name = "任务描述", width = 15)
+ @Schema(description = "任务描述")
+ private java.lang.String taskDesc;
+ /**优先级*/
+ @Excel(name = "优先级", width = 15, dicCode = "task_priority")
+ @Schema(description = "优先级")
+ private java.lang.String priority;
+ /**完成状态*/
+ @Excel(name = "完成状态", width = 15)
+ @Schema(description = "完成状态")
+ private java.lang.Integer taskStatus;
+ /**负责人ID*/
+ @Excel(name = "负责人ID", width = 15)
+ @Schema(description = "负责人ID")
+ private java.lang.String assigneeId;
+ /**负责人*/
+ @Excel(name = "负责人", width = 15)
+ @Schema(description = "负责人")
+ private java.lang.String assigneeName;
+ /**关注人ID*/
+ @Excel(name = "关注人ID", width = 15)
+ @Schema(description = "关注人ID")
+ private java.lang.String followersId;
+ /**关注人*/
+ @Excel(name = "关注人", width = 15)
+ @Schema(description = "关注人")
+ private java.lang.String followersName;
+ /**分配人ID*/
+ @Excel(name = "分配人ID", width = 15)
+ @Schema(description = "分配人ID")
+ private java.lang.String assignId;
+ /**分配人*/
+ @Excel(name = "分配人", width = 15)
+ @Schema(description = "分配人")
+ private java.lang.String assignName;
+ /**开始时间*/
+ @Excel(name = "开始时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "开始时间")
+ private java.util.Date startTime;
+ /**结束时间*/
+ @Excel(name = "结束时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "结束时间")
+ private java.util.Date endTime;
+ /**完成时间*/
+ @Excel(name = "完成时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "完成时间")
+ private java.util.Date completeTime;
+ /**类型*/
+ @Excel(name = "类型", width = 15)
+ @Schema(description = "类型")
+ private java.lang.String type;
+ /**子任务数*/
+ @Excel(name = "子任务数", width = 15)
+ @Schema(description = "子任务数")
+ private java.lang.Integer subTaskCount;
+ /**子任务完成数*/
+ @Excel(name = "子任务完成数", width = 15)
+ @Schema(description = "子任务完成数")
+ private java.lang.Integer completedSubTaskCount;
+ /**参与人ID*/
+ @Excel(name = "参与人ID", width = 15)
+ @Schema(description = "参与人ID")
+ private java.lang.String participantId;
+ /**参与人*/
+ @Excel(name = "参与人", width = 15)
+ @Schema(description = "参与人")
+ private java.lang.String participantName;
+ /**其他事项说明*/
+ @Excel(name = "其他事项说明", width = 15)
+ @Schema(description = "其他事项说明")
+ private java.lang.String remark;
+ /**是否默认分组*/
+ @Excel(name = "是否默认分组", width = 15, dicCode = "is_default")
+ @Schema(description = "是否默认分组(1=是,0=否)")
+ private java.lang.Integer isDefault;
+ /**来源清单名称*/
+ @TableField(exist = false)
+ @Schema(description = "来源清单名称")
+ private java.lang.String listName;
+ /**当前用户对清单的权限*/
+ @TableField(exist = false)
+ @Schema(description = "当前用户对该清单的权限(1=所有者,2=可编辑,3=只读)")
+ private java.lang.String myPermission;
+ /**删除标识*/
+ @Excel(name = "删除标识", width = 15)
+ @Schema(description = "删除标识")
+ @TableLogic
+ private java.lang.String delFlag;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskListFavorite.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskListFavorite.java
new file mode 100644
index 0000000..6ecd951
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskListFavorite.java
@@ -0,0 +1,98 @@
+package org.jeecg.modules.demo.tasklist.entity;
+
+import java.io.Serializable;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableField;
+import org.jeecg.common.constant.ProvinceCityArea;
+import org.jeecg.common.util.SpringContextUtils;
+import lombok.Data;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import java.util.Date;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @Description: 任务清单收藏表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+@Schema(description="任务清单收藏表")
+@Data
+@TableName("task_list_favorite")
+public class TaskListFavorite implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**主键*/
+ @TableId(type = IdType.ASSIGN_ID)
+ @Schema(description = "主键")
+ private java.lang.String id;
+ /**创建人*/
+ @Schema(description = "创建人")
+ private java.lang.String createBy;
+ /**创建日期*/
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "创建日期")
+ private java.util.Date createTime;
+ /**更新人*/
+ @Schema(description = "更新人")
+ private java.lang.String updateBy;
+ /**更新日期*/
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "更新日期")
+ private java.util.Date updateTime;
+ /**所属部门*/
+ @Schema(description = "所属部门")
+ private java.lang.String sysOrgCode;
+ /**主表ID*/
+ @Schema(description = "主表ID")
+ private java.lang.String mainId;
+ /**任务清单(分组)名称*/
+ @Excel(name = "任务清单(分组)名称", width = 15)
+ @Schema(description = "任务清单(分组)名称")
+ private java.lang.String tasklistName;
+ /**用户ID*/
+ @Excel(name = "用户ID", width = 15)
+ @Schema(description = "用户ID")
+ private java.lang.String userId;
+ /**类型(0是分组,1是清单)*/
+ @Excel(name = "类型(0是分组,1是清单)", width = 15)
+ @Schema(description = "类型(0是分组,1是清单)")
+ private java.lang.String type;
+ /**父节点ID*/
+ @Excel(name = "父节点ID", width = 15)
+ @Schema(description = "父节点ID")
+ private java.lang.String pid;
+ /**是否有子节点*/
+ @Excel(name = "是否有子节点", width = 15)
+ @Schema(description = "是否有子节点")
+ private java.lang.String hasChild;
+ /**排序*/
+ @Excel(name = "排序", width = 15)
+ @Schema(description = "排序")
+ private java.lang.Integer sortOrder;
+ /**删除标识*/
+ @Excel(name = "删除标识", width = 15)
+ @Schema(description = "删除标识")
+ @TableLogic
+ private java.lang.String delFlag;
+
+ @TableField(exist = false)
+ @Schema(description = "清单密级")
+ private java.lang.Integer secretLevel;
+
+ @TableField(exist = false)
+ @Schema(description = "清单密级文本")
+ private java.lang.String secretText;
+
+ @TableField(exist = false)
+ @Schema(description = "当前用户对该清单的权限")
+ private java.lang.String permission;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/test/entity/TestMainTable.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskListPermission.java
similarity index 56%
rename from jeecg-module-supervision/src/main/java/org/jeecg/modules/test/entity/TestMainTable.java
rename to jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskListPermission.java
index 603e89f..5a0959d 100644
--- a/jeecg-module-supervision/src/main/java/org/jeecg/modules/test/entity/TestMainTable.java
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/entity/TaskListPermission.java
@@ -1,8 +1,6 @@
-package org.jeecg.modules.test.entity;
+package org.jeecg.modules.demo.tasklist.entity;
import java.io.Serializable;
-import java.io.UnsupportedEncodingException;
-import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -14,52 +12,60 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
-
+import java.util.Date;
import io.swagger.v3.oas.annotations.media.Schema;
+import java.io.UnsupportedEncodingException;
/**
- * @Description: test
+ * @Description: 任务清单权限表
* @Author: jeecg-boot
- * @Date: 2026-04-03
+ * @Date: 2026-04-24
* @Version: V1.0
*/
-@Schema(description="test")
+@Schema(description="任务清单权限表")
@Data
-@TableName("test_main_table")
-public class TestMainTable implements Serializable {
+@TableName("task_list_permission")
+public class TaskListPermission implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
- private String id;
+ private java.lang.String id;
/**创建人*/
@Schema(description = "创建人")
- private String createBy;
+ private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
- private Date createTime;
+ private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
- private String updateBy;
+ private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
- private Date updateTime;
+ private java.util.Date updateTime;
/**所属部门*/
@Schema(description = "所属部门")
- private String sysOrgCode;
- /**a字段*/
- @Excel(name = "a字段", width = 15)
- @Schema(description = "a字段")
- private String fieldA;
- /**b字段*/
- @Excel(name = "b字段", width = 15)
- @Schema(description = "b字段")
- private String fieldB;
- @TableLogic(value = "0", delval = "1")
- private int delFlag = 0;
+ private java.lang.String sysOrgCode;
+ /**主表ID*/
+ @Schema(description = "主表ID")
+ private java.lang.String mainId;
+ /**用户ID*/
+ @Excel(name = "用户ID", width = 15)
+ @Schema(description = "用户ID")
+ private java.lang.String userId;
+ /**权限类型*/
+ @Excel(name = "权限类型", width = 15, dicCode = "collaboration_permission")
+ @Dict(dicCode = "collaboration_permission")
+ @Schema(description = "权限类型")
+ private java.lang.String permission;
+ /**删除标识*/
+ @Excel(name = "删除标识", width = 15)
+ @Schema(description = "删除标识")
+ @TableLogic
+ private java.lang.String delFlag;
}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListDetialMapper.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListDetialMapper.java
new file mode 100644
index 0000000..70f26e6
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListDetialMapper.java
@@ -0,0 +1,63 @@
+package org.jeecg.modules.demo.tasklist.mapper;
+
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+import org.jeecg.modules.demo.tasklist.entity.TaskListDetial;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+public interface TaskListDetialMapper extends BaseMapper {
+
+ boolean deleteByMainId(@Param("mainId") String mainId);
+
+ List selectByMainId(@Param("mainId") String mainId);
+
+ void shiftSortOrderUp(@Param("mainId") String mainId, @Param("pid") String pid, @Param("fromSort") Integer fromSort);
+
+ void shiftSortOrderDown(@Param("mainId") String mainId, @Param("pid") String pid, @Param("fromSort") Integer fromSort);
+
+ Integer getMaxSortOrder(@Param("mainId") String mainId, @Param("pid") String pid);
+
+ int appendFollower(@Param("taskId") String taskId, @Param("userId") String userId, @Param("userName") String userName);
+
+ int removeFollower(@Param("taskId") String taskId, @Param("userId") String userId, @Param("userName") String userName);
+
+ int toggleTaskStatus(@Param("taskId") String taskId, @Param("updateBy") String updateBy, @Param("newStatus") Integer newStatus, @Param("completeTime") java.util.Date completeTime);
+
+ List selectAllByMainId(@Param("mainId") String mainId);
+
+ List selectTopLevelByMainId(@Param("mainId") String mainId);
+
+ List selectSubTasksByPage(@Param("parentId") String parentId, @Param("offset") Integer offset, @Param("pageSize") Integer pageSize);
+
+ void updatePidAndSort(@Param("id") String id, @Param("pid") String pid, @Param("sortOrder") Integer sortOrder);
+
+ List selectChildrenByPid(@Param("pid") String pid);
+
+ void resetPidToNull(@Param("pid") String pid, @Param("mainId") String mainId);
+
+ void incrementSubTaskCount(@Param("parentId") String parentId);
+
+ void decrementSubTaskCount(@Param("parentId") String parentId);
+
+ void incrementCompletedSubTaskCount(@Param("parentId") String parentId);
+
+ void decrementCompletedSubTaskCount(@Param("parentId") String parentId);
+
+ void updateHasChild(@Param("parentId") String parentId, @Param("hasChild") String hasChild);
+
+ void shiftSortOrderUpForGroup(@Param("mainId") String mainId, @Param("fromSort") Integer fromSort);
+
+ void shiftSortOrderDownForGroup(@Param("mainId") String mainId, @Param("fromSort") Integer fromSort);
+
+ Integer getMaxSortOrderForGroup(@Param("mainId") String mainId);
+
+ void updateSortOrder(@Param("id") String id, @Param("sortOrder") Integer sortOrder);
+
+ Integer countChildrenByPid(@Param("pid") String pid);
+
+ List selectByAssigneeId(@Param("assigneeId") String assigneeId, @Param("userId") String userId);
+
+ List selectByFollowersId(@Param("followersId") String followersId, @Param("userId") String userId);
+
+ void appendAssigner(@Param("taskId") String taskId, @Param("userId") String userId, @Param("userName") String userName);
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListFavoriteMapper.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListFavoriteMapper.java
new file mode 100644
index 0000000..c305012
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListFavoriteMapper.java
@@ -0,0 +1,49 @@
+package org.jeecg.modules.demo.tasklist.mapper;
+
+import java.util.List;
+import org.jeecg.modules.demo.tasklist.entity.TaskListFavorite;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @Description: 任务清单收藏表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+public interface TaskListFavoriteMapper extends BaseMapper {
+
+ /**
+ * 通过主表id删除子表数据
+ *
+ * @param mainId 主表id
+ * @return boolean
+ */
+ public boolean deleteByMainId(@Param("mainId") String mainId);
+
+ /**
+ * 通过主表id查询子表数据
+ *
+ * @param mainId 主表id
+ * @return List
+ */
+ public List selectByMainId(@Param("mainId") String mainId);
+
+ Integer selectMaxSortOrder(@Param("userId") String userId, @Param("pid") String pid);
+
+ Integer selectMaxSortOrderByType(@Param("userId") String userId, @Param("pid") String pid, @Param("type") String type);
+
+ void shiftSortOrderUp(@Param("userId") String userId, @Param("pid") String pid, @Param("fromSort") Integer fromSort);
+
+ void shiftSortOrderDown(@Param("userId") String userId, @Param("pid") String pid, @Param("fromSort") Integer fromSort);
+
+ void shiftSortOrderUpByType(@Param("userId") String userId, @Param("type") String type, @Param("fromSort") Integer fromSort);
+
+ void shiftSortOrderDownByType(@Param("userId") String userId, @Param("type") String type, @Param("fromSort") Integer fromSort);
+
+ Integer countChildrenByPid(@Param("userId") String userId, @Param("pid") String pid);
+
+ void updatePidAndSort(@Param("id") String id, @Param("pid") String pid, @Param("sortOrder") Integer sortOrder);
+
+ void updateSortOrder(@Param("id") String id, @Param("sortOrder") Integer sortOrder);
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListMapper.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListMapper.java
new file mode 100644
index 0000000..ee19690
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListMapper.java
@@ -0,0 +1,28 @@
+package org.jeecg.modules.demo.tasklist.mapper;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+import org.jeecg.modules.demo.tasklist.entity.TaskList;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * @Description: 任务清单表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+public interface TaskListMapper extends BaseMapper {
+
+ /**
+ * 查询当前用户可见的所有清单(包含部门、所有者、协作者、任务负责人/参与人维度)
+ * @param userId 当前用户ID
+ * @param userSecLevel 当前用户密级
+ * @param orgCode 当前用户部门编码
+ * @return 可见清单列表
+ */
+ List selectVisibleLists(@Param("userId") String userId,
+ @Param("userSecLevel") Integer userSecLevel,
+ @Param("orgCode") String orgCode);
+
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/test/mapper/TestSonTableMapper.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListPermissionMapper.java
similarity index 52%
rename from jeecg-module-supervision/src/main/java/org/jeecg/modules/test/mapper/TestSonTableMapper.java
rename to jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListPermissionMapper.java
index ee502e6..65a6d86 100644
--- a/jeecg-module-supervision/src/main/java/org/jeecg/modules/test/mapper/TestSonTableMapper.java
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/TaskListPermissionMapper.java
@@ -1,17 +1,17 @@
-package org.jeecg.modules.test.mapper;
+package org.jeecg.modules.demo.tasklist.mapper;
import java.util.List;
-import org.jeecg.modules.test.entity.TestSonTable;
+import org.jeecg.modules.demo.tasklist.entity.TaskListPermission;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
/**
- * @Description: test
+ * @Description: 任务清单权限表
* @Author: jeecg-boot
- * @Date: 2026-04-03
+ * @Date: 2026-04-24
* @Version: V1.0
*/
-public interface TestSonTableMapper extends BaseMapper {
+public interface TaskListPermissionMapper extends BaseMapper {
/**
* 通过主表id删除子表数据
@@ -25,7 +25,7 @@ public interface TestSonTableMapper extends BaseMapper {
* 通过主表id查询子表数据
*
* @param mainId 主表id
- * @return List
+ * @return List
*/
- public List selectByMainId(@Param("mainId") String mainId);
+ public List selectByMainId(@Param("mainId") String mainId);
}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListDetialMapper.xml b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListDetialMapper.xml
new file mode 100644
index 0000000..9225906
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListDetialMapper.xml
@@ -0,0 +1,192 @@
+
+
+
+
+
+ DELETE FROM task_list_detial WHERE main_id = #{mainId}
+
+
+
+
+
+ UPDATE task_list_detial
+ SET sort_order = sort_order + 1
+ WHERE main_id = #{mainId} AND del_flag = '0'
+ AND (pid = #{pid} OR (#{pid} IS NULL AND pid IS NULL))
+ AND sort_order >= #{fromSort}
+
+
+
+ UPDATE task_list_detial
+ SET sort_order = sort_order - 1
+ WHERE main_id = #{mainId} AND del_flag = '0'
+ AND (pid = #{pid} OR (#{pid} IS NULL AND pid IS NULL))
+ AND sort_order > #{fromSort}
+
+
+
+
+
+ UPDATE task_list_detial
+ SET followers_id = CONCAT(IFNULL(followers_id, ''), ',', #{userId}),
+ followers_name = CONCAT(IFNULL(followers_name, ''), ',', #{userName})
+ WHERE id = #{taskId} AND del_flag = '0'
+ AND (followers_id IS NULL OR followers_id NOT LIKE CONCAT('%', #{userId}, '%'))
+
+
+
+ UPDATE task_list_detial
+ SET followers_id = TRIM(BOTH ',' FROM REPLACE(CONCAT(',', followers_id, ','), CONCAT(',', #{userId}, ','), ',')),
+ followers_name = TRIM(BOTH ',' FROM REPLACE(CONCAT(',', followers_name, ','), CONCAT(',', #{userName}, ','), ','))
+ WHERE id = #{taskId} AND del_flag = '0'
+
+
+
+ UPDATE task_list_detial
+ SET task_status = #{newStatus},
+ complete_time = #{completeTime},
+ update_by = #{updateBy}, update_time = NOW()
+ WHERE id = #{taskId} AND del_flag = '0'
+
+
+
+
+
+
+
+
+
+ UPDATE task_list_detial
+ SET pid = #{pid}, sort_order = #{sortOrder}
+ WHERE id = #{id}
+
+
+
+
+
+ UPDATE task_list_detial
+ SET pid = NULL
+ WHERE pid = #{pid} AND main_id = #{mainId} AND del_flag = '0'
+
+
+
+ UPDATE task_list_detial SET sub_task_count = IFNULL(sub_task_count, 0) + 1, has_child = '1'
+ WHERE id = #{parentId}
+
+
+
+ UPDATE task_list_detial SET sub_task_count = GREATEST(IFNULL(sub_task_count, 1) - 1, 0)
+ WHERE id = #{parentId}
+
+
+
+ UPDATE task_list_detial SET completed_sub_task_count = IFNULL(completed_sub_task_count, 0) + 1
+ WHERE id = #{parentId}
+
+
+
+ UPDATE task_list_detial SET completed_sub_task_count = GREATEST(IFNULL(completed_sub_task_count, 1) - 1, 0)
+ WHERE id = #{parentId}
+
+
+
+ UPDATE task_list_detial SET has_child = #{hasChild} WHERE id = #{parentId}
+
+
+
+ UPDATE task_list_detial
+ SET sort_order = sort_order + 1
+ WHERE main_id = #{mainId} AND del_flag = '0'
+ AND type = '0'
+ AND (pid IS NULL OR pid = '')
+ AND sort_order >= #{fromSort}
+
+
+
+ UPDATE task_list_detial
+ SET sort_order = sort_order - 1
+ WHERE main_id = #{mainId} AND del_flag = '0'
+ AND type = '0'
+ AND (pid IS NULL OR pid = '')
+ AND sort_order > #{fromSort}
+
+
+
+
+
+ UPDATE task_list_detial SET sort_order = #{sortOrder} WHERE id = #{id}
+
+
+
+
+
+
+
+
+
+ UPDATE task_list_detial
+ SET assign_id = TRIM(BOTH ',' FROM CONCAT(IFNULL(assign_id, ''), ',', #{userId})),
+ assign_name = TRIM(BOTH ',' FROM CONCAT(IFNULL(assign_name, ''), ',', #{userName}))
+ WHERE id = #{taskId} AND del_flag = '0'
+ AND (assign_id IS NULL OR assign_id NOT LIKE CONCAT('%', #{userId}, '%'))
+
+
+
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListFavoriteMapper.xml b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListFavoriteMapper.xml
new file mode 100644
index 0000000..3cdcd29
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListFavoriteMapper.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+ DELETE
+ FROM task_list_favorite
+ WHERE
+ main_id = #{mainId}
+
+
+
+
+
+
+
+
+ UPDATE task_list_favorite
+ SET sort_order = sort_order + 1
+ WHERE user_id = #{userId} AND del_flag = '0'
+ AND (pid = #{pid} OR (#{pid} IS NULL AND pid IS NULL))
+ AND sort_order >= #{fromSort}
+
+
+
+ UPDATE task_list_favorite
+ SET sort_order = sort_order - 1
+ WHERE user_id = #{userId} AND del_flag = '0'
+ AND (pid = #{pid} OR (#{pid} IS NULL AND pid IS NULL))
+ AND sort_order > #{fromSort}
+
+
+
+ UPDATE task_list_favorite
+ SET sort_order = sort_order + 1
+ WHERE user_id = #{userId} AND del_flag = '0'
+ AND type = #{type}
+ AND pid IS NULL
+ AND sort_order >= #{fromSort}
+
+
+
+ UPDATE task_list_favorite
+ SET sort_order = sort_order - 1
+ WHERE user_id = #{userId} AND del_flag = '0'
+ AND type = #{type}
+ AND pid IS NULL
+ AND sort_order > #{fromSort}
+
+
+
+
+
+ UPDATE task_list_favorite
+ SET pid = #{pid}, sort_order = #{sortOrder}
+ WHERE id = #{id}
+
+
+ UPDATE task_list_favorite SET sort_order = #{sortOrder} WHERE id = #{id}
+
+
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListMapper.xml b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListMapper.xml
new file mode 100644
index 0000000..d6aac66
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListMapper.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/test/mapper/xml/TestSonTableMapper.xml b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListPermissionMapper.xml
similarity index 53%
rename from jeecg-module-supervision/src/main/java/org/jeecg/modules/test/mapper/xml/TestSonTableMapper.xml
rename to jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListPermissionMapper.xml
index 87ca9ba..e620060 100644
--- a/jeecg-module-supervision/src/main/java/org/jeecg/modules/test/mapper/xml/TestSonTableMapper.xml
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/mapper/xml/TaskListPermissionMapper.xml
@@ -1,16 +1,16 @@
-
+
DELETE
- FROM test_son_table
+ FROM task_list_permission
WHERE
- main_table_id = #{mainId}
+ main_id = #{mainId}
-
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListDetialService.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListDetialService.java
new file mode 100644
index 0000000..7e23910
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListDetialService.java
@@ -0,0 +1,43 @@
+package org.jeecg.modules.demo.tasklist.service;
+
+import org.jeecg.modules.demo.tasklist.entity.TaskListDetial;
+import com.baomidou.mybatisplus.extension.service.IService;
+import java.util.List;
+import org.jeecg.modules.demo.tasklist.vo.CreateTaskReq;
+import org.jeecg.modules.demo.tasklist.vo.MoveTaskReq;
+
+public interface ITaskListDetialService extends IService {
+
+ List selectByMainId(String mainId);
+
+ TaskListDetial createTask(CreateTaskReq req);
+
+ void editTask(TaskListDetial task);
+
+ void deleteTask(String taskId);
+
+ List queryAllByMainId(String mainId);
+
+ List queryTopLevelByMainId(String mainId);
+
+ void toggleStatus(String taskId);
+
+ void moveTask(MoveTaskReq req);
+
+ void moveTaskGroup(String taskGroupId, Integer targetSortOrder);
+
+ /**
+ * 纯排序重算:将任务/分组移到同级第 targetPosition 个位置
+ * 不包含权限校验和副作用处理,由调用方负责
+ */
+ void reorderTaskItem(String movedId, String mainId, String pid,
+ Integer targetPosition, String newPid, boolean isGroup);
+
+ void followTask(String taskId);
+
+ void unfollowTask(String taskId);
+
+ List loadSubTasks(String parentTaskId, Integer pageNo, Integer pageSize);
+
+ void ensureDefaultGroup(String mainId);
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListFavoriteService.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListFavoriteService.java
new file mode 100644
index 0000000..154c7e6
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListFavoriteService.java
@@ -0,0 +1,45 @@
+package org.jeecg.modules.demo.tasklist.service;
+
+import org.jeecg.modules.demo.tasklist.entity.TaskListFavorite;
+import com.baomidou.mybatisplus.extension.service.IService;
+import java.util.List;
+
+/**
+ * @Description: 任务清单收藏表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+public interface ITaskListFavoriteService extends IService {
+
+ /**
+ * 通过主表id查询子表数据
+ *
+ * @param mainId 主表id
+ * @return List
+ */
+ public List selectByMainId(String mainId);
+
+ Integer getMaxSortOrder(String userId, String pid);
+
+ Integer getMaxSortOrderByType(String userId, String pid, String type);
+
+ void shiftSortOrderUp(String userId, String pid, Integer fromSort);
+
+ void shiftSortOrderDown(String userId, String pid, Integer fromSort);
+
+ void shiftSortOrderUpByType(String userId, String type, Integer fromSort);
+
+ void shiftSortOrderDownByType(String userId, String type, Integer fromSort);
+
+ Integer countChildren(String userId, String pid);
+
+ void updatePidAndSort(String favoriteId, String pid, Integer sortOrder);
+
+ /**
+ * 纯排序重算:将指定记录移到同级第 targetPosition 个位置
+ * 不包含权限校验和副作用处理,由调用方负责
+ */
+ void reorderItem(String movedId, String userId, String pid, String type,
+ Integer targetPosition, String newPid);
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListPermissionService.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListPermissionService.java
new file mode 100644
index 0000000..35895f0
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListPermissionService.java
@@ -0,0 +1,22 @@
+package org.jeecg.modules.demo.tasklist.service;
+
+import org.jeecg.modules.demo.tasklist.entity.TaskListPermission;
+import com.baomidou.mybatisplus.extension.service.IService;
+import java.util.List;
+
+/**
+ * @Description: 任务清单权限表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+public interface ITaskListPermissionService extends IService {
+
+ /**
+ * 通过主表id查询子表数据
+ *
+ * @param mainId 主表id
+ * @return List
+ */
+ public List selectByMainId(String mainId);
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListService.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListService.java
new file mode 100644
index 0000000..9d35c89
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/ITaskListService.java
@@ -0,0 +1,100 @@
+package org.jeecg.modules.demo.tasklist.service;
+
+import org.jeecg.modules.demo.tasklist.entity.TaskListDetial;
+import org.jeecg.modules.demo.tasklist.entity.TaskListPermission;
+import org.jeecg.modules.demo.tasklist.entity.TaskListFavorite;
+import org.jeecg.modules.demo.tasklist.entity.TaskList;
+import org.jeecg.modules.demo.tasklist.vo.CreateTaskListReq;
+import org.jeecg.modules.demo.tasklist.vo.CreateTaskListGroupReq;
+import org.jeecg.modules.demo.tasklist.vo.MoveTaskListReq;
+import org.jeecg.modules.demo.tasklist.vo.AddCollaboratorReq;
+import org.jeecg.modules.demo.tasklist.vo.CollaboratorVO;
+import com.baomidou.mybatisplus.extension.service.IService;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @Description: 任务清单表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+public interface ITaskListService extends IService {
+
+ /**
+ * 添加一对多
+ *
+ * @param taskList
+ * @param taskListDetialList
+ * @param taskListPermissionList
+ * @param taskListFavoriteList
+ */
+ public void saveMain(TaskList taskList,List taskListDetialList,List taskListPermissionList,List taskListFavoriteList) ;
+
+ /**
+ * 修改一对多
+ *
+ * @param taskList
+ * @param taskListDetialList
+ * @param taskListPermissionList
+ * @param taskListFavoriteList
+ */
+ public void updateMain(TaskList taskList,List taskListDetialList,List taskListPermissionList,List taskListFavoriteList);
+
+ /**
+ * 删除一对多
+ *
+ * @param id
+ */
+ public void delMain (String id);
+
+ /**
+ * 批量删除一对多
+ *
+ * @param idList
+ */
+ public void delBatchMain (Collection extends Serializable> idList);
+
+ String createTaskList(CreateTaskListReq req);
+
+ String createTaskListGroup(CreateTaskListGroupReq req);
+
+ void moveTaskList(MoveTaskListReq req);
+
+ void moveGroup(String groupId, Integer sortOrder);
+
+ List getMyFavorites();
+
+ void deleteTaskList(String taskListId);
+
+ void removeFavorite(String favoriteId);
+
+ void removeFavoriteGroup(String groupId);
+
+ void renameGroup(String groupId, String newName);
+
+ void renameTaskList(String taskListId, String newName);
+
+ List getMyOwnLists();
+
+ List getAllLists();
+
+ List getMyCollabLists();
+
+ List getCollaborators(String taskListId);
+
+ void addCollaborator(AddCollaboratorReq req);
+
+ void removeCollaborator(String permissionId);
+
+ void updateCollaboratorPermission(String permissionId, String newPermission);
+
+ String getMyPermission(String taskListId);
+
+ void addToFavorites(String taskListId, String pid);
+
+ List myResponsibleTasks();
+
+ List myFollowedTasks();
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListDetialServiceImpl.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListDetialServiceImpl.java
new file mode 100644
index 0000000..46a7149
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListDetialServiceImpl.java
@@ -0,0 +1,629 @@
+package org.jeecg.modules.demo.tasklist.service.impl;
+
+import org.jeecg.modules.demo.tasklist.entity.TaskList;
+import org.jeecg.modules.demo.tasklist.entity.TaskListDetial;
+import org.jeecg.modules.demo.tasklist.entity.TaskListPermission;
+import org.jeecg.modules.demo.tasklist.mapper.TaskListDetialMapper;
+import org.jeecg.modules.demo.tasklist.mapper.TaskListMapper;
+import org.jeecg.modules.demo.tasklist.mapper.TaskListPermissionMapper;
+import org.jeecg.modules.demo.tasklist.service.ITaskListDetialService;
+import org.jeecg.modules.demo.tasklist.vo.CreateTaskReq;
+import org.jeecg.modules.demo.tasklist.vo.MoveTaskReq;
+import org.jeecg.common.system.api.ISysBaseAPI;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.oConvertUtils;
+import org.apache.shiro.SecurityUtils;
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import java.util.Date;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import lombok.extern.slf4j.Slf4j;
+import java.util.Set;
+
+@Service
+public class TaskListDetialServiceImpl extends ServiceImpl implements ITaskListDetialService {
+
+ @Autowired
+ private TaskListDetialMapper taskListDetialMapper;
+ @Autowired
+ private TaskListMapper taskListMapper;
+ @Autowired
+ private TaskListPermissionMapper taskListPermissionMapper;
+ @Autowired
+ private ISysBaseAPI sysBaseAPI;
+
+ @Override
+ public List selectByMainId(String mainId) {
+ return taskListDetialMapper.selectByMainId(mainId);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public TaskListDetial createTask(CreateTaskReq req) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ String permission = getPermission(req.getMainId(), userId);
+ if (permission == null) {
+ throw new RuntimeException("无权限在此清单中创建任务");
+ }
+ if ("3".equals(permission)) {
+ throw new RuntimeException("可阅读者无法创建任务");
+ }
+
+ if ("1".equals(req.getType()) && oConvertUtils.isEmpty(req.getPid())) {
+ ensureDefaultGroup(req.getMainId());
+ TaskListDetial defaultGroup = findDefaultGroup(req.getMainId());
+ if (defaultGroup != null) {
+ req.setPid(defaultGroup.getId());
+ }
+ }
+
+ TaskListDetial entity = new TaskListDetial();
+ entity.setMainId(req.getMainId());
+ entity.setTaskName(req.getTaskName());
+ entity.setTaskDesc(req.getTaskDesc());
+ entity.setPriority(req.getPriority());
+ entity.setType(req.getType() != null ? req.getType() : "1");
+ entity.setPid(normalizePid(req.getPid()));
+ entity.setTaskStatus(0);
+ entity.setSubTaskCount(0);
+ entity.setCompletedSubTaskCount(0);
+ entity.setHasChild("0");
+ entity.setDelFlag("0");
+ entity.setSysOrgCode(loginUser.getOrgCode());
+
+ if ("1".equals(req.getType()) && oConvertUtils.isNotEmpty(req.getAssigneeId())) {
+ entity.setAssigneeId(req.getAssigneeId());
+ entity.setAssigneeName(translateUserIdsToNames(req.getAssigneeId()));
+ entity.setAssignId(userId);
+ entity.setAssignName(loginUser.getRealname());
+ }
+
+ if (req.getStartTime() != null) {
+ entity.setStartTime(req.getStartTime());
+ }
+ if (req.getEndTime() != null) {
+ entity.setEndTime(req.getEndTime());
+ }
+
+ if (req.getSortOrder() != null) {
+ taskListDetialMapper.shiftSortOrderUp(req.getMainId(), normalizePid(req.getPid()), req.getSortOrder());
+ entity.setSortOrder(req.getSortOrder());
+ } else {
+ Integer maxSort = taskListDetialMapper.getMaxSortOrder(req.getMainId(), normalizePid(req.getPid()));
+ entity.setSortOrder(maxSort + 1);
+ }
+
+ taskListDetialMapper.insert(entity);
+
+ if ("1".equals(entity.getType()) && oConvertUtils.isNotEmpty(entity.getPid())) {
+ TaskListDetial parent = taskListDetialMapper.selectById(entity.getPid());
+ if (parent != null && "1".equals(parent.getType())) {
+ taskListDetialMapper.incrementSubTaskCount(parent.getId());
+ }
+ }
+
+ return entity;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void editTask(TaskListDetial task) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskListDetial existing = taskListDetialMapper.selectById(task.getId());
+ if (existing == null) {
+ throw new RuntimeException("任务不存在");
+ }
+
+ String permission = getPermission(existing.getMainId(), userId);
+ if (permission == null) {
+ throw new RuntimeException("无权限编辑此任务");
+ }
+ if ("3".equals(permission)) {
+ String assigneeId = existing.getAssigneeId();
+ if (oConvertUtils.isEmpty(assigneeId) || !assigneeId.contains(userId)) {
+ throw new RuntimeException("可阅读者只能编辑自己负责的任务");
+ }
+ task.setAssigneeId(existing.getAssigneeId());
+ task.setAssigneeName(existing.getAssigneeName());
+ }
+
+ TaskList taskListEntity = taskListMapper.selectById(existing.getMainId());
+ if (taskListEntity != null) {
+ Integer listSecLevel = taskListEntity.getSecretLevel();
+ if (listSecLevel == null) {
+ listSecLevel = 1;
+ }
+ if (task.getAssigneeId() != null) {
+ String filtered = filterUsersBySecLevel(task.getAssigneeId(), listSecLevel);
+ if (!Objects.equals(filtered, task.getAssigneeId())) {
+ log.warn(String.format("editTask 密级过滤: 清单[%s] 负责人 原始[%s] 过滤后[%s]",
+ taskListEntity.getId(), task.getAssigneeId(), filtered));
+ }
+ task.setAssigneeId(filtered);
+ if (oConvertUtils.isEmpty(filtered)) {
+ task.setAssigneeName("");
+ }
+ }
+ if (task.getParticipantId() != null) {
+ String filtered = filterUsersBySecLevel(task.getParticipantId(), listSecLevel);
+ if (!Objects.equals(filtered, task.getParticipantId())) {
+ log.warn(String.format("editTask 密级过滤: 清单[%s] 参与人 原始[%s] 过滤后[%s]",
+ taskListEntity.getId(), task.getParticipantId(), filtered));
+ }
+ task.setParticipantId(filtered);
+ if (oConvertUtils.isEmpty(filtered)) {
+ task.setParticipantName("");
+ }
+ }
+ if (task.getFollowersId() != null) {
+ String filtered = filterUsersBySecLevel(task.getFollowersId(), listSecLevel);
+ if (!Objects.equals(filtered, task.getFollowersId())) {
+ log.warn(String.format("editTask 密级过滤: 清单[%s] 关注人 原始[%s] 过滤后[%s]",
+ taskListEntity.getId(), task.getFollowersId(), filtered));
+ }
+ task.setFollowersId(filtered);
+ if (oConvertUtils.isEmpty(filtered)) {
+ task.setFollowersName("");
+ }
+ }
+ }
+
+ if (task.getAssigneeId() != null) {
+ if (oConvertUtils.isNotEmpty(task.getAssigneeId()) && oConvertUtils.isEmpty(task.getAssigneeName())) {
+ task.setAssigneeName(translateUserIdsToNames(task.getAssigneeId()));
+ }
+ if (oConvertUtils.isEmpty(task.getAssigneeId())) {
+ task.setAssigneeName("");
+ }
+ }
+
+ if (task.getParticipantId() != null) {
+ if (oConvertUtils.isNotEmpty(task.getParticipantId()) && oConvertUtils.isEmpty(task.getParticipantName())) {
+ task.setParticipantName(translateUserIdsToNames(task.getParticipantId()));
+ }
+ if (oConvertUtils.isEmpty(task.getParticipantId())) {
+ task.setParticipantName("");
+ }
+ }
+
+ if (task.getFollowersId() != null) {
+ if (oConvertUtils.isNotEmpty(task.getFollowersId()) && oConvertUtils.isEmpty(task.getFollowersName())) {
+ task.setFollowersName(translateUserIdsToNames(task.getFollowersId()));
+ }
+ if (oConvertUtils.isEmpty(task.getFollowersId())) {
+ task.setFollowersName("");
+ }
+ }
+ boolean clearStartTime = task.getStartTime() == null && existing.getStartTime() != null;
+ boolean clearEndTime = task.getEndTime() == null && existing.getEndTime() != null;
+
+ taskListDetialMapper.updateById(task);
+
+ if (clearStartTime || clearEndTime) {
+ UpdateWrapper uw = new UpdateWrapper<>();
+ uw.eq("id", task.getId());
+ if (clearStartTime) uw.set("start_time", null);
+ if (clearEndTime) uw.set("end_time", null);
+ taskListDetialMapper.update(null, uw);
+ }
+
+ taskListDetialMapper.appendAssigner(task.getId(), userId, loginUser.getRealname());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteTask(String taskId) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskListDetial existing = taskListDetialMapper.selectById(taskId);
+ if (existing == null) {
+ throw new RuntimeException("任务不存在");
+ }
+
+ String permission = getPermission(existing.getMainId(), userId);
+ if (permission == null) {
+ throw new RuntimeException("无权限删除此任务");
+ }
+ if ("3".equals(permission)) {
+ throw new RuntimeException("可阅读者无法删除任务");
+ }
+
+ if ("0".equals(existing.getType()) && existing.getIsDefault() != null && existing.getIsDefault() == 1) {
+ throw new RuntimeException("默认分组不能删除");
+ }
+
+ if ("0".equals(existing.getType())) {
+ taskListDetialMapper.resetPidToNull(taskId, existing.getMainId());
+ }
+
+ List children = taskListDetialMapper.selectChildrenByPid(taskId);
+ for (TaskListDetial child : children) {
+ deleteTaskRecursive(child);
+ }
+
+ taskListDetialMapper.deleteById(taskId);
+
+ if (oConvertUtils.isNotEmpty(existing.getPid())) {
+ TaskListDetial parent = taskListDetialMapper.selectById(existing.getPid());
+ if (parent != null && "1".equals(parent.getType())) {
+ taskListDetialMapper.decrementSubTaskCount(parent.getId());
+ if (existing.getTaskStatus() != null && existing.getTaskStatus() == 1) {
+ taskListDetialMapper.decrementCompletedSubTaskCount(parent.getId());
+ }
+ Integer childCount = taskListDetialMapper.countChildrenByPid(parent.getId());
+ if (childCount == null || childCount == 0) {
+ taskListDetialMapper.updateHasChild(parent.getId(), "0");
+ }
+ }
+ }
+ }
+
+ private void deleteTaskRecursive(TaskListDetial task) {
+ List children = taskListDetialMapper.selectChildrenByPid(task.getId());
+ for (TaskListDetial child : children) {
+ deleteTaskRecursive(child);
+ }
+ taskListDetialMapper.deleteById(task.getId());
+ }
+
+ @Override
+ public List queryAllByMainId(String mainId) {
+ List list = taskListDetialMapper.selectAllByMainId(mainId);
+ fillCreateByName(list);
+ return list;
+ }
+
+ @Override
+ public List queryTopLevelByMainId(String mainId) {
+ List list = taskListDetialMapper.selectTopLevelByMainId(mainId);
+ fillCreateByName(list);
+ return list;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void toggleStatus(String taskId) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskListDetial existing = taskListDetialMapper.selectById(taskId);
+ if (existing == null) {
+ throw new RuntimeException("任务不存在");
+ }
+
+ String permission = getPermission(existing.getMainId(), userId);
+ if (permission == null) {
+ throw new RuntimeException("无权限操作此任务");
+ }
+ if ("3".equals(permission)) {
+ String assigneeId = existing.getAssigneeId();
+ if (oConvertUtils.isEmpty(assigneeId) || !assigneeId.contains(userId)) {
+ throw new RuntimeException("可阅读者只能操作自己负责的任务");
+ }
+ }
+
+ int oldStatus = existing.getTaskStatus() != null ? existing.getTaskStatus() : 0;
+ int newStatus = oldStatus == 0 ? 1 : 0;
+ java.util.Date completeTime = oldStatus == 0 ? new java.util.Date() : null;
+
+ taskListDetialMapper.toggleTaskStatus(taskId, loginUser.getUsername(), newStatus, completeTime);
+
+ if (oConvertUtils.isNotEmpty(existing.getPid())) {
+ TaskListDetial parent = taskListDetialMapper.selectById(existing.getPid());
+ if (parent != null && "1".equals(parent.getType())) {
+ if (oldStatus == 0) {
+ taskListDetialMapper.incrementCompletedSubTaskCount(parent.getId());
+ } else {
+ taskListDetialMapper.decrementCompletedSubTaskCount(parent.getId());
+ }
+ }
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void moveTask(MoveTaskReq req) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskListDetial task = taskListDetialMapper.selectById(req.getTaskId());
+ if (task == null) {
+ throw new RuntimeException("任务不存在");
+ }
+
+ String permission = getPermission(task.getMainId(), userId);
+ if (permission == null) {
+ throw new RuntimeException("无权限移动此任务");
+ }
+ if ("3".equals(permission)) {
+ throw new RuntimeException("可阅读者无法移动任务");
+ }
+
+ String oldPid = normalizePid(task.getPid());
+ String newPid = normalizePid(req.getTargetPid());
+
+ boolean pidChanged = (oldPid == null && newPid != null) || (oldPid != null && !oldPid.equals(newPid));
+
+ // 调用纯排序方法
+ reorderTaskItem(req.getTaskId(), task.getMainId(), oldPid, req.getTargetSortOrder(), newPid, false);
+
+ // 仅在 pid 变更时更新父子计数
+ if (pidChanged) {
+ // 清除旧父任务的副作用
+ if (oConvertUtils.isNotEmpty(oldPid)) {
+ TaskListDetial oldParent = taskListDetialMapper.selectById(oldPid);
+ if (oldParent != null && "1".equals(oldParent.getType())) {
+ taskListDetialMapper.decrementSubTaskCount(oldPid);
+ Integer childCount = taskListDetialMapper.countChildrenByPid(oldPid);
+ if (childCount == null || childCount == 0) {
+ taskListDetialMapper.updateHasChild(oldPid, "0");
+ }
+ }
+ }
+
+ // 设置新父任务的副作用
+ if (oConvertUtils.isNotEmpty(newPid)) {
+ TaskListDetial newParent = taskListDetialMapper.selectById(newPid);
+ if (newParent != null && "1".equals(newParent.getType())) {
+ taskListDetialMapper.incrementSubTaskCount(newPid);
+ }
+ taskListDetialMapper.updateHasChild(newPid, "1");
+ }
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void moveTaskGroup(String taskGroupId, Integer targetSortOrder) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskListDetial group = taskListDetialMapper.selectById(taskGroupId);
+ if (group == null || !"0".equals(group.getType())) {
+ throw new RuntimeException("分组不存在");
+ }
+ String mainId = group.getMainId();
+
+ String permission = getPermission(mainId, userId);
+ if (permission == null || "3".equals(permission)) {
+ throw new RuntimeException("无权限移动此分组");
+ }
+
+ reorderTaskItem(taskGroupId, mainId, null, targetSortOrder, null, true);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void reorderTaskItem(String movedId, String mainId, String pid,
+ Integer targetPosition, String newPid, boolean isGroup) {
+ TaskListDetial moved = taskListDetialMapper.selectById(movedId);
+ if (moved == null) {
+ throw new RuntimeException("记录不存在");
+ }
+
+ // 当 newPid 有值时使用 newPid(跨组移动),否则使用 pid(同组内移动)
+ // 关键:newPid="" 表示拖到根级别,此时应设为 null
+ String effectiveNewPid;
+ if (newPid != null) {
+ effectiveNewPid = newPid.isEmpty() ? null : newPid;
+ } else {
+ effectiveNewPid = pid;
+ }
+
+ LambdaQueryWrapper query = new LambdaQueryWrapper<>();
+ query.eq(TaskListDetial::getMainId, mainId);
+ query.eq(TaskListDetial::getDelFlag, "0");
+ if (isGroup) {
+ query.eq(TaskListDetial::getType, "0");
+ query.and(w -> w.isNull(TaskListDetial::getPid).or().eq(TaskListDetial::getPid, ""));
+ } else {
+ if (effectiveNewPid != null) {
+ query.eq(TaskListDetial::getPid, effectiveNewPid);
+ } else {
+ query.isNull(TaskListDetial::getPid);
+ }
+ }
+ query.ne(TaskListDetial::getId, movedId);
+ query.orderByAsc(TaskListDetial::getSortOrder);
+ List siblings = taskListDetialMapper.selectList(query);
+
+ int pos = targetPosition != null ? targetPosition : siblings.size() + 1;
+ pos = Math.max(1, Math.min(pos, siblings.size() + 1));
+
+ int sort = 1;
+ for (int i = 0; i < siblings.size(); i++) {
+ if (sort == pos) {
+ sort++;
+ }
+ TaskListDetial sibling = siblings.get(i);
+ if (!sibling.getSortOrder().equals(sort)) {
+ taskListDetialMapper.updateSortOrder(sibling.getId(), sort);
+ }
+ sort++;
+ }
+
+ taskListDetialMapper.updatePidAndSort(movedId, effectiveNewPid, pos);
+ }
+
+ @Override
+ public void followTask(String taskId) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+ String userName = loginUser.getRealname();
+ taskListDetialMapper.appendFollower(taskId, userId, userName);
+ }
+
+ @Override
+ public void unfollowTask(String taskId) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+ String userName = loginUser.getRealname();
+ taskListDetialMapper.removeFollower(taskId, userId, userName);
+ }
+
+ @Override
+ public List loadSubTasks(String parentTaskId, Integer pageNo, Integer pageSize) {
+ if (pageNo == null || pageNo < 1) pageNo = 1;
+ if (pageSize == null || pageSize < 1) pageSize = 20;
+ int offset = (pageNo - 1) * pageSize;
+ List list = taskListDetialMapper.selectSubTasksByPage(parentTaskId, offset, pageSize);
+ fillCreateByName(list);
+ return list;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void ensureDefaultGroup(String mainId) {
+ TaskListDetial defaultGroup = findDefaultGroup(mainId);
+ if (defaultGroup == null) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ TaskListDetial group = new TaskListDetial();
+ group.setMainId(mainId);
+ group.setTaskName("默认分组");
+ group.setType("0");
+ group.setPid(null);
+ group.setSortOrder(0);
+ group.setIsDefault(1);
+ group.setHasChild("0");
+ group.setSubTaskCount(0);
+ group.setCompletedSubTaskCount(0);
+ group.setDelFlag("0");
+ taskListDetialMapper.insert(group);
+ }
+ }
+
+ private TaskListDetial findDefaultGroup(String mainId) {
+ LambdaQueryWrapper query = new LambdaQueryWrapper<>();
+ query.eq(TaskListDetial::getMainId, mainId);
+ query.eq(TaskListDetial::getType, "0");
+ query.eq(TaskListDetial::getIsDefault, 1);
+ query.last("LIMIT 1");
+ TaskListDetial result = taskListDetialMapper.selectOne(query);
+ if (result != null) {
+ return result;
+ }
+
+ query = new LambdaQueryWrapper<>();
+ query.eq(TaskListDetial::getMainId, mainId);
+ query.eq(TaskListDetial::getType, "0");
+ query.isNull(TaskListDetial::getIsDefault);
+ query.orderByAsc(TaskListDetial::getSortOrder);
+ query.last("LIMIT 1");
+ result = taskListDetialMapper.selectOne(query);
+ if (result != null) {
+ result.setIsDefault(1);
+ taskListDetialMapper.updateById(result);
+ }
+ return result;
+ }
+
+ private String getPermission(String mainId, String userId) {
+ LambdaQueryWrapper query = new LambdaQueryWrapper<>();
+ query.eq(TaskListPermission::getMainId, mainId);
+ query.eq(TaskListPermission::getUserId, userId);
+ TaskListPermission perm = taskListPermissionMapper.selectOne(query);
+ return perm != null ? perm.getPermission() : null;
+ }
+
+ private String normalizePid(String pid) {
+ return oConvertUtils.isNotEmpty(pid) ? pid : null;
+ }
+
+ private void fillCreateByName(List list) {
+ if (list == null || list.isEmpty()) {
+ return;
+ }
+ for (TaskListDetial task : list) {
+ if (oConvertUtils.isNotEmpty(task.getCreateBy())) {
+ List users = sysBaseAPI.queryUsersByUsernames(task.getCreateBy());
+ if (users != null && !users.isEmpty()) {
+ task.setCreateByName(users.get(0).getString("realname"));
+ }
+ }
+ if (oConvertUtils.isNotEmpty(task.getAssigneeId()) && oConvertUtils.isEmpty(task.getAssigneeName())) {
+ task.setAssigneeName(translateUserIdsToNames(task.getAssigneeId()));
+ }
+ if (oConvertUtils.isNotEmpty(task.getParticipantId()) && oConvertUtils.isEmpty(task.getParticipantName())) {
+ task.setParticipantName(translateUserIdsToNames(task.getParticipantId()));
+ }
+ if (oConvertUtils.isNotEmpty(task.getFollowersId()) && oConvertUtils.isEmpty(task.getFollowersName())) {
+ task.setFollowersName(translateUserIdsToNames(task.getFollowersId()));
+ }
+ }
+ }
+
+ private String translateUserIdsToNames(String ids) {
+ if (oConvertUtils.isEmpty(ids)) {
+ return null;
+ }
+ String[] idArr = ids.split(",");
+ StringBuilder names = new StringBuilder();
+ for (String id : idArr) {
+ if (oConvertUtils.isNotEmpty(id)) {
+ LoginUser user = sysBaseAPI.getUserById(id.trim());
+ if (user != null) {
+ if (names.length() > 0) {
+ names.append(",");
+ }
+ names.append(user.getRealname());
+ }
+ }
+ }
+ return names.length() > 0 ? names.toString() : null;
+ }
+
+ private String mergeIds(String existingIds, String newIds) {
+ Set idSet = new LinkedHashSet<>();
+ if (oConvertUtils.isNotEmpty(existingIds)) {
+ for (String id : existingIds.split(",")) {
+ String trimmed = id.trim();
+ if (oConvertUtils.isNotEmpty(trimmed)) {
+ idSet.add(trimmed);
+ }
+ }
+ }
+ if (oConvertUtils.isNotEmpty(newIds)) {
+ for (String id : newIds.split(",")) {
+ String trimmed = id.trim();
+ if (oConvertUtils.isNotEmpty(trimmed)) {
+ idSet.add(trimmed);
+ }
+ }
+ }
+ return idSet.isEmpty() ? null : String.join(",", idSet);
+ }
+
+ private String filterUsersBySecLevel(String userIds, Integer listSecLevel) {
+ if (oConvertUtils.isEmpty(userIds)) return userIds;
+ if (listSecLevel == null) {
+ listSecLevel = 1;
+ }
+ String[] ids = userIds.split(",");
+ java.util.List validIds = new java.util.ArrayList<>();
+ for (String id : ids) {
+ LoginUser user = sysBaseAPI.getUserById(id.trim());
+ if (user == null) {
+ continue;
+ }
+ Integer userSecLevel = user.getUserSecurityLevel();
+ if (userSecLevel == null) {
+ userSecLevel = 3;
+ }
+ if (userSecLevel > listSecLevel) {
+ validIds.add(id.trim());
+ }
+ }
+ return String.join(",", validIds);
+ }
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListFavoriteServiceImpl.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListFavoriteServiceImpl.java
new file mode 100644
index 0000000..c758fbd
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListFavoriteServiceImpl.java
@@ -0,0 +1,122 @@
+package org.jeecg.modules.demo.tasklist.service.impl;
+
+import org.jeecg.modules.demo.tasklist.entity.TaskListFavorite;
+import org.jeecg.modules.demo.tasklist.mapper.TaskListFavoriteMapper;
+import org.jeecg.modules.demo.tasklist.service.ITaskListFavoriteService;
+import org.springframework.stereotype.Service;
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @Description: 任务清单收藏表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+@Service
+public class TaskListFavoriteServiceImpl extends ServiceImpl implements ITaskListFavoriteService {
+
+ @Autowired
+ private TaskListFavoriteMapper taskListFavoriteMapper;
+
+ @Override
+ public List selectByMainId(String mainId) {
+ return taskListFavoriteMapper.selectByMainId(mainId);
+ }
+
+ @Override
+ public Integer getMaxSortOrder(String userId, String pid) {
+ Integer max = taskListFavoriteMapper.selectMaxSortOrder(userId, pid);
+ return max != null ? max : 0;
+ }
+
+ @Override
+ public Integer getMaxSortOrderByType(String userId, String pid, String type) {
+ Integer max = taskListFavoriteMapper.selectMaxSortOrderByType(userId, pid, type);
+ return max != null ? max : 0;
+ }
+
+ @Override
+ public void shiftSortOrderUp(String userId, String pid, Integer fromSort) {
+ taskListFavoriteMapper.shiftSortOrderUp(userId, pid, fromSort);
+ }
+
+ @Override
+ public void shiftSortOrderDown(String userId, String pid, Integer fromSort) {
+ taskListFavoriteMapper.shiftSortOrderDown(userId, pid, fromSort);
+ }
+
+ @Override
+ public void shiftSortOrderUpByType(String userId, String type, Integer fromSort) {
+ taskListFavoriteMapper.shiftSortOrderUpByType(userId, type, fromSort);
+ }
+
+ @Override
+ public void shiftSortOrderDownByType(String userId, String type, Integer fromSort) {
+ taskListFavoriteMapper.shiftSortOrderDownByType(userId, type, fromSort);
+ }
+
+ @Override
+ public Integer countChildren(String userId, String pid) {
+ return taskListFavoriteMapper.countChildrenByPid(userId, pid);
+ }
+
+ @Override
+ public void updatePidAndSort(String favoriteId, String pid, Integer sortOrder) {
+ taskListFavoriteMapper.updatePidAndSort(favoriteId, pid, sortOrder);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void reorderItem(String movedId, String userId, String pid, String type,
+ Integer targetPosition, String newPid) {
+ TaskListFavorite moved = taskListFavoriteMapper.selectById(movedId);
+ if (moved == null) {
+ throw new RuntimeException("记录不存在");
+ }
+
+ // 当 newPid 有值时使用 newPid(跨组移动),否则使用 pid(同组内移动)
+ // 关键:newPid="" 表示拖到根级别(无分组),此时应设为 null
+ String effectiveNewPid;
+ if (newPid != null) {
+ effectiveNewPid = newPid.isEmpty() ? null : newPid;
+ } else {
+ effectiveNewPid = pid;
+ }
+
+ LambdaQueryWrapper query = new LambdaQueryWrapper<>();
+ query.eq(TaskListFavorite::getUserId, userId);
+ query.eq(TaskListFavorite::getDelFlag, "0");
+ if (type != null) {
+ query.eq(TaskListFavorite::getType, type);
+ }
+ if (effectiveNewPid != null) {
+ query.eq(TaskListFavorite::getPid, effectiveNewPid);
+ } else {
+ query.isNull(TaskListFavorite::getPid);
+ }
+ query.ne(TaskListFavorite::getId, movedId);
+ query.orderByAsc(TaskListFavorite::getSortOrder);
+ List siblings = taskListFavoriteMapper.selectList(query);
+
+ int pos = targetPosition != null ? targetPosition : siblings.size() + 1;
+ pos = Math.max(1, Math.min(pos, siblings.size() + 1));
+
+ int sort = 1;
+ for (int i = 0; i < siblings.size(); i++) {
+ if (sort == pos) {
+ sort++;
+ }
+ TaskListFavorite sibling = siblings.get(i);
+ if (!sibling.getSortOrder().equals(sort)) {
+ taskListFavoriteMapper.updateSortOrder(sibling.getId(), sort);
+ }
+ sort++;
+ }
+
+ taskListFavoriteMapper.updatePidAndSort(movedId, effectiveNewPid, pos);
+ }
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListPermissionServiceImpl.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListPermissionServiceImpl.java
new file mode 100644
index 0000000..fc6a148
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListPermissionServiceImpl.java
@@ -0,0 +1,27 @@
+package org.jeecg.modules.demo.tasklist.service.impl;
+
+import org.jeecg.modules.demo.tasklist.entity.TaskListPermission;
+import org.jeecg.modules.demo.tasklist.mapper.TaskListPermissionMapper;
+import org.jeecg.modules.demo.tasklist.service.ITaskListPermissionService;
+import org.springframework.stereotype.Service;
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @Description: 任务清单权限表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+@Service
+public class TaskListPermissionServiceImpl extends ServiceImpl implements ITaskListPermissionService {
+
+ @Autowired
+ private TaskListPermissionMapper taskListPermissionMapper;
+
+ @Override
+ public List selectByMainId(String mainId) {
+ return taskListPermissionMapper.selectByMainId(mainId);
+ }
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListServiceImpl.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListServiceImpl.java
new file mode 100644
index 0000000..0a5d63b
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/service/impl/TaskListServiceImpl.java
@@ -0,0 +1,862 @@
+package org.jeecg.modules.demo.tasklist.service.impl;
+
+import org.jeecg.modules.demo.tasklist.entity.TaskList;
+import org.jeecg.modules.demo.tasklist.entity.TaskListDetial;
+import org.jeecg.modules.demo.tasklist.entity.TaskListPermission;
+import org.jeecg.modules.demo.tasklist.entity.TaskListFavorite;
+import org.jeecg.modules.demo.tasklist.mapper.TaskListDetialMapper;
+import org.jeecg.modules.demo.tasklist.mapper.TaskListPermissionMapper;
+import org.jeecg.modules.demo.tasklist.mapper.TaskListFavoriteMapper;
+import org.jeecg.modules.demo.tasklist.mapper.TaskListMapper;
+import org.jeecg.modules.demo.tasklist.service.ITaskListService;
+import org.jeecg.modules.demo.tasklist.service.ITaskListFavoriteService;
+import org.jeecg.modules.demo.tasklist.vo.CreateTaskListReq;
+import org.jeecg.modules.demo.tasklist.vo.CreateTaskListGroupReq;
+import org.jeecg.modules.demo.tasklist.vo.MoveTaskListReq;
+import org.jeecg.modules.demo.tasklist.vo.AddCollaboratorReq;
+import org.jeecg.modules.demo.tasklist.vo.CollaboratorVO;
+import org.jeecg.common.system.api.ISysBaseAPI;
+import org.jeecg.common.system.vo.LoginUser;
+import org.apache.shiro.SecurityUtils;
+import org.jeecg.common.util.oConvertUtils;
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * @Description: 任务清单表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+@Service
+public class TaskListServiceImpl extends ServiceImpl implements ITaskListService {
+
+ @Autowired
+ private TaskListMapper taskListMapper;
+ @Autowired
+ private TaskListDetialMapper taskListDetialMapper;
+ @Autowired
+ private TaskListPermissionMapper taskListPermissionMapper;
+ @Autowired
+ private TaskListFavoriteMapper taskListFavoriteMapper;
+ @Autowired
+ private ITaskListFavoriteService taskListFavoriteService;
+ @Autowired
+ private ISysBaseAPI sysBaseAPI;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void saveMain(TaskList taskList, List taskListDetialList,List taskListPermissionList,List taskListFavoriteList) {
+ taskListMapper.insert(taskList);
+ if(taskListDetialList!=null && taskListDetialList.size()>0) {
+ for(TaskListDetial entity:taskListDetialList) {
+ //外键设置
+ entity.setMainId(taskList.getId());
+ taskListDetialMapper.insert(entity);
+ }
+ }
+ if(taskListPermissionList!=null && taskListPermissionList.size()>0) {
+ for(TaskListPermission entity:taskListPermissionList) {
+ //外键设置
+ entity.setMainId(taskList.getId());
+ taskListPermissionMapper.insert(entity);
+ }
+ }
+ if(taskListFavoriteList!=null && taskListFavoriteList.size()>0) {
+ for(TaskListFavorite entity:taskListFavoriteList) {
+ //外键设置
+ entity.setMainId(taskList.getId());
+ taskListFavoriteMapper.insert(entity);
+ }
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateMain(TaskList taskList,List taskListDetialList,List taskListPermissionList,List taskListFavoriteList) {
+ taskListMapper.updateById(taskList);
+
+ //1.先删除子表数据
+ taskListDetialMapper.deleteByMainId(taskList.getId());
+ taskListPermissionMapper.deleteByMainId(taskList.getId());
+ taskListFavoriteMapper.deleteByMainId(taskList.getId());
+
+ //2.子表数据重新插入
+ if(taskListDetialList!=null && taskListDetialList.size()>0) {
+ for(TaskListDetial entity:taskListDetialList) {
+ //外键设置
+ entity.setMainId(taskList.getId());
+ taskListDetialMapper.insert(entity);
+ }
+ }
+ if(taskListPermissionList!=null && taskListPermissionList.size()>0) {
+ for(TaskListPermission entity:taskListPermissionList) {
+ //外键设置
+ entity.setMainId(taskList.getId());
+ taskListPermissionMapper.insert(entity);
+ }
+ }
+ if(taskListFavoriteList!=null && taskListFavoriteList.size()>0) {
+ for(TaskListFavorite entity:taskListFavoriteList) {
+ //外键设置
+ entity.setMainId(taskList.getId());
+ taskListFavoriteMapper.insert(entity);
+ }
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void delMain(String id) {
+ taskListDetialMapper.deleteByMainId(id);
+ taskListPermissionMapper.deleteByMainId(id);
+ taskListFavoriteMapper.deleteByMainId(id);
+ taskListMapper.deleteById(id);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void delBatchMain(Collection extends Serializable> idList) {
+ for(Serializable id:idList) {
+ taskListDetialMapper.deleteByMainId(id.toString());
+ taskListPermissionMapper.deleteByMainId(id.toString());
+ taskListFavoriteMapper.deleteByMainId(id.toString());
+ taskListMapper.deleteById(id);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String createTaskList(CreateTaskListReq req) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+ Integer userSecLevel = loginUser.getUserSecurityLevel();
+ if (userSecLevel == null) {
+ userSecLevel = 3;
+ }
+
+ if (req.getSecretLevel() != null && req.getSecretLevel() >= userSecLevel) {
+ throw new RuntimeException("您无权创建该密级的清单");
+ }
+
+ if (oConvertUtils.isNotEmpty(req.getPid())) {
+ TaskListFavorite groupFav = taskListFavoriteMapper.selectById(req.getPid());
+ if (groupFav == null || !"0".equals(groupFav.getType()) || !userId.equals(groupFav.getUserId())) {
+ throw new RuntimeException("目标分组不存在或无权限");
+ }
+ }
+
+ TaskList taskList = new TaskList();
+ taskList.setTasklistName(req.getTasklistName());
+ taskList.setSecretLevel(req.getSecretLevel());
+ taskList.setSecretText(getSecretText(req.getSecretLevel()));
+ taskListMapper.insert(taskList);
+
+ TaskListDetial defaultGroup = new TaskListDetial();
+ defaultGroup.setMainId(taskList.getId());
+ defaultGroup.setTaskName("默认分组");
+ defaultGroup.setType("0");
+ defaultGroup.setPid(null);
+ defaultGroup.setSortOrder(0);
+ defaultGroup.setIsDefault(1);
+ defaultGroup.setHasChild("0");
+ defaultGroup.setSubTaskCount(0);
+ defaultGroup.setCompletedSubTaskCount(0);
+ defaultGroup.setDelFlag("0");
+ taskListDetialMapper.insert(defaultGroup);
+
+ TaskListPermission permission = new TaskListPermission();
+ permission.setMainId(taskList.getId());
+ permission.setUserId(userId);
+ permission.setPermission("1");
+ taskListPermissionMapper.insert(permission);
+
+ if (oConvertUtils.isNotEmpty(req.getPid())) {
+ TaskListFavorite parentFav = new TaskListFavorite();
+ parentFav.setId(req.getPid());
+ parentFav.setHasChild("1");
+ taskListFavoriteMapper.updateById(parentFav);
+ }
+
+ return taskList.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public String createTaskListGroup(CreateTaskListGroupReq req) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ Integer sortOrder = 1;
+ taskListFavoriteMapper.shiftSortOrderUpByType(userId, "0", sortOrder);
+
+ TaskListFavorite favorite = new TaskListFavorite();
+ favorite.setMainId(null);
+ favorite.setUserId(userId);
+ favorite.setType("0");
+ favorite.setPid(null);
+ favorite.setHasChild("0");
+ favorite.setSortOrder(sortOrder);
+ favorite.setTasklistName(req.getTasklistName());
+ taskListFavoriteMapper.insert(favorite);
+
+ return favorite.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void moveTaskList(MoveTaskListReq req) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskListFavorite favorite = taskListFavoriteMapper.selectById(req.getFavoriteId());
+ if (favorite == null || !userId.equals(favorite.getUserId())) {
+ throw new RuntimeException("无权限操作此记录");
+ }
+ if ("0".equals(favorite.getType())) {
+ throw new RuntimeException("分组不支持移动操作");
+ }
+
+ String oldPid = normalizePid(favorite.getPid());
+
+ String newPid = null;
+ if (req.getTargetGroupId() != null) {
+ if (oConvertUtils.isNotEmpty(req.getTargetGroupId())) {
+ TaskListFavorite targetGroup = taskListFavoriteMapper.selectById(req.getTargetGroupId());
+ if (targetGroup == null || !"0".equals(targetGroup.getType()) || !userId.equals(targetGroup.getUserId())) {
+ throw new RuntimeException("目标分组不存在或无权限");
+ }
+ newPid = normalizePid(req.getTargetGroupId());
+ } else {
+ newPid = "";
+ }
+ }
+
+ // 调用纯排序方法
+ taskListFavoriteService.reorderItem(req.getFavoriteId(), userId, oldPid, "1", req.getSortOrder(), newPid);
+
+ // 维护旧父节点 hasChild
+ if (oConvertUtils.isNotEmpty(oldPid) && !oldPid.equals(newPid)) {
+ Integer remainCount = taskListFavoriteService.countChildren(userId, oldPid);
+ if (remainCount == null || remainCount == 0) {
+ TaskListFavorite oldParent = new TaskListFavorite();
+ oldParent.setId(oldPid);
+ oldParent.setHasChild("0");
+ taskListFavoriteMapper.updateById(oldParent);
+ }
+ }
+
+ // 维护新父节点 hasChild
+ if (oConvertUtils.isNotEmpty(newPid)) {
+ TaskListFavorite newParent = new TaskListFavorite();
+ newParent.setId(newPid);
+ newParent.setHasChild("1");
+ taskListFavoriteMapper.updateById(newParent);
+ }
+ }
+
+ @Override
+ public List getMyFavorites() {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+ Integer userSecLevel = loginUser.getUserSecurityLevel();
+ if (userSecLevel == null) {
+ userSecLevel = 3;
+ }
+
+ QueryWrapper query = new QueryWrapper<>();
+ query.eq("user_id", userId);
+ query.eq("del_flag", "0");
+ query.orderByAsc("type");
+ query.orderByAsc("sort_order");
+ List favorites = taskListFavoriteMapper.selectList(query);
+
+ List mainIds = new java.util.ArrayList<>();
+ for (TaskListFavorite fav : favorites) {
+ if ("1".equals(fav.getType()) && fav.getMainId() != null) {
+ mainIds.add(fav.getMainId());
+ }
+ }
+
+ java.util.Map listMap = new java.util.HashMap<>();
+ if (!mainIds.isEmpty()) {
+ List lists = taskListMapper.selectBatchIds(mainIds);
+ for (TaskList tl : lists) {
+ listMap.put(tl.getId(), tl);
+ }
+ }
+
+ List result = new java.util.ArrayList<>();
+ for (TaskListFavorite fav : favorites) {
+ if ("1".equals(fav.getType()) && fav.getMainId() != null) {
+ TaskList tl = listMap.get(fav.getMainId());
+ if (tl == null) continue;
+
+ Integer listSecLevel = tl.getSecretLevel() != null ? tl.getSecretLevel() : 1;
+ if (userSecLevel <= listSecLevel) {
+ continue;
+ }
+ fav.setTasklistName(tl.getTasklistName());
+ fav.setSecretLevel(tl.getSecretLevel());
+ fav.setSecretText(tl.getSecretText());
+
+ LambdaQueryWrapper permQuery = new LambdaQueryWrapper<>();
+ permQuery.eq(TaskListPermission::getMainId, fav.getMainId());
+ permQuery.eq(TaskListPermission::getUserId, userId);
+ TaskListPermission perm = taskListPermissionMapper.selectOne(permQuery);
+ if (perm != null) {
+ fav.setPermission(perm.getPermission());
+ }
+ result.add(fav);
+ } else {
+ result.add(fav);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteTaskList(String taskListId) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ LambdaQueryWrapper permCheck = new LambdaQueryWrapper<>();
+ permCheck.eq(TaskListPermission::getMainId, taskListId);
+ permCheck.eq(TaskListPermission::getUserId, userId);
+ permCheck.eq(TaskListPermission::getPermission, "1");
+ Long count = taskListPermissionMapper.selectCount(permCheck);
+ if (count == 0) {
+ throw new RuntimeException("仅所有者可删除任务清单");
+ }
+
+ taskListMapper.deleteById(taskListId);
+
+ LambdaQueryWrapper detailQuery = new LambdaQueryWrapper<>();
+ detailQuery.eq(TaskListDetial::getMainId, taskListId);
+ taskListDetialMapper.delete(detailQuery);
+
+ LambdaQueryWrapper permQuery = new LambdaQueryWrapper<>();
+ permQuery.eq(TaskListPermission::getMainId, taskListId);
+ taskListPermissionMapper.delete(permQuery);
+
+ LambdaQueryWrapper favQuery = new LambdaQueryWrapper<>();
+ favQuery.eq(TaskListFavorite::getMainId, taskListId);
+ List favs = taskListFavoriteMapper.selectList(favQuery);
+ for (TaskListFavorite fav : favs) {
+ String favUserId = fav.getUserId();
+ taskListFavoriteMapper.deleteById(fav.getId());
+ if (fav.getPid() != null) {
+ Integer remainCount = taskListFavoriteService.countChildren(favUserId, fav.getPid());
+ if (remainCount == 0) {
+ TaskListFavorite parentUpdate = new TaskListFavorite();
+ parentUpdate.setId(fav.getPid());
+ parentUpdate.setHasChild("0");
+ taskListFavoriteMapper.updateById(parentUpdate);
+ }
+ }
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void removeFavorite(String favoriteId) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskListFavorite fav = taskListFavoriteMapper.selectById(favoriteId);
+ if (fav == null || !userId.equals(fav.getUserId())) {
+ throw new RuntimeException("无权限操作此记录");
+ }
+
+ taskListFavoriteMapper.deleteById(favoriteId);
+
+ if (fav.getPid() != null) {
+ Integer remainCount = taskListFavoriteService.countChildren(userId, fav.getPid());
+ if (remainCount == 0) {
+ TaskListFavorite parentUpdate = new TaskListFavorite();
+ parentUpdate.setId(fav.getPid());
+ parentUpdate.setHasChild("0");
+ taskListFavoriteMapper.updateById(parentUpdate);
+ }
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void removeFavoriteGroup(String groupId) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskListFavorite groupFav = taskListFavoriteMapper.selectById(groupId);
+ if (groupFav == null || !userId.equals(groupFav.getUserId()) || !"0".equals(groupFav.getType())) {
+ throw new RuntimeException("分组不存在或无权限");
+ }
+
+ taskListFavoriteMapper.deleteById(groupId);
+
+ LambdaQueryWrapper childQuery = new LambdaQueryWrapper<>();
+ childQuery.eq(TaskListFavorite::getPid, groupId);
+ childQuery.eq(TaskListFavorite::getUserId, userId);
+ List children = taskListFavoriteMapper.selectList(childQuery);
+ for (TaskListFavorite child : children) {
+ taskListFavoriteMapper.deleteById(child.getId());
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void renameGroup(String groupId, String newName) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskListFavorite groupFav = taskListFavoriteMapper.selectById(groupId);
+ if (groupFav == null || !userId.equals(groupFav.getUserId()) || !"0".equals(groupFav.getType())) {
+ throw new RuntimeException("分组不存在或无权限");
+ }
+
+ groupFav.setTasklistName(newName);
+ taskListFavoriteMapper.updateById(groupFav);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void moveGroup(String groupId, Integer sortOrder) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskListFavorite group = taskListFavoriteMapper.selectById(groupId);
+ if (group == null || !userId.equals(group.getUserId()) || !"0".equals(group.getType())) {
+ throw new RuntimeException("分组不存在或无权限");
+ }
+
+ taskListFavoriteService.reorderItem(groupId, userId, null, "0", sortOrder, null);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void renameTaskList(String taskListId, String newName) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ TaskList taskList = taskListMapper.selectById(taskListId);
+ if (taskList == null) {
+ throw new RuntimeException("清单不存在");
+ }
+
+ LambdaQueryWrapper permQuery = new LambdaQueryWrapper<>();
+ permQuery.eq(TaskListPermission::getMainId, taskListId);
+ permQuery.eq(TaskListPermission::getUserId, userId);
+ permQuery.in(TaskListPermission::getPermission, "1", "2");
+ boolean hasPermission = taskListPermissionMapper.exists(permQuery);
+
+ if (!hasPermission) {
+ throw new RuntimeException("清单不存在或无权限");
+ }
+
+ taskList.setTasklistName(newName);
+ taskListMapper.updateById(taskList);
+
+ LambdaQueryWrapper favQuery = new LambdaQueryWrapper<>();
+ favQuery.eq(TaskListFavorite::getMainId, taskListId);
+ List favs = taskListFavoriteMapper.selectList(favQuery);
+ for (TaskListFavorite fav : favs) {
+ fav.setTasklistName(newName);
+ taskListFavoriteMapper.updateById(fav);
+ }
+ }
+
+ @Override
+ public List getMyOwnLists() {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+ Integer userSecLevel = loginUser.getUserSecurityLevel();
+ if (userSecLevel == null) {
+ userSecLevel = 3;
+ }
+ final Integer finalUserSecLevel = userSecLevel;
+
+ LambdaQueryWrapper permQuery = new LambdaQueryWrapper<>();
+ permQuery.eq(TaskListPermission::getUserId, userId);
+ permQuery.eq(TaskListPermission::getPermission, "1");
+ List perms = taskListPermissionMapper.selectList(permQuery);
+
+ List mainIds = perms.stream().map(TaskListPermission::getMainId).collect(java.util.stream.Collectors.toList());
+ if (mainIds.isEmpty()) {
+ return java.util.Collections.emptyList();
+ }
+
+ LambdaQueryWrapper query = new LambdaQueryWrapper<>();
+ query.in(TaskList::getId, mainIds);
+ query.and(w -> w.lt(TaskList::getSecretLevel, finalUserSecLevel).or().isNull(TaskList::getSecretLevel));
+ query.orderByAsc(TaskList::getCreateTime);
+ return enrichListSummaries(taskListMapper.selectList(query));
+ }
+
+ @Override
+ public List getAllLists() {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+ Integer userSecLevel = loginUser.getUserSecurityLevel();
+ if (userSecLevel == null) {
+ userSecLevel = 3;
+ }
+ String orgCode = loginUser.getOrgCode();
+
+ List lists = taskListMapper.selectVisibleLists(userId, userSecLevel, orgCode);
+ return enrichListSummaries(lists);
+ }
+
+ @Override
+ public List getMyCollabLists() {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+ Integer userSecLevel = loginUser.getUserSecurityLevel();
+ if (userSecLevel == null) {
+ userSecLevel = 3;
+ }
+ final Integer finalUserSecLevel = userSecLevel;
+
+ LambdaQueryWrapper permQuery = new LambdaQueryWrapper<>();
+ permQuery.eq(TaskListPermission::getUserId, userId);
+ permQuery.ne(TaskListPermission::getPermission, "1");
+ List perms = taskListPermissionMapper.selectList(permQuery);
+
+ List mainIds = perms.stream().map(TaskListPermission::getMainId).collect(java.util.stream.Collectors.toList());
+ if (mainIds.isEmpty()) {
+ return java.util.Collections.emptyList();
+ }
+
+ LambdaQueryWrapper query = new LambdaQueryWrapper<>();
+ query.in(TaskList::getId, mainIds);
+ query.and(w -> w.lt(TaskList::getSecretLevel, finalUserSecLevel).or().isNull(TaskList::getSecretLevel));
+ query.orderByAsc(TaskList::getCreateTime);
+ return enrichListSummaries(taskListMapper.selectList(query));
+ }
+
+ private String normalizePid(String pid) {
+ return oConvertUtils.isNotEmpty(pid) ? pid : null;
+ }
+
+ private List enrichListSummaries(List lists) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ for (TaskList list : lists) {
+ if (list.getCreateTime() != null) {
+ list.setCreateTimeStr(sdf.format(list.getCreateTime()));
+ }
+
+ LambdaQueryWrapper ownerQuery = new LambdaQueryWrapper<>();
+ ownerQuery.eq(TaskListPermission::getMainId, list.getId());
+ ownerQuery.eq(TaskListPermission::getPermission, "1");
+ TaskListPermission owner = taskListPermissionMapper.selectOne(ownerQuery);
+ if (owner != null) {
+ LoginUser ownerUser = sysBaseAPI.getUserById(owner.getUserId());
+ if (ownerUser != null) {
+ list.setOwnerName(ownerUser.getRealname());
+ }
+ }
+
+ LambdaQueryWrapper collabQuery = new LambdaQueryWrapper<>();
+ collabQuery.eq(TaskListPermission::getMainId, list.getId());
+ collabQuery.ne(TaskListPermission::getPermission, "1");
+ List collabs = taskListPermissionMapper.selectList(collabQuery);
+ if (!collabs.isEmpty()) {
+ List collabUserIds = collabs.stream().map(TaskListPermission::getUserId).collect(java.util.stream.Collectors.toList());
+ List collabNames = new java.util.ArrayList<>();
+ for (String collabUserId : collabUserIds) {
+ LoginUser collabUser = sysBaseAPI.getUserById(collabUserId);
+ if (collabUser != null && collabUser.getRealname() != null) {
+ collabNames.add(collabUser.getRealname());
+ }
+ }
+ list.setCollaboratorNames(String.join(", ", collabNames));
+ }
+ }
+ return lists;
+ }
+
+ @Override
+ public List getCollaborators(String taskListId) {
+ LambdaQueryWrapper query = new LambdaQueryWrapper<>();
+ query.eq(TaskListPermission::getMainId, taskListId);
+ query.orderByAsc(TaskListPermission::getCreateTime);
+ List perms = taskListPermissionMapper.selectList(query);
+
+ List result = new java.util.ArrayList<>();
+ for (TaskListPermission perm : perms) {
+ CollaboratorVO vo = new CollaboratorVO();
+ vo.setPermissionId(perm.getId());
+ vo.setUserId(perm.getUserId());
+ vo.setPermission(perm.getPermission());
+ LoginUser user = sysBaseAPI.getUserById(perm.getUserId());
+ if (user != null) {
+ vo.setUsername(user.getRealname());
+ }
+ result.add(vo);
+ }
+ return result;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void addCollaborator(AddCollaboratorReq req) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String currentUserId = loginUser.getId();
+
+ LambdaQueryWrapper ownerCheck = new LambdaQueryWrapper<>();
+ ownerCheck.eq(TaskListPermission::getMainId, req.getTaskListId());
+ ownerCheck.eq(TaskListPermission::getUserId, currentUserId);
+ ownerCheck.eq(TaskListPermission::getPermission, "1");
+ Long ownerCount = taskListPermissionMapper.selectCount(ownerCheck);
+ if (ownerCount == 0) {
+ throw new RuntimeException("仅所有者可添加协作人");
+ }
+
+ if (!"2".equals(req.getPermission()) && !"3".equals(req.getPermission())) {
+ throw new RuntimeException("权限类型无效,仅支持可阅读(2)或可编辑(3)");
+ }
+
+ TaskList taskList = taskListMapper.selectById(req.getTaskListId());
+ if (taskList == null) {
+ throw new RuntimeException("清单不存在");
+ }
+
+ LoginUser targetUser = sysBaseAPI.getUserById(req.getUserId());
+ if (targetUser == null) {
+ throw new RuntimeException("用户不存在");
+ }
+
+ Integer targetUserSecLevel = targetUser.getUserSecurityLevel();
+ Integer listSecLevel = taskList.getSecretLevel();
+ if (listSecLevel == null) {
+ listSecLevel = 1;
+ }
+ if (targetUserSecLevel == null) {
+ targetUserSecLevel = 3;
+ }
+
+ if (targetUserSecLevel <= listSecLevel) {
+ throw new RuntimeException("该用户密级不足,无法添加为协作人");
+ }
+
+ LambdaQueryWrapper existCheck = new LambdaQueryWrapper<>();
+ existCheck.eq(TaskListPermission::getMainId, req.getTaskListId());
+ existCheck.eq(TaskListPermission::getUserId, req.getUserId());
+ Long existCount = taskListPermissionMapper.selectCount(existCheck);
+ if (existCount > 0) {
+ throw new RuntimeException("该用户已是协作人");
+ }
+
+ TaskListPermission permission = new TaskListPermission();
+ permission.setMainId(req.getTaskListId());
+ permission.setUserId(req.getUserId());
+ permission.setPermission(req.getPermission());
+ taskListPermissionMapper.insert(permission);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void removeCollaborator(String permissionId) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String currentUserId = loginUser.getId();
+
+ TaskListPermission perm = taskListPermissionMapper.selectById(permissionId);
+ if (perm == null) {
+ throw new RuntimeException("权限记录不存在");
+ }
+ if ("1".equals(perm.getPermission())) {
+ throw new RuntimeException("不能移除所有者");
+ }
+
+ LambdaQueryWrapper ownerCheck = new LambdaQueryWrapper<>();
+ ownerCheck.eq(TaskListPermission::getMainId, perm.getMainId());
+ ownerCheck.eq(TaskListPermission::getUserId, currentUserId);
+ ownerCheck.eq(TaskListPermission::getPermission, "1");
+ Long ownerCount = taskListPermissionMapper.selectCount(ownerCheck);
+ if (ownerCount == 0) {
+ throw new RuntimeException("仅所有者可移除协作人");
+ }
+
+ taskListPermissionMapper.deleteById(permissionId);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateCollaboratorPermission(String permissionId, String newPermission) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String currentUserId = loginUser.getId();
+
+ TaskListPermission perm = taskListPermissionMapper.selectById(permissionId);
+ if (perm == null) {
+ throw new RuntimeException("权限记录不存在");
+ }
+ if ("1".equals(perm.getPermission())) {
+ throw new RuntimeException("不能修改所有者权限");
+ }
+ if (!"2".equals(newPermission) && !"3".equals(newPermission)) {
+ throw new RuntimeException("权限类型无效");
+ }
+
+ LambdaQueryWrapper ownerCheck = new LambdaQueryWrapper<>();
+ ownerCheck.eq(TaskListPermission::getMainId, perm.getMainId());
+ ownerCheck.eq(TaskListPermission::getUserId, currentUserId);
+ ownerCheck.eq(TaskListPermission::getPermission, "1");
+ Long ownerCount = taskListPermissionMapper.selectCount(ownerCheck);
+ if (ownerCount == 0) {
+ throw new RuntimeException("仅所有者可修改协作人权限");
+ }
+
+ TaskListPermission update = new TaskListPermission();
+ update.setId(permissionId);
+ update.setPermission(newPermission);
+ taskListPermissionMapper.updateById(update);
+ }
+
+ @Override
+ public String getMyPermission(String taskListId) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+
+ LambdaQueryWrapper query = new LambdaQueryWrapper<>();
+ query.eq(TaskListPermission::getMainId, taskListId);
+ query.eq(TaskListPermission::getUserId, userId);
+ TaskListPermission perm = taskListPermissionMapper.selectOne(query);
+ return perm != null ? perm.getPermission() : null;
+ }
+
+ @Override
+ public void addToFavorites(String taskListId, String pid) {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ String userId = loginUser.getId();
+ Integer userSecLevel = loginUser.getUserSecurityLevel();
+ if (userSecLevel == null) {
+ userSecLevel = 3;
+ }
+
+ TaskList taskList = taskListMapper.selectById(taskListId);
+ if (taskList == null) {
+ throw new RuntimeException("清单不存在");
+ }
+
+ Integer listSecLevel = taskList.getSecretLevel();
+ if (listSecLevel == null) {
+ listSecLevel = 1;
+ }
+ if (userSecLevel <= listSecLevel) {
+ throw new RuntimeException("您无权收藏该密级的清单");
+ }
+
+ LambdaQueryWrapper existCheck = new LambdaQueryWrapper<>();
+ existCheck.eq(TaskListFavorite::getMainId, taskListId);
+ existCheck.eq(TaskListFavorite::getUserId, userId);
+ existCheck.eq(TaskListFavorite::getType, "1");
+ existCheck.eq(TaskListFavorite::getDelFlag, "0");
+ TaskListFavorite existFav = taskListFavoriteMapper.selectOne(existCheck);
+ if (existFav != null) {
+ throw new RuntimeException("该清单已在收藏中");
+ }
+
+ LambdaQueryWrapper reactivateCheck = new LambdaQueryWrapper<>();
+ reactivateCheck.eq(TaskListFavorite::getMainId, taskListId);
+ reactivateCheck.eq(TaskListFavorite::getUserId, userId);
+ reactivateCheck.eq(TaskListFavorite::getType, "1");
+ reactivateCheck.eq(TaskListFavorite::getDelFlag, "1");
+ TaskListFavorite softDeletedFav = taskListFavoriteMapper.selectOne(reactivateCheck);
+ if (softDeletedFav != null) {
+ UpdateWrapper uw = new UpdateWrapper<>();
+ uw.eq("id", softDeletedFav.getId());
+ uw.set("del_flag", "0");
+ taskListFavoriteMapper.update(null, uw);
+ if (softDeletedFav.getPid() != null) {
+ TaskListFavorite parentUpdate = new TaskListFavorite();
+ parentUpdate.setId(softDeletedFav.getPid());
+ parentUpdate.setHasChild("1");
+ taskListFavoriteMapper.updateById(parentUpdate);
+ }
+ return;
+ }
+
+ String normalizedPid = oConvertUtils.isNotEmpty(pid) ? pid : null;
+ if (normalizedPid != null) {
+ TaskListFavorite groupFav = taskListFavoriteMapper.selectById(normalizedPid);
+ if (groupFav == null || !"0".equals(groupFav.getType()) || !userId.equals(groupFav.getUserId())) {
+ throw new RuntimeException("目标分组不存在或无权限");
+ }
+ }
+
+ Integer maxSort = taskListFavoriteService.getMaxSortOrderByType(userId, normalizedPid, "1");
+ TaskListFavorite favorite = new TaskListFavorite();
+ favorite.setMainId(taskListId);
+ favorite.setUserId(userId);
+ favorite.setType("1");
+ favorite.setPid(normalizedPid);
+ favorite.setHasChild("0");
+ favorite.setSortOrder(maxSort + 1);
+ favorite.setTasklistName(taskList.getTasklistName());
+ taskListFavoriteMapper.insert(favorite);
+
+ if (normalizedPid != null) {
+ TaskListFavorite parentFav = new TaskListFavorite();
+ parentFav.setId(normalizedPid);
+ parentFav.setHasChild("1");
+ taskListFavoriteMapper.updateById(parentFav);
+ }
+ }
+
+ @Override
+ public List myResponsibleTasks() {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ return taskListDetialMapper.selectByAssigneeId(loginUser.getId(), loginUser.getId());
+ }
+
+ @Override
+ public List myFollowedTasks() {
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ return taskListDetialMapper.selectByFollowersId(loginUser.getId(), loginUser.getId());
+ }
+
+ private String getSecretText(Integer level) {
+ if (level == null) return "非密";
+ switch (level) {
+ case 1: return "非密";
+ case 2: return "内部";
+ case 3: return "秘密";
+ case 4: return "机密";
+ default: return "非密";
+ }
+ }
+
+ private String filterUsersBySecLevel(String userIds, Integer listSecLevel) {
+ if (oConvertUtils.isEmpty(userIds)) return userIds;
+ if (listSecLevel == null) {
+ listSecLevel = 1;
+ }
+ String[] ids = userIds.split(",");
+ java.util.List validIds = new java.util.ArrayList<>();
+ for (String id : ids) {
+ LoginUser user = sysBaseAPI.getUserById(id.trim());
+ if (user == null) {
+ continue;
+ }
+ Integer userSecLevel = user.getUserSecurityLevel();
+ if (userSecLevel == null) {
+ userSecLevel = 3;
+ }
+ if (userSecLevel > listSecLevel) {
+ validIds.add(id.trim());
+ }
+ }
+ return String.join(",", validIds);
+ }
+
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/AddCollaboratorReq.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/AddCollaboratorReq.java
new file mode 100644
index 0000000..abf7262
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/AddCollaboratorReq.java
@@ -0,0 +1,18 @@
+package org.jeecg.modules.demo.tasklist.vo;
+
+import lombok.Data;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Data
+@Schema(description = "添加协作人请求")
+public class AddCollaboratorReq {
+
+ @Schema(description = "清单ID")
+ private String taskListId;
+
+ @Schema(description = "被添加的用户ID")
+ private String userId;
+
+ @Schema(description = "权限类型: 2=可编辑 3=可阅读")
+ private String permission;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CollaboratorVO.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CollaboratorVO.java
new file mode 100644
index 0000000..58f5d83
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CollaboratorVO.java
@@ -0,0 +1,21 @@
+package org.jeecg.modules.demo.tasklist.vo;
+
+import lombok.Data;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Data
+@Schema(description = "协作人信息")
+public class CollaboratorVO {
+
+ @Schema(description = "权限记录ID")
+ private String permissionId;
+
+ @Schema(description = "用户ID")
+ private String userId;
+
+ @Schema(description = "用户姓名")
+ private String username;
+
+ @Schema(description = "权限类型: 1=所有者 2=可编辑 3=可阅读")
+ private String permission;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CreateTaskListGroupReq.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CreateTaskListGroupReq.java
new file mode 100644
index 0000000..cd0f242
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CreateTaskListGroupReq.java
@@ -0,0 +1,12 @@
+package org.jeecg.modules.demo.tasklist.vo;
+
+import lombok.Data;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Data
+@Schema(description = "创建任务清单分组请求")
+public class CreateTaskListGroupReq {
+
+ @Schema(description = "分组名称")
+ private String tasklistName;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CreateTaskListReq.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CreateTaskListReq.java
new file mode 100644
index 0000000..d59e601
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CreateTaskListReq.java
@@ -0,0 +1,21 @@
+package org.jeecg.modules.demo.tasklist.vo;
+
+import lombok.Data;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Data
+@Schema(description = "创建任务清单请求")
+public class CreateTaskListReq {
+
+ @Schema(description = "清单名称")
+ private String tasklistName;
+
+ @Schema(description = "父分组ID(可选,不传则放在根级别)")
+ private String pid;
+
+ @Schema(description = "目标排序位置(可选,不传则追加到末尾)")
+ private Integer sortOrder;
+
+ @Schema(description = "密级:1=非密,2=内部,3=秘密,4=机密")
+ private Integer secretLevel;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CreateTaskReq.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CreateTaskReq.java
new file mode 100644
index 0000000..c735048
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/CreateTaskReq.java
@@ -0,0 +1,45 @@
+package org.jeecg.modules.demo.tasklist.vo;
+
+import lombok.Data;
+import io.swagger.v3.oas.annotations.media.Schema;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.springframework.format.annotation.DateTimeFormat;
+
+@Data
+@Schema(description = "创建任务请求")
+public class CreateTaskReq {
+
+ @Schema(description = "所属清单ID")
+ private String mainId;
+
+ @Schema(description = "任务名称")
+ private String taskName;
+
+ @Schema(description = "任务描述")
+ private String taskDesc;
+
+ @Schema(description = "优先级")
+ private String priority;
+
+ @Schema(description = "类型:0=任务分组,1=普通任务")
+ private String type;
+
+ @Schema(description = "父节点ID(分组ID或父任务ID,为空则归入默认分组)")
+ private String pid;
+
+ @Schema(description = "负责人ID")
+ private String assigneeId;
+
+ @Schema(description = "开始时间")
+ @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private java.util.Date startTime;
+
+ @Schema(description = "结束时间")
+ @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ private java.util.Date endTime;
+
+ @Schema(description = "排序号(指定则在指定位置插入)")
+ private Integer sortOrder;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/MoveTaskListReq.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/MoveTaskListReq.java
new file mode 100644
index 0000000..2316e76
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/MoveTaskListReq.java
@@ -0,0 +1,18 @@
+package org.jeecg.modules.demo.tasklist.vo;
+
+import lombok.Data;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Data
+@Schema(description = "拖拽移动任务清单请求")
+public class MoveTaskListReq {
+
+ @Schema(description = "被移动的清单对应的 favorite 记录ID")
+ private String favoriteId;
+
+ @Schema(description = "目标分组ID(null 或空字符串表示移动到根级别)")
+ private String targetGroupId;
+
+ @Schema(description = "目标位置排序号(可选,不传则追加到末尾)")
+ private Integer sortOrder;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/MoveTaskReq.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/MoveTaskReq.java
new file mode 100644
index 0000000..a842689
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/MoveTaskReq.java
@@ -0,0 +1,18 @@
+package org.jeecg.modules.demo.tasklist.vo;
+
+import lombok.Data;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Data
+@Schema(description = "拖拽移动任务请求")
+public class MoveTaskReq {
+
+ @Schema(description = "任务ID")
+ private String taskId;
+
+ @Schema(description = "目标父节点ID(分组ID或父任务ID,为空表示移入默认分组)")
+ private String targetPid;
+
+ @Schema(description = "目标位置排序号(可选,不传则追加到末尾)")
+ private Integer targetSortOrder;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/SubTaskPageReq.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/SubTaskPageReq.java
new file mode 100644
index 0000000..4a32552
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/SubTaskPageReq.java
@@ -0,0 +1,18 @@
+package org.jeecg.modules.demo.tasklist.vo;
+
+import lombok.Data;
+import io.swagger.v3.oas.annotations.media.Schema;
+
+@Data
+@Schema(description = "子任务分页请求")
+public class SubTaskPageReq {
+
+ @Schema(description = "父任务ID")
+ private String parentTaskId;
+
+ @Schema(description = "页码,默认1")
+ private Integer pageNo = 1;
+
+ @Schema(description = "每页条数,默认20")
+ private Integer pageSize = 20;
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/TaskListPage.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/TaskListPage.java
new file mode 100644
index 0000000..16f911a
--- /dev/null
+++ b/jeecg-module-supervision/src/main/java/org/jeecg/modules/tasklist/vo/TaskListPage.java
@@ -0,0 +1,82 @@
+package org.jeecg.modules.demo.tasklist.vo;
+
+import java.util.List;
+import org.jeecg.modules.demo.tasklist.entity.TaskList;
+import org.jeecg.modules.demo.tasklist.entity.TaskListDetial;
+import org.jeecg.modules.demo.tasklist.entity.TaskListPermission;
+import org.jeecg.modules.demo.tasklist.entity.TaskListFavorite;
+import lombok.Data;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.jeecgframework.poi.excel.annotation.ExcelEntity;
+import org.jeecgframework.poi.excel.annotation.ExcelCollection;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.util.Date;
+import org.jeecg.common.aspect.annotation.Dict;
+import org.jeecg.common.constant.ProvinceCityArea;
+import org.jeecg.common.util.SpringContextUtils;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+/**
+ * @Description: 任务清单表
+ * @Author: jeecg-boot
+ * @Date: 2026-04-24
+ * @Version: V1.0
+ */
+@Data
+@Schema(description="任务清单表")
+public class TaskListPage {
+
+ /**主键*/
+ @Schema(description = "主键")
+ private java.lang.String id;
+ /**创建人*/
+ @Schema(description = "创建人")
+ private java.lang.String createBy;
+ /**创建日期*/
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "创建日期")
+ private java.util.Date createTime;
+ /**更新人*/
+ @Schema(description = "更新人")
+ private java.lang.String updateBy;
+ /**更新日期*/
+ @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
+ @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+ @Schema(description = "更新日期")
+ private java.util.Date updateTime;
+ /**所属部门*/
+ @Schema(description = "所属部门")
+ private java.lang.String sysOrgCode;
+ /**清单名称*/
+ @Excel(name = "清单名称", width = 15)
+ @Schema(description = "清单名称")
+ private java.lang.String tasklistName;
+ /**删除标识*/
+ @Excel(name = "删除标识", width = 15)
+ @Schema(description = "删除标识")
+ private java.lang.String delFlag;
+
+ @ExcelCollection(name="任务清单详情表")
+ @Schema(description = "任务清单详情表")
+ private List taskListDetialList;
+ @ExcelCollection(name="任务清单权限表")
+ @Schema(description = "任务清单权限表")
+ private List taskListPermissionList;
+ /**密级: 1=非密, 2=内部, 3=秘密, 4=机密*/
+ @Excel(name = "密级", width = 15)
+ @Schema(description = "密级:1=非密,2=内部,3=秘密,4=机密")
+ private java.lang.Integer secretLevel;
+
+ /**密级文本*/
+ @Excel(name = "密级文本", width = 15)
+ @Schema(description = "密级文本")
+ private java.lang.String secretText;
+
+ @ExcelCollection(name="任务清单收藏表")
+ @Schema(description = "任务清单收藏表")
+ private List taskListFavoriteList;
+
+}
diff --git a/jeecg-module-supervision/src/main/java/org/jeecg/modules/test/controller/TestMainTableController.java b/jeecg-module-supervision/src/main/java/org/jeecg/modules/test/controller/TestMainTableController.java
deleted file mode 100644
index 5cf41b6..0000000
--- a/jeecg-module-supervision/src/main/java/org/jeecg/modules/test/controller/TestMainTableController.java
+++ /dev/null
@@ -1,270 +0,0 @@
-package org.jeecg.modules.test.controller;
-
-import java.io.UnsupportedEncodingException;
-import java.io.IOException;
-import java.net.URLDecoder;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import java.util.HashMap;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.jeecgframework.poi.excel.ExcelImportUtil;
-import org.jeecgframework.poi.excel.def.NormalExcelConstants;
-import org.jeecgframework.poi.excel.entity.ExportParams;
-import org.jeecgframework.poi.excel.entity.ImportParams;
-import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
-import org.jeecg.common.system.vo.LoginUser;
-import org.apache.shiro.SecurityUtils;
-import org.jeecg.common.api.vo.Result;
-import org.jeecg.common.system.query.QueryGenerator;
-import org.jeecg.common.system.query.QueryRuleEnum;
-import org.jeecg.common.util.oConvertUtils;
-import org.jeecg.modules.test.entity.TestSonTable;
-import org.jeecg.modules.test.entity.TestMainTable;
-import org.jeecg.modules.test.vo.TestMainTablePage;
-import org.jeecg.modules.test.service.ITestMainTableService;
-import org.jeecg.modules.test.service.ITestSonTableService;
-import org.springframework.beans.BeanUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.multipart.MultipartHttpServletRequest;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import lombok.extern.slf4j.Slf4j;
-import com.alibaba.fastjson.JSON;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import io.swagger.v3.oas.annotations.Operation;
-import org.jeecg.common.aspect.annotation.AutoLog;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
-
-
- /**
- * @Description: test
- * @Author: jeecg-boot
- * @Date: 2026-04-03
- * @Version: V1.0
- */
-@Tag(name="test")
-@RestController
-@RequestMapping("/test/testMainTable")
-@Slf4j
-public class TestMainTableController {
- @Autowired
- private ITestMainTableService testMainTableService;
- @Autowired
- private ITestSonTableService testSonTableService;
-
- /**
- * 分页列表查询
- *
- * @param testMainTable
- * @param pageNo
- * @param pageSize
- * @param req
- * @return
- */
- //@AutoLog(value = "test-分页列表查询")
- @Operation(summary="test-分页列表查询")
- @GetMapping(value = "/list")
- public Result> queryPageList(TestMainTable testMainTable,
- @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
- @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
- HttpServletRequest req) {
- QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(testMainTable, req.getParameterMap());
- Page page = new Page(pageNo, pageSize);
- IPage pageList = testMainTableService.page(page, queryWrapper);
- return Result.OK(pageList);
- }
-
- /**
- * 添加
- *
- * @param testMainTablePage
- * @return
- */
- @AutoLog(value = "test-添加")
- @Operation(summary="test-添加")
- @RequiresPermissions("test:test_main_table:add")
- @PostMapping(value = "/add")
- public Result add(@RequestBody TestMainTablePage testMainTablePage) {
- TestMainTable testMainTable = new TestMainTable();
- BeanUtils.copyProperties(testMainTablePage, testMainTable);
- testMainTableService.saveMain(testMainTable, testMainTablePage.getTestSonTableList());
- return Result.OK("添加成功!");
- }
-
- /**
- * 编辑
- *
- * @param testMainTablePage
- * @return
- */
- @AutoLog(value = "test-编辑")
- @Operation(summary="test-编辑")
- @RequiresPermissions("test:test_main_table:edit")
- @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
- public Result edit(@RequestBody TestMainTablePage testMainTablePage) {
- TestMainTable testMainTable = new TestMainTable();
- BeanUtils.copyProperties(testMainTablePage, testMainTable);
- TestMainTable testMainTableEntity = testMainTableService.getById(testMainTable.getId());
- if(testMainTableEntity==null) {
- return Result.error("未找到对应数据");
- }
- testMainTableService.updateMain(testMainTable, testMainTablePage.getTestSonTableList());
- return Result.OK("编辑成功!");
- }
-
- /**
- * 通过id删除
- *
- * @param id
- * @return
- */
- @AutoLog(value = "test-通过id删除")
- @Operation(summary="test-通过id删除")
- @RequiresPermissions("test:test_main_table:delete")
- @DeleteMapping(value = "/delete")
- public Result delete(@RequestParam(name="id",required=true) String id) {
- testMainTableService.delMain(id);
- return Result.OK("删除成功!");
- }
-
- /**
- * 批量删除
- *
- * @param ids
- * @return
- */
- @AutoLog(value = "test-批量删除")
- @Operation(summary="test-批量删除")
- @RequiresPermissions("test:test_main_table:deleteBatch")
- @DeleteMapping(value = "/deleteBatch")
- public Result deleteBatch(@RequestParam(name="ids",required=true) String ids) {
- this.testMainTableService.delBatchMain(Arrays.asList(ids.split(",")));
- return Result.OK("批量删除成功!");
- }
-
- /**
- * 通过id查询
- *
- * @param id
- * @return
- */
- //@AutoLog(value = "test-通过id查询")
- @Operation(summary="test-通过id查询")
- @GetMapping(value = "/queryById")
- public Result queryById(@RequestParam(name="id",required=true) String id) {
- TestMainTable testMainTable = testMainTableService.getById(id);
- if(testMainTable==null) {
- return Result.error("未找到对应数据");
- }
- return Result.OK(testMainTable);
-
- }
-
- /**
- * 通过id查询
- *
- * @param id
- * @return
- */
- //@AutoLog(value = "test通过主表ID查询")
- @Operation(summary="test主表ID查询")
- @GetMapping(value = "/queryTestSonTableByMainId")
- public Result> queryTestSonTableListByMainId(@RequestParam(name="id",required=true) String id) {
- List testSonTableList = testSonTableService.selectByMainId(id);
- return Result.OK(testSonTableList);
- }
-
- /**
- * 导出excel
- *
- * @param request
- * @param testMainTable
- */
- @RequiresPermissions("test:test_main_table:exportXls")
- @RequestMapping(value = "/exportXls")
- public ModelAndView exportXls(HttpServletRequest request, TestMainTable testMainTable) {
-
- // Step.1 组装查询条件查询数据
- QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(testMainTable, request.getParameterMap());
- LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
-
- //配置选中数据查询条件
- String selections = request.getParameter("selections");
- if(oConvertUtils.isNotEmpty(selections)) {
- List selectionList = Arrays.asList(selections.split(","));
- queryWrapper.in("id",selectionList);
- }
- //Step.2 获取导出数据
- List testMainTableList = testMainTableService.list(queryWrapper);
-
- // Step.3 组装pageList
- List