From 5ffd2bbd9628793af35778c38581000692203a7f Mon Sep 17 00:00:00 2001 From: zhangrui Date: Tue, 15 Sep 2026 14:33:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E6=A1=88=E6=89=BF=E5=8A=9E=E5=8D=95?= =?UTF-8?q?=E4=BD=8D=E8=BD=AC=E8=AE=A9=E4=B8=8D=E8=83=BD=E6=8C=89=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E4=BA=BA=E6=89=80=E5=9C=A8=E5=8D=95=E4=BD=8D=E4=BA=BA?= =?UTF-8?q?=E5=91=98=E6=9F=A5=E8=AF=A2=EF=BC=8C=E6=94=B9=E4=B8=BA=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E5=BD=93=E5=89=8D=20WF=20=E4=BB=BB=E5=8A=A1=E4=B8=AD?= =?UTF-8?q?=E8=B4=9F=E8=B4=A3=E4=BA=BA=E8=A7=92=E8=89=B2=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E7=9A=84=E6=89=BF=E5=8A=9E=E5=8D=95=E4=BD=8D=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProposalUnderTakeReplyController.java | 19 +-- .../service/common/ProposalCommonService.java | 9 + .../common/ProposalCommonServiceImpl.java | 54 ++++++ ...proposal_add_missing_unit_reply_actors.sql | 156 ++++++++++++++++++ .../proposal_add_unit_reply_actor_092108.sql | 126 ++++++++++++++ .../proposal/transact/unitReply/index.html | 5 +- 6 files changed, 352 insertions(+), 17 deletions(-) create mode 100644 src/main/resources/db/proposal_add_missing_unit_reply_actors.sql create mode 100644 src/main/resources/db/proposal_add_unit_reply_actor_092108.sql 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 546ff4b3..b406cab2 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 @@ -27,7 +27,6 @@ import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; -import org.nutz.dao.util.cri.SqlExpressionGroup; import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; @@ -230,21 +229,9 @@ public class ProposalUnderTakeReplyController { @At @SaCheckPermission("proposal.unitReply") @ApiOperation("查询本单位可转办用户") - public Result selectViceUser(@Param("keyword") @Valid String keyword) { - Sql sql = Sqls.create("select id,loginname,username from vw_user $condition"); - Cnd cnd = Cnd.NEW(); - cnd.andEX("unitId", "=", SecurityUtil.getUnitId()); - cnd.andEX("id", "!=", SecurityUtil.getUserId()); - SqlExpressionGroup seg = new SqlExpressionGroup(); - seg.orLike("username", keyword); - seg.orLike("loginname", keyword); - cnd.and(seg); - sql.setCondition(cnd); - - sql.setParam("unitId", SecurityUtil.getUnitId()); - sql.setParam("userId", SecurityUtil.getUserId()); - Pagination pagination = proposalCommonService.listPageMap(1, 10, sql); - return Result.success(pagination.getList()); + public Result selectViceUser(@Param("taskId") @Valid Long taskId, + @Param("keyword") @Valid String keyword) { + return Result.success(proposalCommonService.queryTransferUsers(taskId, keyword)); } @At diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonService.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonService.java index d2b959da..38c9b47c 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonService.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonService.java @@ -22,6 +22,15 @@ public interface ProposalCommonService extends BaseService { */ ProposalReplyUnit getReplyUnitByTaskActorRole(String proposalId, Long taskId, String taskName); + /** + * 查询当前 WF 承办答复任务可转交的本承办单位人员。 + * + * @param taskId WF 流程任务 ID,任务必须处于待办理状态;系统根据任务办理人的承办角色确定单位 + * @param keyword 姓名或工号搜索关键字,可为空 + * @return 人员列表,每项包含 id、loginname、username,最多返回 10 条 + */ + List queryTransferUsers(Long taskId, String keyword); + /** * 查询提案信息 */ diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java index 8763c728..024007ee 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonServiceImpl.java @@ -20,10 +20,12 @@ import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.flow.vo.ProcessTaskVO; import com.budwk.app.sys.models.Sys_dict; import com.budwk.app.sys.models.Sys_role; +import com.budwk.app.sys.models.Sys_unit; import com.budwk.app.sys.models.Sys_user; import com.budwk.app.sys.models.Sys_user_role; import com.budwk.app.sys.services.SysDictService; import com.budwk.app.sys.services.SysRoleService; +import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.base.Globals; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConsolidation; @@ -43,6 +45,7 @@ import org.nutz.dao.FieldFilter; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; import org.nutz.dao.util.Daos; +import org.nutz.dao.util.cri.SqlExpressionGroup; import org.nutz.dao.util.cri.Static; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; @@ -146,6 +149,57 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp return replyUnits.get(0); } + /** + * 根据当前 WF 任务的承办角色单位查询可转交人员,避免负责人系统所属单位与承办单位不一致时串单位。 + */ + @Override + public List queryTransferUsers(Long taskId, String keyword) { + if (taskId == null) { + throw new BaseException("流程任务不能为空"); + } + ProcessTask task = dao().fetch(ProcessTask.class, taskId); + if (task == null || !Objects.equals(task.getTaskState(), ProcessTaskStateEnum.DOING.getCode())) { + throw new BaseException("当前任务不存在或已办理"); + } + + // 非超级管理员只能查询本人待办任务对应的承办单位人员。 + if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { + int actorCount = dao().count(ProcessTaskActor.class, + Cnd.where(ProcessTaskActor::getProcessTaskId, "=", taskId) + .and(ProcessTaskActor::getActorId, "=", SecurityUtil.getUserId())); + if (actorCount == 0) { + throw new BaseException("您不是当前任务办理人"); + } + } + + ProcessInstance instance = dao().fetch(ProcessInstance.class, task.getProcessInstanceId()); + if (instance == null || StrUtil.isBlank(instance.getBusinessNo())) { + throw new BaseException("当前任务未关联提案"); + } + ProposalReplyUnit replyUnit = getReplyUnitByTaskActorRole( + instance.getBusinessNo(), taskId, task.getTaskName()); + + String unitCode = StrUtil.blankToDefault(replyUnit.getUnitCode(), replyUnit.getUnitId()); + Sys_unit systemUnit = dao().fetch(Sys_unit.class, + Cnd.where(Sys_unit::getUnitcode, "=", unitCode)); + if (systemUnit == null) { + throw new BaseException("承办单位未匹配到系统单位,无法查询转交人员"); + } + + Sql sql = Sqls.create("select id, loginname, username from vw_user $condition"); + Cnd cnd = Cnd.where("unitId", "=", systemUnit.getId()) + .and("id", "!=", SecurityUtil.getUserId()); + if (StrUtil.isNotBlank(keyword)) { + SqlExpressionGroup keywordGroup = new SqlExpressionGroup(); + keywordGroup.orLike("username", keyword); + keywordGroup.orLike("loginname", keyword); + cnd.and(keywordGroup); + } + cnd.asc("loginname"); + sql.setCondition(cnd); + return listPageMap(1, 10, sql).getList(); + } + @Override public NutMap info(String id) { if (StrUtil.isBlank(id)) { diff --git a/src/main/resources/db/proposal_add_missing_unit_reply_actors.sql b/src/main/resources/db/proposal_add_missing_unit_reply_actors.sql new file mode 100644 index 00000000..452e7724 --- /dev/null +++ b/src/main/resources/db/proposal_add_missing_unit_reply_actors.sql @@ -0,0 +1,156 @@ +-- 为当前届次提案流程补充遗漏的承办单位负责人待办任务。 +-- 目标人员:医学院机关 石萍(070775)、体育学院 乔润华(067890)。 +-- 处理范围:当前启用届次、JDHTA_NC 流程、仍处于待办理状态的承办单位答复任务。 +-- 原任务办理人予以保留,NOT EXISTS 保证脚本可重复执行。 + +SET @now_ms := CAST(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000 AS UNSIGNED); + +-- 执行前校验:应返回两名有效用户,且角色承办单位分别为 4002 和 6200。 +SELECT + target.account, + target.underTakeId, + target_user.id AS userId, + target_user.username, + undertake.name AS underTakeName, + leader_role.code AS roleCode +FROM ( + SELECT '070775' AS account, '4002' AS underTakeId + UNION ALL + SELECT '067890' AS account, '6200' AS underTakeId +) target +INNER JOIN sys_user target_user + ON target_user.loginname = target.account + AND target_user.disabled = 0 + AND target_user.delFlag = 0 +INNER JOIN sys_role leader_role + ON leader_role.code = 'PROPOSAL_UNIT_LEADER' +INNER JOIN sys_user_role target_role + ON target_role.userId = target_user.id + AND target_role.roleId = leader_role.id + AND target_role.underTakeId = target.underTakeId +INNER JOIN proposal_undertake undertake + ON undertake.id = target_role.underTakeId + AND undertake.enable = 1 + AND undertake.delFlag = 0 +ORDER BY target.account; + +START TRANSACTION; + +-- 流程任务参与人在任务创建时固化;本次将当前负责人追加到本单位已有的活动任务中。 +INSERT INTO wf_process_task_actor + (createdBy, createdAt, updatedBy, updatedAt, delFlag, + processTaskId, actorId, actorAccount, actorName, actorUnitName, actorUnitId) +SELECT + COALESCE(MIN(source_actor.createdBy), target_user.id) AS createdBy, + @now_ms AS createdAt, + COALESCE(MIN(source_actor.createdBy), target_user.id) AS updatedBy, + @now_ms AS updatedAt, + 0 AS delFlag, + task.id AS processTaskId, + target_user.id AS actorId, + target_user.loginname AS actorAccount, + target_user.username AS actorName, + undertake.name AS actorUnitName, + undertake.id AS actorUnitId +FROM ( + SELECT '070775' AS account, '4002' AS underTakeId + UNION ALL + SELECT '067890' AS account, '6200' AS underTakeId +) target +INNER JOIN sys_user target_user + ON target_user.loginname = target.account + AND target_user.disabled = 0 + AND target_user.delFlag = 0 +INNER JOIN sys_role leader_role + ON leader_role.code = 'PROPOSAL_UNIT_LEADER' +INNER JOIN sys_user_role target_role + ON target_role.userId = target_user.id + AND target_role.roleId = leader_role.id + AND target_role.underTakeId = target.underTakeId +INNER JOIN proposal_undertake undertake + ON undertake.id = target_role.underTakeId + AND undertake.enable = 1 + AND undertake.delFlag = 0 +INNER JOIN teacher_congress_session current_session + ON current_session.enable = 1 + AND (current_session.delFlag = 0 OR current_session.delFlag IS NULL) +INNER JOIN proposal_info proposal + ON proposal.sessionId = current_session.id + AND proposal.delFlag = 0 +INNER JOIN wf_process_instance instance + ON instance.businessNo = proposal.id + AND instance.delFlag = 0 +INNER JOIN wf_process_define process_define + ON process_define.id = instance.processDefineId + AND process_define.name = 'JDHTA_NC' + AND process_define.delFlag = 0 +INNER JOIN wf_process_task task + ON task.processInstanceId = instance.id + AND task.taskState = 10 + AND task.delFlag = 0 + AND task.taskName IN ('master_reply', 'opinion_master_reply', 'slave_reply') +INNER JOIN proposal_reply_unit reply_unit + ON reply_unit.proposalId = proposal.id + AND reply_unit.unitId = target.underTakeId + AND reply_unit.delFlag = 0 + AND ( + (task.taskName IN ('master_reply', 'opinion_master_reply') AND reply_unit.isMaster = 1) + OR (task.taskName = 'slave_reply' AND reply_unit.isMaster = 0) + ) +INNER JOIN wf_process_task_actor source_actor + ON source_actor.processTaskId = task.id + AND source_actor.actorUnitId = target.underTakeId + AND source_actor.delFlag = 0 +WHERE NOT EXISTS ( + SELECT 1 + FROM wf_process_task_actor existing_actor + WHERE existing_actor.processTaskId = task.id + AND existing_actor.actorId = target_user.id + AND existing_actor.delFlag = 0 +) +GROUP BY + task.id, + target_user.id, + target_user.loginname, + target_user.username, + undertake.id, + undertake.name; + +-- 当前开发环境预计为 5;线上以任务执行时的实际待办数量为准。 +SELECT ROW_COUNT() AS insertedRows; + +COMMIT; + +-- 执行后核验:查看两名负责人当前届次下已获得的承办单位答复任务。 +SELECT DISTINCT + actor.actorAccount, + actor.actorName, + actor.actorUnitName, + task.id AS taskId, + proposal.code AS proposalCode, + proposal.name AS proposalName, + task.displayName AS taskName +FROM wf_process_task_actor actor +INNER JOIN wf_process_task task + ON task.id = actor.processTaskId + AND task.taskState = 10 + AND task.delFlag = 0 + AND task.taskName IN ('master_reply', 'opinion_master_reply', 'slave_reply') +INNER JOIN wf_process_instance instance + ON instance.id = task.processInstanceId + AND instance.delFlag = 0 +INNER JOIN wf_process_define process_define + ON process_define.id = instance.processDefineId + AND process_define.name = 'JDHTA_NC' + AND process_define.delFlag = 0 +INNER JOIN proposal_info proposal + ON proposal.id = instance.businessNo + AND proposal.delFlag = 0 +INNER JOIN teacher_congress_session current_session + ON current_session.id = proposal.sessionId + AND current_session.enable = 1 + AND (current_session.delFlag = 0 OR current_session.delFlag IS NULL) +WHERE actor.actorAccount IN ('070775', '067890') + AND actor.actorUnitId IN ('4002', '6200') + AND actor.delFlag = 0 +ORDER BY actor.actorAccount, task.id; diff --git a/src/main/resources/db/proposal_add_unit_reply_actor_092108.sql b/src/main/resources/db/proposal_add_unit_reply_actor_092108.sql new file mode 100644 index 00000000..3c0461da --- /dev/null +++ b/src/main/resources/db/proposal_add_unit_reply_actor_092108.sql @@ -0,0 +1,126 @@ +-- 为龚宁江(092108)补充研究生院(党委研究生工作部)的待办承办单位答复任务。 +-- 适用范围:仅处理当前仍为待办理状态的主办、意见主办及协办单位答复任务。 +-- 执行结果:保留任务原办理人,并追加龚宁江;使用 NOT EXISTS 保证脚本可重复执行。 + +SET @target_account := '092108'; +SET @undertake_id := '2009'; +SET @now_ms := CAST(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000 AS UNSIGNED); + +-- 执行前确认目标用户已绑定研究生院的提案承办单位领导角色。 +SELECT + u.id AS userId, + u.loginname, + u.username, + pu.id AS underTakeId, + pu.name AS underTakeName, + r.code AS roleCode +FROM sys_user u +INNER JOIN sys_user_role ur ON ur.userId = u.id +INNER JOIN sys_role r ON r.id = ur.roleId +INNER JOIN proposal_undertake pu ON pu.id = ur.underTakeId +WHERE u.loginname = @target_account + AND u.disabled = 0 + AND u.delFlag = 0 + AND r.code = 'PROPOSAL_UNIT_LEADER' + AND ur.underTakeId = @undertake_id + AND pu.enable = 1 + AND pu.delFlag = 0; + +START TRANSACTION; + +-- 流程任务创建后参与人不会随角色配置自动变化,因此为匹配承办单位的活动任务补充当前负责人。 +INSERT INTO wf_process_task_actor + (createdBy, createdAt, updatedBy, updatedAt, delFlag, + processTaskId, actorId, actorAccount, actorName, actorUnitName, actorUnitId) +SELECT + COALESCE(MIN(source_actor.createdBy), target_user.id) AS createdBy, + @now_ms AS createdAt, + COALESCE(MIN(source_actor.createdBy), target_user.id) AS updatedBy, + @now_ms AS updatedAt, + 0 AS delFlag, + task.id AS processTaskId, + target_user.id AS actorId, + target_user.loginname AS actorAccount, + target_user.username AS actorName, + undertake.name AS actorUnitName, + undertake.id AS actorUnitId +FROM wf_process_task task +INNER JOIN wf_process_instance instance + ON instance.id = task.processInstanceId + AND instance.delFlag = 0 +INNER JOIN proposal_info proposal + ON proposal.id = instance.businessNo + AND proposal.delFlag = 0 +INNER JOIN proposal_reply_unit reply_unit + ON reply_unit.proposalId = proposal.id + AND reply_unit.unitId = @undertake_id + AND reply_unit.delFlag = 0 + AND ( + (task.taskName IN ('master_reply', 'opinion_master_reply') AND reply_unit.isMaster = 1) + OR (task.taskName = 'slave_reply' AND reply_unit.isMaster = 0) + ) +INNER JOIN wf_process_task_actor source_actor + ON source_actor.processTaskId = task.id + AND source_actor.actorUnitId = @undertake_id + AND source_actor.delFlag = 0 +INNER JOIN sys_user target_user + ON target_user.loginname = @target_account + AND target_user.disabled = 0 + AND target_user.delFlag = 0 +INNER JOIN sys_role leader_role + ON leader_role.code = 'PROPOSAL_UNIT_LEADER' +INNER JOIN sys_user_role target_role + ON target_role.userId = target_user.id + AND target_role.roleId = leader_role.id + AND target_role.underTakeId = @undertake_id +INNER JOIN proposal_undertake undertake + ON undertake.id = target_role.underTakeId + AND undertake.enable = 1 + AND undertake.delFlag = 0 +WHERE task.taskState = 10 + AND task.delFlag = 0 + AND task.taskName IN ('master_reply', 'opinion_master_reply', 'slave_reply') + AND NOT EXISTS ( + SELECT 1 + FROM wf_process_task_actor existing_actor + WHERE existing_actor.processTaskId = task.id + AND existing_actor.actorId = target_user.id + AND existing_actor.delFlag = 0 + ) +GROUP BY + task.id, + target_user.id, + target_user.loginname, + target_user.username, + undertake.id, + undertake.name; + +SELECT ROW_COUNT() AS insertedRows; + +COMMIT; + +-- 执行后核验:应返回龚宁江当前可以查看、办理的研究生院承办答复任务。 +SELECT DISTINCT + task.id AS taskId, + proposal.code AS proposalCode, + proposal.name AS proposalName, + task.displayName AS taskName, + actor.actorAccount, + actor.actorName, + actor.actorUnitName +FROM wf_process_task_actor actor +INNER JOIN wf_process_task task + ON task.id = actor.processTaskId + AND task.taskState = 10 + AND task.delFlag = 0 +INNER JOIN wf_process_instance instance + ON instance.id = task.processInstanceId + AND instance.delFlag = 0 +INNER JOIN proposal_info proposal + ON proposal.id = instance.businessNo + AND proposal.delFlag = 0 +WHERE actor.actorAccount = @target_account + AND actor.actorUnitId = @undertake_id + AND actor.delFlag = 0 + AND task.taskName IN ('master_reply', 'opinion_master_reply', 'slave_reply') +ORDER BY task.id; diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/unitReply/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/unitReply/index.html index c15659ad..04955ae9 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/unitReply/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/unitReply/index.html @@ -406,7 +406,10 @@ layout("/layouts/platform.html"){ // 查询转办用户 selectTransferUser(keyword) { - this.$axios.post("/platform/proposal/unitReply/selectViceUser", {keyword}).then(res => { + this.$axios.post("/platform/proposal/unitReply/selectViceUser", { + taskId: this.transferForm.taskId, + keyword + }).then(res => { if (res.code === 0) { this.transferUserOptions = res.data }