From 4afb3bd83560cefecf9241518759ad238cbf4bf1 Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Thu, 2 Jul 2026 16:06:57 +0800 Subject: [PATCH 1/3] commit --- .../app/flow/service/ProcessTaskService.java | 9 +++ .../service/impl/ProcessTaskServiceImpl.java | 25 ++++++ .../config/ProposalConfigUnitController.java | 81 +++++++++++++++++++ .../ProposalUnderTakeReplyController.java | 11 ++- .../proposal/config/unit/index.html | 21 ++++- 5 files changed, 143 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/budwk/app/flow/service/ProcessTaskService.java b/src/main/java/com/budwk/app/flow/service/ProcessTaskService.java index 3f51ce85..22a25bf0 100644 --- a/src/main/java/com/budwk/app/flow/service/ProcessTaskService.java +++ b/src/main/java/com/budwk/app/flow/service/ProcessTaskService.java @@ -134,6 +134,15 @@ public interface ProcessTaskService extends BaseService { */ void removeTaskActor(Long processTaskId, List actors); + /** + * 按最新办理人配置覆盖同步指定流程任务的参与者。 + * + * @param processTaskId 流程任务id + * @param actors 最新办理人用户id集合;为空时不处理,避免误清空任务办理人 + * @return 新增和移除的参与者数量合计 + */ + int syncTaskActors(Long processTaskId, List actors); + /** * 根据taskId、operator,判断操作人operator是否允许执行任务 * diff --git a/src/main/java/com/budwk/app/flow/service/impl/ProcessTaskServiceImpl.java b/src/main/java/com/budwk/app/flow/service/impl/ProcessTaskServiceImpl.java index 0398eb5b..bb542295 100644 --- a/src/main/java/com/budwk/app/flow/service/impl/ProcessTaskServiceImpl.java +++ b/src/main/java/com/budwk/app/flow/service/impl/ProcessTaskServiceImpl.java @@ -279,6 +279,31 @@ public class ProcessTaskServiceImpl extends BaseServiceImpl impleme dao().clear(ProcessTaskActor.class, Cnd.where(ProcessTaskActor::getProcessTaskId, "=", processTaskId).and(ProcessTaskActor::getActorId, "in", actors)); } + @Override + @Aop(TransAop.READ_COMMITTED) + public int syncTaskActors(Long processTaskId, List actors) { + if (CollectionUtil.isEmpty(actors)) { + return 0; + } + List latestActors = actors.stream().filter(StrUtil::isNotBlank).distinct().toList(); + if (CollectionUtil.isEmpty(latestActors)) { + return 0; + } + + // 以最新负责人配置为准:缺失的办理人补上,不再是负责人的办理人移除。 + List dbActors = getTaskActors(processTaskId); + List addActors = latestActors.stream().filter(actor -> !dbActors.contains(actor)).toList(); + List removeActors = dbActors.stream().filter(actor -> !latestActors.contains(actor)).toList(); + + if (CollectionUtil.isNotEmpty(removeActors)) { + removeTaskActor(processTaskId, removeActors); + } + if (CollectionUtil.isNotEmpty(addActors)) { + addTaskActor(processTaskId, addActors); + } + return addActors.size() + removeActors.size(); + } + @Override public boolean isAllowed(ProcessTask task, String operator) { // 执行者为超级管理员或自动执行用户 diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/config/ProposalConfigUnitController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/config/ProposalConfigUnitController.java index 905907ff..a8cf49e1 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/config/ProposalConfigUnitController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/config/ProposalConfigUnitController.java @@ -13,6 +13,9 @@ import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; import com.budwk.app.base.service.BaseService; +import com.budwk.app.flow.entity.ProcessTask; +import com.budwk.app.flow.enums.ProcessTaskStateEnum; +import com.budwk.app.flow.service.ProcessTaskService; import com.budwk.app.sys.models.Sys_role; import com.budwk.app.sys.models.Sys_unit; import com.budwk.app.sys.models.Sys_user_role; @@ -57,6 +60,8 @@ public class ProposalConfigUnitController { private BaseService baseService; @Inject private SysRoleService sysRoleService; + @Inject + private ProcessTaskService processTaskService; @At("") @Ok("beetl:/platform/zhgh/democratic/proposal/config/unit/index.html") @@ -146,6 +151,82 @@ public class ProposalConfigUnitController { return Result.success(); } + /** + * 同步当前进行中的承办单位答复任务办理人。 + * + * 按承办单位当前配置的单位负责人覆盖同步 wf_process_task_actor: + * 1. 参数:无参数,系统自动扫描所有启用承办单位。 + * 2. 单位负责人来源:PROPOSAL_UNIT_LEADER 角色且 underTakeId 等于承办单位id。 + * 3. 同步范围:当前进行中的主办答复、协办答复、意见主办答复任务。 + * 4. 返回值:Result,msg 中说明同步任务数、办理人变更数、未配置负责人单位数。 + */ + @At + @SaCheckPermission("proposal.config.unit") + @SLog(tag = "提案", msg = "同步承办单位答复人") + @Aop(TransAop.READ_COMMITTED) + public Result syncUnitReplyActors() { + Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_LEADER); + List undertakes = dao.query(ProposalUndertake.class, Cnd.where(ProposalUndertake::getEnable, "=", true)); + int scanTaskCount = 0; + int syncTaskCount = 0; + int syncActorCount = 0; + int skipUnitCount = 0; + + for (ProposalUndertake undertake : undertakes) { + List sysUserRoles = dao.query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()) + .and(Sys_user_role::getUnderTakeId, "=", undertake.getId())); + List userIds = sysUserRoles.stream().map(Sys_user_role::getUserId).distinct().toList(); + if (CollectionUtil.isEmpty(userIds)) { + skipUnitCount++; + continue; + } + + List tasks = queryDoingUnitReplyTasks(undertake.getId()); + scanTaskCount += tasks.size(); + for (ProcessTask task : tasks) { + int syncCount = processTaskService.syncTaskActors(task.getId(), userIds); + if (syncCount > 0) { + syncTaskCount++; + syncActorCount += syncCount; + } + } + } + + return Result.success("同步完成,扫描任务" + scanTaskCount + "个,更新任务" + syncTaskCount + "个,办理人变更" + syncActorCount + "人次,跳过未配置负责人单位" + skipUnitCount + "个。"); + } + + /** + * 根据承办单位和提案承办单位记录查询正在办理中的答复任务。 + * + * 说明: + * 1. 参数 underTakeId 为 proposal_undertake.id,即当前承办单位配置ID。 + * 2. 不依赖 wf_process_task.variable 中的 underTakeId,避免历史任务变量和承办单位配置不一致导致漏同步。 + * 3. 主办单位只匹配 master_reply、opinion_master_reply,协办单位只匹配 slave_reply。 + * 4. 返回值为当前仍在办理中的流程任务列表。 + */ + private List queryDoingUnitReplyTasks(String underTakeId) { + Sql sql = Sqls.queryEntity(""" + SELECT DISTINCT + t.* + FROM + wf_process_task t + INNER JOIN wf_process_instance ins ON ins.id = t.processInstanceId + INNER JOIN proposal_reply_unit pru ON pru.proposalId = ins.businessNo + AND pru.unitId = @underTakeId + WHERE + t.taskState = @taskState + AND ( + (t.taskName IN ('master_reply', 'opinion_master_reply') AND pru.isMaster = 1) + OR (t.taskName = 'slave_reply' AND pru.isMaster = 0) + ) + """); + sql.setParam("underTakeId", underTakeId); + sql.setParam("taskState", ProcessTaskStateEnum.DOING.getCode()); + sql.setEntity(dao.getEntity(ProcessTask.class)); + dao.execute(sql); + return sql.getList(ProcessTask.class); + } + /** * 设置分管校领导 * diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java index 79274ce4..63a02c1f 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java @@ -101,8 +101,8 @@ public class ProposalUnderTakeReplyController { IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName, IFNULL(GROUP_CONCAT(DISTINCT nt.taskName),'结束') curTaskKey, CASE WHEN t.taskState = 20 AND ( rt.id IS NULL OR rt.taskState = 10 ) THEN 1 ELSE 0 END AS canRevoke, - t.variable->>'$.underTakeName' AS underTakeName, - IF(JSON_EXTRACT(t.variable, '$.underTakeIsMaster') = true, 1, 0) AS underTakeIsMaster, + IFNULL(pru.unitName, t.variable->>'$.underTakeName') AS underTakeName, + IFNULL(pru.isMaster, IF(JSON_EXTRACT(t.variable, '$.underTakeIsMaster') = true, 1, 0)) AS underTakeIsMaster, t.variable->>'$.tf_transferUserId' AS tf_transferUserId, transferUser.username AS transferUserName FROM @@ -114,6 +114,11 @@ public class ProposalUnderTakeReplyController { LEFT JOIN proposal_info info ON info.id = ins.businessNo LEFT JOIN proposal_type type on type.id = info.typeId LEFT JOIN proposal_merge mer ON mer.proposalId = info.id + LEFT JOIN proposal_reply_unit pru ON pru.proposalId = ins.businessNo + AND ( + (t.taskName IN ('master_reply', 'opinion_master_reply') AND pru.isMaster = 1) + OR (t.taskName = 'slave_reply' AND pru.isMaster = 0) + ) LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId LEFT JOIN sys_user transferUser on transferUser.id = t.variable->>'$.tf_transferUserId' @@ -132,7 +137,7 @@ public class ProposalUnderTakeReplyController { cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); } ProposalSearchParam.buildSearch(cnd, pageForm); - cnd.groupBy("t.id"); + cnd.groupBy("t.id", "pru.unitId", "pru.unitName", "pru.isMaster"); cnd.desc("t.createdAt"); sql.setCondition(cnd); Pagination pagination = proposalCommonService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/config/unit/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/config/unit/index.html index 121ff1a0..41980463 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/config/unit/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/config/unit/index.html @@ -22,6 +22,7 @@ layout("/layouts/platform.html"){ 同步系统单位 + 同步承办单位答复人 @@ -69,7 +70,8 @@ layout("/layouts/platform.html"){ data() { return { currentTreeNode: null, - currentTreeData: null + currentTreeData: null, + syncUnitReplyActorsLoading: false } }, methods: { @@ -85,6 +87,23 @@ layout("/layouts/platform.html"){ this.$refs.treeRef.listTree() } }) + }, + + syncUnitReplyActors() { + this.$confirm("确定要按当前单位领导配置同步进行中的承办单位答复待办吗?", "提示", { + type: "warning" + }).then(() => { + this.syncUnitReplyActorsLoading = true + this.$axios.post(loc() + "/syncUnitReplyActors") + .then((res) => { + if (res.code === 0) { + this.$message.success(res.msg) + } + }) + .finally(() => { + this.syncUnitReplyActorsLoading = false + }) + }) } }, created() { From a79280d16d6c6a2fab4ca842a1efea272bceda84 Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Thu, 2 Jul 2026 16:52:08 +0800 Subject: [PATCH 2/3] commit --- .../config/ProposalConfigUnitController.java | 5 +- .../ProposalQueryUnitReplyController.java | 45 ++++++++++ .../ProposalUnderTakeReplyController.java | 53 +++++++++-- .../proposal/query/underTakeReply/index.html | 88 ++++++++++++++++++- .../proposal/transact/unitReply/index.html | 16 +++- 5 files changed, 193 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/config/ProposalConfigUnitController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/config/ProposalConfigUnitController.java index a8cf49e1..22d32afe 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/config/ProposalConfigUnitController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/config/ProposalConfigUnitController.java @@ -85,8 +85,8 @@ public class ProposalConfigUnitController { Sql sql = Sqls.create(""" SELECT t1.*, - u1.username AS branchSchoolLeader, - u2.username AS unitLeader + GROUP_CONCAT(DISTINCT u1.username ORDER BY u1.username SEPARATOR '、') AS branchSchoolLeader, + GROUP_CONCAT(DISTINCT u2.username ORDER BY u2.username SEPARATOR '、') AS unitLeader FROM proposal_undertake t1 LEFT JOIN sys_user_role sur1 ON sur1.underTakeId = t1.id @@ -108,6 +108,7 @@ public class ProposalConfigUnitController { cnd.where().orLike("t1.name", pageForm.getSearchKeyword()); } cnd.and("t1.enable","=", true); + cnd.groupBy("t1.id"); cnd.asc("t1.code"); sql.setCondition(cnd); Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryUnitReplyController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryUnitReplyController.java index c4d7cc88..b6f716ca 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryUnitReplyController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryUnitReplyController.java @@ -1,13 +1,18 @@ package com.budwk.app.zhgh.democratic.proposal.controller.query; +import cn.hutool.core.bean.BeanUtil; import cn.dev33.satoken.annotation.SaCheckPermission; +import com.budwk.app.base.page.Pagination; import com.budwk.app.base.result.Result; import com.budwk.app.flow.engine.util.FlowUtil; import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.enums.ProcessTaskStateEnum; +import com.budwk.app.zhgh.democratic.proposal.controller.vo.ProposalUnitReplyProposalVO; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig; import com.budwk.app.zhgh.democratic.proposal.models.ProposalReplyUnit; +import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; +import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; @@ -19,6 +24,7 @@ import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; +import javax.validation.Valid; import java.util.*; import java.util.stream.Collectors; @@ -131,4 +137,43 @@ public class ProposalQueryUnitReplyController { return Result.success().addData(Map.of("tableData", tableData, "slaveNeedReply", slaveNeedReply)); } + + @At + @SaCheckPermission("proposal.query.unitReply") + @ApiOperation("承办单位提案明细") + public Result getProposalsByUnit(@Valid ProposalSearchParam pageForm, String unitCode) { + Sql sql = Sqls.create(""" + SELECT + info.id, + info.code, + info.name, + info.createUserName, + type.name AS typeName, + tcs.fullName AS sessionName, + tcd.`name` AS delegationName, + pru.unitName AS underTakeUnitName, + IF(pru.isMaster = 1, '主办', '协办') AS underTakeType, + IFNULL(GROUP_CONCAT(DISTINCT t.displayName), '结束') AS curTaskName + FROM + proposal_reply_unit pru + INNER JOIN proposal_info info ON info.id = pru.proposalId + LEFT JOIN proposal_type type on type.id = info.typeId + LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId + LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId + LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id + LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10 + $condition + """); + Cnd cnd = Cnd.NEW(); + // 明细弹窗按当前届次和点击的承办单位编码查询,返回该单位主办、协办的提案列表。 + cnd.andEX("pru.unitCode", "=", unitCode); + ProposalSearchParam.buildSearch(cnd, pageForm); + cnd.groupBy("info.id", "pru.unitName", "pru.isMaster"); + sql.setCondition(cnd); + + Pagination pagination = proposalCommonService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + List list = BeanUtil.copyToList(pagination.getList(), ProposalUnitReplyProposalVO.class); + pagination.setList(list); + return Result.success(pagination); + } } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java index 63a02c1f..9eadb5c6 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReplyController.java @@ -98,9 +98,28 @@ public class ProposalUnderTakeReplyController { t.finishTime, t.taskParentId, t.variable taskVariable, - IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName, - IFNULL(GROUP_CONCAT(DISTINCT nt.taskName),'结束') curTaskKey, - CASE WHEN t.taskState = 20 AND ( rt.id IS NULL OR rt.taskState = 10 ) THEN 1 ELSE 0 END AS canRevoke, + IFNULL(( + SELECT GROUP_CONCAT(DISTINCT nt.displayName) + FROM wf_process_task nt + WHERE nt.processInstanceId = ins.id AND nt.taskState = 10 + ), '结束') curTaskName, + IFNULL(( + SELECT GROUP_CONCAT(DISTINCT nt.taskName) + FROM wf_process_task nt + WHERE nt.processInstanceId = ins.id AND nt.taskState = 10 + ), '结束') curTaskKey, + CASE WHEN t.taskState = 20 AND ( + NOT EXISTS ( + SELECT 1 + FROM wf_process_task rt + WHERE rt.processInstanceId = ins.id AND rt.taskParentId = t.id + ) + OR EXISTS ( + SELECT 1 + FROM wf_process_task rt + WHERE rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10 + ) + ) THEN 1 ELSE 0 END AS canRevoke, IFNULL(pru.unitName, t.variable->>'$.underTakeName') AS underTakeName, IFNULL(pru.isMaster, IF(JSON_EXTRACT(t.variable, '$.underTakeIsMaster') = true, 1, 0)) AS underTakeIsMaster, t.variable->>'$.tf_transferUserId' AS tf_transferUserId, @@ -109,8 +128,6 @@ public class ProposalUnderTakeReplyController { wf_process_task t LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id - LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10 - LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id LEFT JOIN proposal_info info ON info.id = ins.businessNo LEFT JOIN proposal_type type on type.id = info.typeId LEFT JOIN proposal_merge mer ON mer.proposalId = info.id @@ -124,6 +141,16 @@ public class ProposalUnderTakeReplyController { LEFT JOIN sys_user transferUser on transferUser.id = t.variable->>'$.tf_transferUserId' $condition """); + // 分页统计只保留任务、办理人和提案查询所需关联,避免完整列表 SQL 的聚合字段参与 count。 + Sql countSql = Sqls.create(""" + SELECT COUNT(DISTINCT t.id) + FROM + wf_process_task t + LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId + LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id + LEFT JOIN proposal_info info ON info.id = ins.businessNo + $condition + """); Cnd cnd = Cnd.NEW(); cnd.and("t.taskName", "in", List.of("master_reply", "slave_reply", "opinion_master_reply")); @@ -140,11 +167,23 @@ public class ProposalUnderTakeReplyController { cnd.groupBy("t.id", "pru.unitId", "pru.unitName", "pru.isMaster"); cnd.desc("t.createdAt"); sql.setCondition(cnd); - Pagination pagination = proposalCommonService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + Cnd countCnd = Cnd.NEW(); + countCnd.and("t.taskName", "in", List.of("master_reply", "slave_reply", "opinion_master_reply")); + if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { + countCnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId())); + } + if (approval) { + countCnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.TRANSFER.getCode())); + } else { + countCnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode()); + } + ProposalSearchParam.buildSearch(countCnd, pageForm); + countSql.setCondition(countCnd); + Pagination pagination = proposalCommonService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql, countSql); List list = pagination.getList(); for (NutMap row : list) { - if (row.getInt("taskIsMaster") == 1) { + if (row.getInt("underTakeIsMaster") == 1) { boolean b = Stream.of("master_reply", "slave_reply", "school_leader") .anyMatch(a -> row.getString("curTaskKey").contains(a)); row.setv("canRevoke", b && row.getInt("taskState") == ProcessTaskStateEnum.FINISHED.getCode()); diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/query/underTakeReply/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/query/underTakeReply/index.html index 21b0b9dd..728260ed 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/query/underTakeReply/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/query/underTakeReply/index.html @@ -21,7 +21,11 @@ layout("/layouts/platform.html"){ - + + + @@ -35,6 +39,40 @@ layout("/layouts/platform.html"){ + + + + + + + + + + + + + + + + + + + + +
+ +
+