From 46afff880c0023859f312030c7d89ba8cbfae264 Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 24 Jul 2026 10:31:32 +0800 Subject: [PATCH] commit --- .../app/sys/controller/SysUnitController.java | 92 +++++++---- .../app/sys/services/SysUnitService.java | 12 ++ .../sys/services/impl/SysUnitServiceImpl.java | 77 +++++++++ .../config/ProposalConfigUnitController.java | 148 ++++++++++++----- .../ProposalCommitteeFilingController.java | 13 ++ ...ProposalCommitteeFilingUnitController.java | 12 ++ .../transact/ProposalControlController.java | 53 +++--- .../transact/ProposalMineController.java | 18 ++ .../transact/ProposalSecondedController.java | 18 ++ .../ProposalUnderTakeReadController.java | 7 +- .../ProposalMasterUnitAssignmentHandler.java | 10 +- ...ProposalSchoolLeaderAssignmentHandler.java | 33 +--- .../ProposalSlaveUnitAssignmentHandler.java | 11 +- .../ProposalUnitAssignmentHandler.java | 18 +- .../service/ProposalControlService.java | 30 ++++ .../service/common/ProposalCommonService.java | 32 ++++ .../common/ProposalCommonServiceImpl.java | 120 +++++++++++++- ...roposalCommitteeFilingUnitServiceImpl.java | 71 ++++++-- .../impl/ProposalControlServiceImpl.java | 154 ++++++++++++++++++ ...ProposalFeedbackEvaluationServiceImpl.java | 5 +- ...oposalSchoolLeaderApprovalServiceImpl.java | 4 +- .../views/platform/sys/unit/unitLeader.js | 7 +- .../proposal/config/unit/index.html | 8 +- .../democratic/proposal/config/unit/user.js | 65 ++++++-- .../proposal/export/comprehensive/index.html | 3 +- .../proposal/transact/control/index.html | 24 +-- .../proposal/transact/mine/index.html | 12 +- .../proposal/transact/seconded/index.html | 2 +- .../zhghh5/democratic/proposal/common/info.js | 31 +++- .../transact/committeeFiling/index.html | 56 ++++++- .../transact/committeeFilingUnit/index.html | 56 ++++++- .../proposal/transact/mine/index.html | 2 +- .../proposal/transact/seconded/index.html | 2 +- .../proposal/transact/unitReply/index.html | 19 ++- 34 files changed, 969 insertions(+), 256 deletions(-) create mode 100644 src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalControlService.java create mode 100644 src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalControlServiceImpl.java diff --git a/src/main/java/com/budwk/app/sys/controller/SysUnitController.java b/src/main/java/com/budwk/app/sys/controller/SysUnitController.java index b833c61..3ec5733 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysUnitController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysUnitController.java @@ -14,11 +14,9 @@ import com.budwk.app.base.exception.BaseException; 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.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.services.SysDictService; import com.budwk.app.sys.services.SysRoleService; import com.budwk.app.sys.services.SysUnitService; import com.budwk.app.sys.services.SysUserService; @@ -26,6 +24,7 @@ import com.budwk.app.sys.views.View_user; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig; +import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake; import io.swagger.annotations.ApiOperation; import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Chain; @@ -67,8 +66,6 @@ public class SysUnitController { @Inject private SysUserService sysUserService; @Inject - private SysDictService sysDictService; - @Inject private SysRoleService sysRoleService; @At("") @@ -115,37 +112,7 @@ public class SysUnitController { @Ok("json") @SaCheckLogin public Object leaderPageData(PageForm pageForm, String unitId) { - List unitRoles = sysDictService.getSubListByCode("UNIT_ROLES"); - if (Lang.isEmpty(unitRoles)) { - return Result.success(); - } - List unitRoleCodes = unitRoles.stream().map(Sys_dict::getCode).toList(); - Sql sql = Sqls.create(""" - SELECT - t1.userId, - t2.username, - t2.loginname, - t3.`code` AS roleCode, - t3.`name` AS roleName - FROM - sys_user_role t1 - LEFT JOIN sys_user t2 ON t2.id = t1.userId - LEFT JOIN sys_role t3 ON t3.id = t1.roleId - $condition - """); - Cnd cnd = Cnd.where("t3.`code`", "in", unitRoleCodes); - cnd.and("t1.unitId", "=", unitId); - if (StrUtil.isAllNotBlank(pageForm.getSearchName(), pageForm.getSearchKeyword())) { - switch (pageForm.getSearchName()) { - case "loginname" -> cnd.where().andLike("t2.loginname", pageForm.getSearchKeyword()); - case "username" -> cnd.where().andLike("t2.username", pageForm.getSearchKeyword()); - case "roleName" -> cnd.where().andLike("t3.`name`", pageForm.getSearchKeyword()); - } - } - cnd.asc("t2.username"); - sql.setCondition(cnd); - Pagination pagination = sysUserService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); - return Result.success(pagination); + return Result.success(sysUnitService.getUnitLeaderPageData(pageForm, unitId)); } @At @@ -182,6 +149,25 @@ public class SysUnitController { @Ok("json") @Aop(TransAop.READ_COMMITTED) public Result insertUnitUserRole(String userId, String roleCode, String unitId) { + // 党委书记按分工会归属维护,单位页仅用于展示,避免把分工会角色错误写成单个单位角色。 + if (RoleConstant.UNIT_PARTY_SECRETARY.name().equals(roleCode)) { + return Result.error("单位党委书记请在分工会管理中设置"); + } + // 页面选择“单位分管校领导”时,维护提案流程实际使用的承办单位角色数据。 + if ("UNIT_SCHOOL_LEADER".equals(roleCode)) { + ProposalUndertake undertake = getProposalUndertakeByUnitId(unitId); + Sys_role schoolLeaderRole = sysRoleService.fetch(Cnd.where("code", "=", RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name())); + if (Lang.isEmpty(schoolLeaderRole)) { + throw new BaseException("未找到单位分管校领导角色"); + } + sysRoleService.clear("sys_user_role", Cnd.where("roleId", "=", schoolLeaderRole.getId()) + .and("userId", "=", userId).and("underTakeId", "=", undertake.getId())); + sysRoleService.insert("sys_user_role", Chain.make("roleId", schoolLeaderRole.getId()) + .add("userId", userId).add("underTakeId", undertake.getId())); + sysRoleService.clearCache(); + sysUserService.clearCache(); + return Result.success(); + } Sys_role role = sysRoleService.fetch(Cnd.where("code", "=", roleCode)); if (Lang.isEmpty(role)) { throw new BaseException("无法找到{}对应编码的角色", roleCode); @@ -197,6 +183,23 @@ public class SysUnitController { @Ok("json") @Aop(TransAop.READ_COMMITTED) public Result deleteUnitUserRole(String userId, String roleCode, String unitId) { + // 党委书记按分工会归属维护,单位页只展示其现有设置。 + if (RoleConstant.UNIT_PARTY_SECRETARY.name().equals(roleCode)) { + return Result.error("单位党委书记请在分工会管理中设置"); + } + // 分管校领导列表返回的是提案流程实际角色,删除时按承办单位绑定维度清理。 + if (RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name().equals(roleCode)) { + ProposalUndertake undertake = getProposalUndertakeByUnitId(unitId); + Sys_role schoolLeaderRole = sysRoleService.fetch(Cnd.where("code", "=", RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name())); + if (Lang.isEmpty(schoolLeaderRole)) { + throw new BaseException("未找到单位分管校领导角色"); + } + sysRoleService.clear("sys_user_role", Cnd.where("roleId", "=", schoolLeaderRole.getId()) + .and("userId", "=", userId).and("underTakeId", "=", undertake.getId())); + sysRoleService.clearCache(); + sysUserService.clearCache(); + return Result.success(); + } Sys_role role = sysRoleService.fetch(Cnd.where("code", "=", roleCode)); if (Lang.isEmpty(role)) { throw new BaseException("无法找到{}对应编码的角色", roleCode); @@ -207,6 +210,25 @@ public class SysUnitController { return Result.success(); } + /** + * 根据系统单位编码找到对应提案承办单位,保证单位页维护的分管校领导直接用于提案流程。 + * + * @param unitId 系统单位 ID + * @return 对应的提案承办单位 + */ + private ProposalUndertake getProposalUndertakeByUnitId(String unitId) { + Sys_unit unit = sysUnitService.fetch(unitId); + if (unit == null || StrUtil.isBlank(unit.getUnitcode())) { + throw new BaseException("当前单位不存在或未设置单位编码"); + } + ProposalUndertake undertake = sysUnitService.dao().fetch(ProposalUndertake.class, + Cnd.where("code", "=", unit.getUnitcode())); + if (undertake == null) { + throw new BaseException("当前单位未同步为提案承办单位,无法设置分管校领导"); + } + return undertake; + } + @At("/child") @Ok("json") @SaCheckLogin diff --git a/src/main/java/com/budwk/app/sys/services/SysUnitService.java b/src/main/java/com/budwk/app/sys/services/SysUnitService.java index 257cd73..8aaf706 100644 --- a/src/main/java/com/budwk/app/sys/services/SysUnitService.java +++ b/src/main/java/com/budwk/app/sys/services/SysUnitService.java @@ -1,5 +1,7 @@ package com.budwk.app.sys.services; +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.PageForm; import com.budwk.app.base.service.BaseService; import com.budwk.app.sys.models.Sys_unit; @@ -22,6 +24,16 @@ public interface SysUnitService extends BaseService { */ void deleteAndChild(Sys_unit unit); + /** + * 查询单位干部。单位党委书记按分工会归属展示,提案分管校领导按承办单位归属展示, + * 避免把两类已有业务角色误当作直接绑定在 sys_unit 上的角色。 + * + * @param pageForm 分页及查询条件 + * @param unitId 当前系统单位 ID + * @return 单位干部分页数据,包含数据来源及是否允许在本页维护的标记 + */ + Pagination getUnitLeaderPageData(PageForm pageForm, String unitId); + /** * 清空缓存 */ diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysUnitServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysUnitServiceImpl.java index 953e014..e149e66 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysUnitServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysUnitServiceImpl.java @@ -2,6 +2,9 @@ package com.budwk.app.sys.services.impl; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.constant.RedisConstant; +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.base.page.Pagination; +import com.budwk.app.base.param.PageForm; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.sys.models.Sys_unit; import com.budwk.app.sys.services.SysUnitService; @@ -10,6 +13,7 @@ import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.Strings; @@ -69,6 +73,79 @@ public class SysUnitServiceImpl extends BaseServiceImpl implements Sys } } + /** + * 查询单位干部,并按角色的实际维护维度关联数据。 + * 单位党委书记由分工会干部维护,提案分管校领导由提案承办单位维护,二者均不应按 + * sys_user_role.unitId 直接过滤,否则迁移和现有配置的数据都无法在单位页展示。 + * + * @param pageForm 分页及查询条件 + * @param unitId 当前系统单位 ID + * @return 含角色来源和页面维护权限的分页结果 + */ + @Override + public Pagination getUnitLeaderPageData(PageForm pageForm, String unitId) { + String searchCondition = switch (pageForm.getSearchName()) { + case "loginname" -> " AND t2.loginname LIKE CONCAT('%', @searchKeyword, '%')"; + case "username" -> " AND t2.username LIKE CONCAT('%', @searchKeyword, '%')"; + case "roleName" -> " AND (t3.name LIKE CONCAT('%', @searchKeyword, '%') " + + "OR t3.code = '" + RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name() + "')"; + default -> ""; + }; + if (StrUtil.isBlank(pageForm.getSearchKeyword())) { + searchCondition = ""; + } + + Sql sql = Sqls.create(""" + SELECT + t1.userId, + t2.username, + t2.loginname, + t3.`code` AS roleCode, + CASE t3.`code` + WHEN 'PROPOSAL_BRANCH_SCHOOL_LEADER' THEN '单位分管校领导' + ELSE t3.`name` + END AS roleName, + CASE + WHEN t3.`code` = 'UNIT_PARTY_SECRETARY' THEN 0 + ELSE 1 + END AS canManage + FROM sys_user_role t1 + LEFT JOIN sys_user t2 ON t2.id = t1.userId + LEFT JOIN sys_role t3 ON t3.id = t1.roleId + LEFT JOIN sys_unit currentUnit ON currentUnit.id = @unitId + LEFT JOIN proposal_undertake undertake ON undertake.id = t1.underTakeId + WHERE currentUnit.id = @unitId + AND ( + ( + t3.`code` IN ( + SELECT dictRole.`code` + FROM sys_dict dictRole + WHERE dictRole.parentId = ( + SELECT dictParent.id FROM sys_dict dictParent + WHERE dictParent.`code` = 'UNIT_ROLES' LIMIT 1 + ) + ) + AND ( + t1.unitId = currentUnit.id + OR ( + t3.`code` = 'UNIT_PARTY_SECRETARY' + AND t1.unionId = currentUnit.unionId + ) + ) + ) + OR ( + t3.`code` = 'PROPOSAL_BRANCH_SCHOOL_LEADER' + AND undertake.`code` = currentUnit.unitcode + ) + ) + """ + searchCondition + " ORDER BY t2.username"); + sql.setParam("unitId", unitId); + if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + sql.setParam("searchKeyword", pageForm.getSearchKeyword()); + } + return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + } + @CacheRemoveAll public void clearCache() { 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 5c75880..91e32fa 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,10 +13,10 @@ 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.sys.models.Sys_role; +import com.budwk.app.sys.models.Sys_unit; import com.budwk.app.sys.models.Sys_user_role; import com.budwk.app.sys.services.SysRoleService; import com.budwk.app.web.commons.auth.utils.SecurityUtil; -import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig; import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake; import com.budwk.app.zhgh.democratic.proposal.service.ProposalUndertakeConfigService; import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; @@ -37,6 +37,7 @@ import org.nutz.mvc.annotation.Param; import javax.validation.Valid; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -82,26 +83,34 @@ public class ProposalConfigUnitController { Sql sql = Sqls.create(""" SELECT t1.*, - u1.username AS branchSchoolLeader, - u2.username AS unitLeader + branchLeader.branchSchoolLeader, + unitManager.unitLeader FROM - proposal_undertake t1 - LEFT JOIN sys_user_role sur1 ON sur1.underTakeId = t1.id - AND sur1.roleId = ( SELECT r.id FROM sys_role r WHERE r.CODE = @PROPOSAL_BRANCH_SCHOOL_LEADER ) - LEFT JOIN sys_user u1 ON u1.id = sur1.userId - LEFT JOIN sys_user_role sur2 ON sur2.underTakeId = t1.id - AND sur2.roleId = ( SELECT r.id FROM sys_role r WHERE r.CODE = @PROPOSAL_UNIT_LEADER) - LEFT JOIN sys_user u2 ON u2.id = sur2.userId + proposal_undertake t1 + LEFT JOIN sys_unit sysUnit ON sysUnit.unitcode = t1.code + LEFT JOIN ( + SELECT sur.underTakeId, + GROUP_CONCAT(DISTINCT u.username ORDER BY u.username SEPARATOR '、') AS branchSchoolLeader + FROM sys_user_role sur + JOIN sys_role r ON r.id = sur.roleId AND r.CODE = @PROPOSAL_BRANCH_SCHOOL_LEADER + LEFT JOIN sys_user u ON u.id = sur.userId + GROUP BY sur.underTakeId + ) branchLeader ON branchLeader.underTakeId = t1.id + LEFT JOIN ( + SELECT sur.unitId, + GROUP_CONCAT(DISTINCT u.username ORDER BY u.username SEPARATOR '、') AS unitLeader + FROM sys_user_role sur + JOIN sys_role r ON r.id = sur.roleId AND r.CODE = 'UNIT_MANAGER' + LEFT JOIN sys_user u ON u.id = sur.userId + GROUP BY sur.unitId + ) unitManager ON unitManager.unitId = sysUnit.id $condition """); sql.setParam("PROPOSAL_BRANCH_SCHOOL_LEADER", RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name()); - sql.setParam("PROPOSAL_UNIT_LEADER", RoleConstant.PROPOSAL_UNIT_LEADER.name()); Cnd cnd = Cnd.NEW(); if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { - cnd.where().orLike("u1.username", pageForm.getSearchKeyword()); - cnd.where().orLike("u1.loginname", pageForm.getSearchKeyword()); - cnd.where().orLike("u2.username", pageForm.getSearchKeyword()); - cnd.where().orLike("u2.loginname", pageForm.getSearchKeyword()); + cnd.where().orLike("branchLeader.branchSchoolLeader", pageForm.getSearchKeyword()); + cnd.where().orLike("unitManager.unitLeader", pageForm.getSearchKeyword()); cnd.where().orLike("t1.name", pageForm.getSearchKeyword()); } // 承办单位列表未指定有效排序时继续按单位编码升序。 @@ -167,15 +176,18 @@ public class ProposalConfigUnitController { @SLog(tag = "提案", msg = "设置承办单位领导") @Aop(TransAop.READ_COMMITTED) public Result updateUnitLeader(@Valid String id, @Param("userIds") String[] userIds) { - Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_LEADER); - //删除原来的用户 + Sys_unit unit = getSystemUnitByUndertakeId(id); + Sys_role sysRole = sysRoleService.getByCode("UNIT_MANAGER"); + // 系统单位负责人是提案承办单位答复的唯一人员来源,按系统单位维度整体覆盖。 dao.clear(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()) - .and(Sys_user_role::getUnderTakeId, "=", id)); + .and(Sys_user_role::getUnitId, "=", unit.getId())); List sysUserRoles = Arrays.stream(userIds).map(userId -> { Sys_user_role sysUserRole = new Sys_user_role(); sysUserRole.setRoleId(sysRole.getId()); sysUserRole.setUserId(userId); - sysUserRole.setUnderTakeId(id); + sysUserRole.setUnitId(unit.getId()); + // 与单位管理页保持同一角色关联结构,确保两页均可维护同一位负责人。 + sysUserRole.setUnderTakeId(unit.getId()); return sysUserRole; }).toList(); dao.insert(sysUserRoles); @@ -191,25 +203,21 @@ public class ProposalConfigUnitController { @At @SaCheckPermission("proposal.config.unit") public Result branchSchoolLeaderTransferData(@Valid String id) { - //已设置的领导 - Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER); - List sysUserRoles = dao.query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()).and(Sys_user_role::getUnderTakeId, "=", id)); - List selectUserIds = sysUserRoles.stream().map(Sys_user_role::getUserId).toList(); + return Result.success(createBranchSchoolLeaderTransferData(id, null)); + } - //查询全部的校领导 - Sql sql = Sqls.create("select id,username,loginname,unitname,concat(username,'(',loginname,')') label from vw_user where unitId in (@unitId)"); - ProposalConfig proposalConfig = dao.fetch(ProposalConfig.class, Cnd.NEW()); - if(ObjectUtil.isNull(proposalConfig)){ - return Result.error("提案全局配置未配置,请在提案管理-基础配置-提案配置页面进行配置。"); - } - if(ObjectUtil.isEmpty(proposalConfig.getSchoolLeaderUnitIds())){ - return Result.error("校领导单位未配置,请在提案管理-基础配置-提案配置页面进行配置。"); - } - - sql.setParam("unitId", proposalConfig.getSchoolLeaderUnitIds()); - List allUsers = baseService.listMap(sql); - - return Result.success(Map.of("allUsers", allUsers, "selectUserIds", selectUserIds)); + /** + * 单位分管校领导候选人员。首次仅返回前五十人;按姓名或工号搜索时由服务端重新查询。 + * 已选人员始终补入候选集合,保证不在当前搜索结果中的人员也能回显。 + * + * @param id 承办单位 ID + * @param keyword 姓名或工号关键字 + * @return 候选人员、已选人员和总数 + */ + @At + @SaCheckPermission("proposal.config.unit") + public Result branchSchoolLeaderUserPageData(@Valid String id, String keyword) { + return Result.success(createBranchSchoolLeaderTransferData(id, keyword)); } /** @@ -221,18 +229,20 @@ public class ProposalConfigUnitController { @At @SaCheckPermission("proposal.config.unit") public Result unitUsersTransferData(@Valid String id) { - //已设置的单位领导 - Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_LEADER); - List sysUserRoles = dao.query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()).and(Sys_user_role::getUnderTakeId, "=", id)); + // 已设置的系统单位负责人。 + Sys_unit unit = getSystemUnitByUndertakeId(id); + Sys_role sysRole = sysRoleService.getByCode("UNIT_MANAGER"); + List sysUserRoles = dao.query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()).and(Sys_user_role::getUnitId, "=", unit.getId())); List selectUserIds = sysUserRoles.stream().map(Sys_user_role::getUserId).toList(); //查询该单位下全部的用户 - ProposalUndertake proposalUndertake = dao.fetch(ProposalUndertake.class, id); - Sql sql = Sqls.create("select id,username,loginname,unitname,concat(username,'(',loginname,')') label from vw_user $condition"); Cnd cnd = Cnd.NEW(); - cnd.and("unitCode","=",proposalUndertake.getCode()); - cnd.orEX("id","in",selectUserIds); + cnd.and("unitCode","=",unit.getUnitcode()); + if (ObjectUtil.isNotEmpty(selectUserIds)) { + // 已设置负责人可能不属于当前单位,仍要放入候选集合,才能在穿梭框回显。 + cnd.orEX("id","in",selectUserIds); + } sql.setCondition(cnd); // sql.setParam("unitCode", proposalUndertake.getCode()); // sql.setParam("selectUserIds", selectUserIds); @@ -241,6 +251,56 @@ public class ProposalConfigUnitController { return Result.success(Map.of("allUsers", allUsers, "selectUserIds", selectUserIds)); } + /** 根据承办单位编码查询系统单位,保证配置页面和流程处理人使用同一单位数据。 */ + private Sys_unit getSystemUnitByUndertakeId(String undertakeId) { + ProposalUndertake undertake = dao.fetch(ProposalUndertake.class, undertakeId); + if (undertake == null || StrUtil.isBlank(undertake.getCode())) { + throw new IllegalArgumentException("承办单位不存在或未设置单位编码"); + } + Sys_unit unit = dao.fetch(Sys_unit.class, Cnd.where(Sys_unit::getUnitcode, "=", undertake.getCode())); + if (unit == null) { + throw new IllegalArgumentException("承办单位未关联系统单位,请先同步系统单位"); + } + return unit; + } + + /** + * 组装分管校领导穿梭框数据。默认或搜索时均固定返回前五十条候选人,并合并已选人员。 + */ + @SuppressWarnings("unchecked") + private Map createBranchSchoolLeaderTransferData(String undertakeId, String keyword) { + Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER); + List selectUserIds = dao.query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()) + .and(Sys_user_role::getUnderTakeId, "=", undertakeId)) + .stream().map(Sys_user_role::getUserId).toList(); + + Sql sql = Sqls.create("select id,username,loginname,unitname,concat(username,'(',loginname,')') label from vw_user $condition"); + Cnd cnd = Cnd.NEW(); + if (StrUtil.isNotBlank(keyword)) { + cnd.where().orLike("username", keyword); + cnd.where().orLike("loginname", keyword); + } + // 候选人员按所属分工会归类展示,同一分工会内再按姓名、工号排序。 + cnd.asc("unionName").asc("username").asc("loginname"); + sql.setCondition(cnd); + Pagination page = (Pagination) baseService.listPageMap(1, 50, sql); + + LinkedHashMap userMap = new LinkedHashMap<>(); + for (NutMap user : page.getList()) { + userMap.put(user.getString("id"), user); + } + if (ObjectUtil.isNotEmpty(selectUserIds)) { + Sql selectedSql = Sqls.create("select id,username,loginname,unitname,concat(username,'(',loginname,')') label from vw_user where id in (@userIds)"); + selectedSql.setParam("userIds", selectUserIds); + List selectedUsers = (List) baseService.listMap(selectedSql); + for (NutMap user : selectedUsers) { + userMap.putIfAbsent(user.getString("id"), user); + } + } + return Map.of("allUsers", new ArrayList<>(userMap.values()), "selectUserIds", selectUserIds, + "totalCount", page.getTotalCount()); + } + @At @SaCheckPermission("proposal.config.unit") @Aop(TransAop.READ_COMMITTED) diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java index e7884b5..84e8adb 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingController.java @@ -137,6 +137,19 @@ public class ProposalCommitteeFilingController { return pageData(pageForm, approval); } + /** + * 提案委员会立案 H5 撤回。 + * 复用 PC 端撤回服务,确保并案提案仍按整组撤回处理。 + */ + @At("/h5/revokeTask") + @SaCheckPermission("h5.proposal.committeeFiling") + @ApiOperation("提案委员会立案 H5 撤回任务") + @Aop(TransAop.READ_COMMITTED) + public Result h5RevokeTask(@Param("taskId") Long taskId, @Param("proposalId") String proposalId) { + proposalCommitteeFilingService.revokeTask(taskId, proposalId); + return Result.success(); + } + @At @Ok("void") @SaCheckPermission("proposal.committeeFiling") diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java index f57221d..cfc9b1c 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalCommitteeFilingUnitController.java @@ -150,6 +150,18 @@ public class ProposalCommitteeFilingUnitController { return pageData(pageForm, approval); } + /** + * 提案委员会确认承办单位 H5 撤回。 + * 与 PC 端共用现有撤回规则,保证并案提案会整组回退。 + */ + @At("/h5/revokeTask") + @SaCheckPermission("h5.proposal.committeeFilingUnit") + @ApiOperation("提案委员会确认承办单位 H5 撤回任务") + @Aop(TransAop.READ_COMMITTED) + public Result h5RevokeTask(@Param("taskId") Long taskId, @Param("proposalId") String proposalId) { + return revokeTask(taskId, proposalId); + } + @At @Ok("void") @SaCheckPermission("proposal.committeeFilingUnit") diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalControlController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalControlController.java index 9f77a5e..07cbbc1 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalControlController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalControlController.java @@ -5,11 +5,11 @@ import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.result.Result; import com.budwk.app.base.service.BaseService; -import com.budwk.app.flow.enums.ProcessTaskPerformTypeEnum; import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; +import com.budwk.app.zhgh.democratic.proposal.service.ProposalControlService; import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -36,6 +36,8 @@ public class ProposalControlController { private BaseService baseService; @Inject private ProposalCommonService proposalCommonService; + @Inject + private ProposalControlService proposalControlService; @At("") @Ok("beetl:/platform/zhgh/democratic/proposal/transact/control/index.html") @@ -58,20 +60,12 @@ public class ProposalControlController { ins.state instanceState, ins.variable instanceVariable, ins.processDefineId instanceProcessDefineId, - t.id taskId, - t.taskName AS taskKey, - t.displayName taskName, - t.taskType, - t.performType taskPerformType, - t.taskState, - t.finishTime, - t.taskParentId, - t.variable taskVariable, - IFNULL(GROUP_CONCAT(DISTINCT nt.displayName), '结束') curTaskName + MIN(t.id) taskId, + GROUP_CONCAT(DISTINCT t.taskName ORDER BY t.id) taskKeys, + IFNULL(GROUP_CONCAT(DISTINCT t.displayName ORDER BY t.id), '结束') curTaskName 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 wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10 LEFT JOIN proposal_info info ON info.id = ins.businessNo LEFT JOIN proposal_type type ON type.id = info.typeId LEFT JOIN teacher_congress_session tcs ON tcs.id = info.sessionId @@ -86,7 +80,8 @@ public class ProposalControlController { } ProposalSearchParam.buildSearch(cnd, pageForm); - cnd.groupBy("t.id"); + // 一个提案实例可能有多个并行承办单位答复待办;状态调整按实例聚合,只展示一条提案记录。 + cnd.groupBy("ins.id"); // 状态调整列表未指定有效排序时保留任务创建时间倒序。 if (!proposalCommonService.applySafeProposalListOrder(cnd, pageForm, "control")) { cnd.desc("t.createdAt"); @@ -100,25 +95,17 @@ public class ProposalControlController { @SaCheckPermission("proposal.control") @ApiOperation("获取可跳转节点") public Result jumpNodes(Long instanceId) { - if (instanceId == null) { - return Result.error("参数错误"); - } - Sql sql = Sqls.create(""" - SELECT - t.taskName AS value, - MAX(t.displayName) AS label, - MIN(t.id) AS seq - FROM wf_process_task t - WHERE t.processInstanceId = @instanceId - AND t.taskState IN (@doing, @finish) - AND t.performType != @countersign - GROUP BY t.taskName - ORDER BY seq ASC - """); - sql.params().set("instanceId", instanceId); - sql.params().set("doing", ProcessTaskStateEnum.DOING.getCode()); - sql.params().set("finish", ProcessTaskStateEnum.FINISHED.getCode()); - sql.params().set("countersign", ProcessTaskPerformTypeEnum.COUNTERSIGN.getCode()); - return Result.success(baseService.listMap(sql)); + return Result.success(proposalControlService.listJumpNodes(instanceId)); + } + + /** + * 按整个提案流程实例跳转节点,统一清理并行待办后再创建目标节点待办。 + */ + @At + @SaCheckPermission("proposal.control") + @ApiOperation("跳转提案流程节点") + public Result jump(Long instanceId, String targetTaskName) { + proposalControlService.jump(instanceId, targetTaskName); + return Result.success(); } } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalMineController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalMineController.java index e35c77c..3dad69d 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalMineController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalMineController.java @@ -116,6 +116,14 @@ public class ProposalMineController { @SaCheckPermission("proposal.mine") @Aop(TransAop.READ_COMMITTED) public Result pageData(@Valid ProposalSearchParam pageForm, String sessionId) { + return queryPageData(pageForm, sessionId); + } + + /** + * 查询当前登录人的提案分页数据。 + * PC 与 H5 入口共用该私有方法,避免 H5 请求进入带 PC 权限注解的方法。 + */ + private Result queryPageData(ProposalSearchParam pageForm, String sessionId) { Sql sql = Sqls.create(""" SELECT info.*, @@ -163,6 +171,16 @@ public class ProposalMineController { return Result.success(pagination); } + /** + * 我的提案 H5 分页列表。 + * H5 菜单权限与 PC 权限独立,列表查询仍复用 PC 的同一数据口径。 + */ + @At("/h5/pageData") + @SaCheckPermission("h5.proposal.mine") + public Result h5PageData(@Valid ProposalSearchParam pageForm, String sessionId) { + return queryPageData(pageForm, sessionId); + } + @At @SaCheckPermission("proposal.mine") @Aop(TransAop.READ_COMMITTED) diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java index df72f0d..5a3398a 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalSecondedController.java @@ -91,6 +91,14 @@ public class ProposalSecondedController { @At @SaCheckPermission("proposal.seconded") public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval) { + return queryPageData(pageForm, approval); + } + + /** + * 查询当前登录附议人的提案分页数据。 + * PC 与 H5 入口共用该私有方法,避免 H5 请求进入带 PC 权限注解的方法。 + */ + private Result queryPageData(ProposalSearchParam pageForm, boolean approval) { Sql sql = Sqls.create(""" SELECT info.*, @@ -165,6 +173,16 @@ public class ProposalSecondedController { return Result.success(pagination); } + /** + * 提案附议 H5 分页列表。 + * H5 菜单权限与 PC 权限独立,列表查询仍复用 PC 的同一数据口径。 + */ + @At("/h5/pageData") + @SaCheckPermission("h5.proposal.seconded") + public Result h5PageData(@Valid ProposalSearchParam pageForm, boolean approval) { + return queryPageData(pageForm, approval); + } + @At @SaCheckPermission("proposal.seconded") @Aop(TransAop.READ_COMMITTED) diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReadController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReadController.java index 63eda76..e100a9d 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReadController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/transact/ProposalUnderTakeReadController.java @@ -115,12 +115,7 @@ public class ProposalUnderTakeReadController { String unitIdsStr = "''"; if (!isSysAdmin) { - Sys_role role = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_LEADER.name()); - List userRoles = proposalCommonService.dao().query(Sys_user_role.class, - Cnd.where(Sys_user_role::getUserId, "=", SecurityUtil.getUserId()) - .and(Sys_user_role::getRoleId, "=", role.getId()) - ); - unitIds = userRoles.stream().map(Sys_user_role::getUnderTakeId).collect(Collectors.toSet()); + unitIds = new HashSet<>(proposalCommonService.getUserManagedUndertakeIds(SecurityUtil.getUserId())); unitIdsStr = unitIds.stream() .map(id -> "'" + id + "'") .collect(Collectors.joining(",")); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalMasterUnitAssignmentHandler.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalMasterUnitAssignmentHandler.java index 828df63..ec3e981 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalMasterUnitAssignmentHandler.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalMasterUnitAssignmentHandler.java @@ -13,6 +13,7 @@ import com.budwk.app.sys.models.Sys_user_role; import com.budwk.app.sys.services.SysRoleService; import com.budwk.app.zhgh.democratic.proposal.models.ProposalReplyUnit; import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake; +import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; @@ -29,7 +30,7 @@ public class ProposalMasterUnitAssignmentHandler implements AssignmentHandler { @Aop(TransAop.READ_COMMITTED) public List assign(TaskModel model, Execution execution) { Dao dao = ServiceContext.find(Dao.class); - SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class); + ProposalCommonService proposalCommonService = ServiceContext.find(ProposalCommonService.class); // 提案ID String proposalId = execution.getProcessInstance().getBusinessNo(); @@ -64,11 +65,8 @@ public class ProposalMasterUnitAssignmentHandler implements AssignmentHandler { throw new BaseException("主办单位不存在"); } - // 获取负责人 - Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_LEADER); - - List sysUserRoles = dao.query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()).and(Sys_user_role::getUnderTakeId, "=", undertake.getId())); - List selectUserIds = sysUserRoles.stream().map(Sys_user_role::getUserId).toList(); + // 获取系统单位负责人。 + List selectUserIds = proposalCommonService.getUndertakeUnitManagerUserIds(undertake.getId()); if (selectUserIds.isEmpty()) { throw new BaseException("主办单位没有负责人"); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalSchoolLeaderAssignmentHandler.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalSchoolLeaderAssignmentHandler.java index f66b80d..5a8c0d5 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalSchoolLeaderAssignmentHandler.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalSchoolLeaderAssignmentHandler.java @@ -1,25 +1,11 @@ package com.budwk.app.zhgh.democratic.proposal.handler; -import cn.hutool.core.lang.Dict; -import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.exception.BaseException; import com.budwk.app.flow.engine.AssignmentHandler; import com.budwk.app.flow.engine.core.Execution; import com.budwk.app.flow.engine.core.ServiceContext; import com.budwk.app.flow.engine.model.TaskModel; -import com.budwk.app.flow.entity.Candidate; -import com.budwk.app.sys.models.Sys_role; -import com.budwk.app.sys.models.Sys_user; -import com.budwk.app.sys.models.Sys_user_role; -import com.budwk.app.sys.services.SysRoleService; -import com.budwk.app.web.controllers.open.commons.service.CommonService; -import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig; -import org.nutz.dao.Cnd; -import org.nutz.dao.Dao; -import org.nutz.dao.Sqls; -import org.nutz.dao.sql.Sql; -import org.nutz.lang.Lang; -import org.nutz.lang.util.NutMap; +import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import java.util.List; @@ -30,19 +16,18 @@ public class ProposalSchoolLeaderAssignmentHandler implements AssignmentHandler @Override public List assign(TaskModel model, Execution execution) { - CommonService commonService = ServiceContext.find(CommonService.class); - List users = commonService.findUserInfoByRoleCode(RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name()); - - if(Lang.isEmpty(users)) { - throw new BaseException("未查询到相关审批人"); - } - - return users.stream().map(Sys_user::getId).toList(); + ProposalCommonService proposalCommonService = ServiceContext.find(ProposalCommonService.class); + String proposalId = execution.getProcessInstance().getBusinessNo(); + List userIds = proposalCommonService.getMasterUndertakeSchoolLeaderUserIds(proposalId); + if (userIds.isEmpty()) { + throw new BaseException("主办单位未设置分管校领导"); + } + return userIds; } @Override public String getMessage() { - return "获取提案主管校领导"; + return "主办单位分管校领导"; } @Override diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalSlaveUnitAssignmentHandler.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalSlaveUnitAssignmentHandler.java index bb1c732..ec93960 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalSlaveUnitAssignmentHandler.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalSlaveUnitAssignmentHandler.java @@ -11,6 +11,7 @@ import com.budwk.app.sys.models.Sys_user_role; import com.budwk.app.sys.services.SysRoleService; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig; import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake; +import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; @@ -35,21 +36,19 @@ public class ProposalSlaveUnitAssignmentHandler implements AssignmentHandler { return assignee; } - SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class); + ProposalCommonService proposalCommonService = ServiceContext.find(ProposalCommonService.class); // 协办单位id List helpUnitIds = (List) execution.getArgs().get("tf_slaveUnitIds"); if (helpUnitIds != null && !helpUnitIds.isEmpty()) { - Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_LEADER); - for (String helpUnitId : helpUnitIds) { ProposalUndertake undertake = dao.fetch(ProposalUndertake.class, helpUnitId); - List sysUserRoles = dao.query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()).and(Sys_user_role::getUnderTakeId, "=", undertake.getId())); - if (sysUserRoles.isEmpty()) { + List unitManagerUserIds = proposalCommonService.getUndertakeUnitManagerUserIds(undertake.getId()); + if (unitManagerUserIds.isEmpty()) { throw new BaseException(undertake.getName() + "单位没有设置负责人!"); } - assignee.addAll(sysUserRoles.stream().map(Sys_user_role::getUserId).toList()); + assignee.addAll(unitManagerUserIds); } return assignee; } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalUnitAssignmentHandler.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalUnitAssignmentHandler.java index e8374f9..9464a87 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalUnitAssignmentHandler.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/handler/ProposalUnitAssignmentHandler.java @@ -16,6 +16,7 @@ import com.budwk.app.sys.models.Sys_user_role; import com.budwk.app.sys.services.SysRoleService; import com.budwk.app.zhgh.democratic.proposal.models.ProposalReplyUnit; import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake; +import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; @@ -34,7 +35,7 @@ public class ProposalUnitAssignmentHandler implements AssignmentHandler { @Aop(TransAop.READ_COMMITTED) public List assign(TaskModel model, Execution execution) { Dao dao = ServiceContext.find(Dao.class); - SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class); + ProposalCommonService proposalCommonService = ServiceContext.find(ProposalCommonService.class); String proposalId = execution.getProcessInstance().getBusinessNo(); List unitIds = resolveUnitIds(model, execution, dao, proposalId); @@ -43,11 +44,6 @@ public class ProposalUnitAssignmentHandler implements AssignmentHandler { throw new BaseException("提案没有配置承办单位"); } - Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_LEADER); - if (sysRole == null) { - throw new BaseException("提案承办单位领导角色不存在"); - } - List assignee = new ArrayList<>(); for (String unitId : unitIds) { ProposalUndertake undertake = dao.fetch(ProposalUndertake.class, unitId); @@ -56,10 +52,8 @@ public class ProposalUnitAssignmentHandler implements AssignmentHandler { } // 只要存在单位 ID,就必须找到该单位负责人,否则流程无法继续往下分派。 - List sysUserRoles = dao.query(Sys_user_role.class, - Cnd.where(Sys_user_role::getRoleId, "=", sysRole.getId()) - .and(Sys_user_role::getUnderTakeId, "=", undertake.getId())); - if (ObjectUtil.isEmpty(sysUserRoles)) { + List unitManagerUserIds = proposalCommonService.getUndertakeUnitManagerUserIds(undertake.getId()); + if (ObjectUtil.isEmpty(unitManagerUserIds)) { throw new BaseException(undertake.getName() + "单位没有设置负责人!"); } /* @@ -67,7 +61,7 @@ public class ProposalUnitAssignmentHandler implements AssignmentHandler { * 同一个用户若同时负责主办、协办多个承办单位,不能在这里跨单位去重; * 否则流程会把多个承办单位合并成一个会签实例,导致“全部承办单位答复完”判断失真。 */ - assignee.addAll(sysUserRoles.stream().map(Sys_user_role::getUserId).toList()); + assignee.addAll(unitManagerUserIds); } return assignee; } @@ -177,7 +171,7 @@ public class ProposalUnitAssignmentHandler implements AssignmentHandler { @Override public String getMessage() { - return "提案单位处理人"; + return "承办单位负责人"; } @Override diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalControlService.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalControlService.java new file mode 100644 index 0000000..e957c74 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/ProposalControlService.java @@ -0,0 +1,30 @@ +package com.budwk.app.zhgh.democratic.proposal.service; + +import org.nutz.lang.util.NutMap; + +import java.util.List; + +/** + * 提案流程控制服务。 + * 状态调整必须按流程实例整体处理,避免并行承办单位待办被拆分为多个互相独立的跳转操作。 + */ +public interface ProposalControlService { + + /** + * 查询当前流程实例可回退的历史节点。 + * 节点顺序依据教代会提案流程定义,不依赖迁移任务的插入 ID。 + * + * @param instanceId 流程实例 ID + * @return 可跳转节点列表 + */ + List listJumpNodes(Long instanceId); + + /** + * 将整个提案流程实例跳转到指定历史节点。 + * 会先废弃全部当前待办,再创建一个目标节点待办;已完成审核历史保留不动。 + * + * @param instanceId 流程实例 ID + * @param targetTaskName 目标任务节点编码 + */ + void jump(Long instanceId, String targetTaskName); +} 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 dd69cca..f715e19 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 @@ -57,6 +57,38 @@ public interface ProposalCommonService extends BaseService { */ List getProposalUnionRoleUserIds(ProposalInfo proposalInfo, String... roleCodes); + /** + * 按承办单位编码关联系统单位,查询该系统单位已设置的单位负责人。 + * + * @param undertakeId 提案承办单位 ID + * @return 可参与提案流程的单位负责人用户 ID + */ + List getUndertakeUnitManagerUserIds(String undertakeId); + + /** + * 获取承办单位的一个单位负责人登录名,供旧 BPM 指派接口使用。 + * + * @param undertakeId 提案承办单位 ID + * @return 单位负责人登录名;未设置时返回空字符串 + */ + String getUndertakeUnitManagerLoginName(String undertakeId); + + /** + * 查询用户作为系统单位负责人的对应承办单位范围。 + * + * @param userId 用户 ID + * @return 可管理的承办单位 ID + */ + List getUserManagedUndertakeIds(String userId); + + /** + * 查询提案主办单位已设置的分管校领导。 + * + * @param proposalId 提案 ID + * @return 主办单位分管校领导用户 ID;多个主办单位或多人时合并去重 + */ + List getMasterUndertakeSchoolLeaderUserIds(String proposalId); + /** * 按白名单字段对已汇总的统计结果进行后端排序。 * 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 f079185..0ac1cb8 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 @@ -30,6 +30,7 @@ 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; @@ -123,8 +124,14 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp return false; } + // H5 列表首次加载不会传排序字段;Map.of 实现不接受空键,需在查询白名单前返回。 + String orderName = pageForm.getPageOrderName(); + if (StrUtil.isBlank(orderName)) { + return false; + } + // 仅允许白名单中的字段参与排序,防止构造任意SQL排序字段。 - String orderColumn = allowedOrderColumns.get(pageForm.getPageOrderName()); + String orderColumn = allowedOrderColumns.get(orderName); String orderBy = PageUtil.getOrder(pageForm.getPageOrderBy()); if (StrUtil.isBlank(orderColumn) || StrUtil.isBlank(orderBy)) { return false; @@ -251,6 +258,107 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp .toList(); } + /** + * 提案承办单位和系统单位通过单位编码一一关联;单位负责人以系统单位角色为唯一来源。 + * 历史 PROPOSAL_UNIT_LEADER 数据保留用于审计,但不再参与新流程取人。 + */ + @Override + public List getUndertakeUnitManagerUserIds(String undertakeId) { + ProposalUndertake undertake = dao().fetch(ProposalUndertake.class, undertakeId); + if (undertake == null || StrUtil.isBlank(undertake.getCode())) { + return List.of(); + } + Sys_unit unit = dao().fetch(Sys_unit.class, Cnd.where(Sys_unit::getUnitcode, "=", undertake.getCode())); + Sys_role role = sysRoleService.getByCode("UNIT_MANAGER"); + if (unit == null || role == null) { + return List.of(); + } + return dao().query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", role.getId()) + .and(Sys_user_role::getUnitId, "=", unit.getId())) + .stream() + .map(Sys_user_role::getUserId) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + } + + @Override + public String getUndertakeUnitManagerLoginName(String undertakeId) { + for (String userId : getUndertakeUnitManagerUserIds(undertakeId)) { + Sys_user user = dao().fetch(Sys_user.class, userId); + if (user != null && StrUtil.isNotBlank(user.getLoginname())) { + return user.getLoginname(); + } + } + return ""; + } + + @Override + public List getUserManagedUndertakeIds(String userId) { + Sys_role role = sysRoleService.getByCode("UNIT_MANAGER"); + if (role == null || StrUtil.isBlank(userId)) { + return List.of(); + } + List unitIds = dao().query(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", role.getId()) + .and(Sys_user_role::getUserId, "=", userId)) + .stream() + .map(Sys_user_role::getUnitId) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + if (unitIds.isEmpty()) { + return List.of(); + } + List unitCodes = dao().query(Sys_unit.class, Cnd.where(Sys_unit::getId, "in", unitIds)) + .stream() + .map(Sys_unit::getUnitcode) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + if (unitCodes.isEmpty()) { + return List.of(); + } + return dao().query(ProposalUndertake.class, Cnd.where(ProposalUndertake::getCode, "in", unitCodes)) + .stream() + .map(ProposalUndertake::getId) + .toList(); + } + + /** + * 校领导审批仅处理主办单位的分管校领导。角色关联以承办单位 ID 为范围, + * 避免把其他承办单位配置的校领导加入当前提案的待办。 + */ + @Override + public List getMasterUndertakeSchoolLeaderUserIds(String proposalId) { + if (StrUtil.isBlank(proposalId)) { + return List.of(); + } + Sys_role role = sysRoleService.getByCode(RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER); + if (role == null) { + return List.of(); + } + List masterUndertakeIds = dao().query(ProposalReplyUnit.class, + Cnd.where(ProposalReplyUnit::getProposalId, "=", proposalId) + .and(ProposalReplyUnit::getIsMaster, "=", true)) + .stream() + .map(ProposalReplyUnit::getUnitId) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + if (masterUndertakeIds.isEmpty()) { + return List.of(); + } + return dao().query(Sys_user_role.class, + Cnd.where(Sys_user_role::getRoleId, "=", role.getId()) + .and(Sys_user_role::getUnderTakeId, "in", masterUndertakeIds)) + .stream() + .map(Sys_user_role::getUserId) + .filter(StrUtil::isNotBlank) + .distinct() + .filter(this::isEnabledUser) + .toList(); + } + /** 校验流程参与人仍是可用系统用户。 */ private boolean isEnabledUser(String userId) { Sys_user user = dao().fetch(Sys_user.class, userId); @@ -1039,13 +1147,11 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp @Override public ProposalUndertake getSelfManageUndertake() { - Sys_role role = sysRoleService.getByCode(RoleConstant.PROPOSAL_UNIT_LEADER.name()); - Sys_user_role userRole = dao().fetch(Sys_user_role.class, Cnd.where(Sys_user_role::getRoleId, "=", role.getId()) - .and(Sys_user_role::getUserId, "=", SecurityUtil.getUserId())); - if (Lang.isEmpty(userRole)) { - throw new RuntimeException("当前登录用户不是" + role.getName()); + List undertakeIds = getUserManagedUndertakeIds(SecurityUtil.getUserId()); + if (undertakeIds.isEmpty()) { + throw new RuntimeException("当前登录用户不是单位负责人"); } - return dao().fetch(ProposalUndertake.class, Cnd.where(ProposalUndertake::getId, "=", userRole.getUnderTakeId())); + return dao().fetch(ProposalUndertake.class, undertakeIds.get(0)); } @Override diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalCommitteeFilingUnitServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalCommitteeFilingUnitServiceImpl.java index da91582..253c465 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalCommitteeFilingUnitServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalCommitteeFilingUnitServiceImpl.java @@ -42,6 +42,7 @@ import org.nutz.ioc.loader.annotation.IocBean; import java.util.ArrayList; import java.util.Collection; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -82,6 +83,13 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl proposalIds, Dict args) { String caseFilingResult = args.getStr(FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingResult"); @@ -113,8 +120,10 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl slaveUnitIds = getUnitIdList(args, FlowConst.TASK_FORM_DATA_PREFIX + "slaveUnitIds"); - List allUnitIds = new ArrayList<>(); - allUnitIds.addAll(masterUnitIds); - allUnitIds.addAll(slaveUnitIds); - if (allUnitIds.isEmpty()) { + Map selectedUnitRoleMap = new LinkedHashMap<>(); + masterUnitIds.forEach(unitId -> selectedUnitRoleMap.put(unitId, true)); + // 同一单位同时被选为主协办时,以主办身份为准,避免重复创建承办单位记录。 + slaveUnitIds.forEach(unitId -> selectedUnitRoleMap.putIfAbsent(unitId, false)); + if (selectedUnitRoleMap.isEmpty()) { + dao().clear(ProposalReplyUnit.class, Cnd.where(ProposalReplyUnit::getProposalId, "in", proposalIds)); return; } - Map undertakeMap = dao().query(ProposalUndertake.class, Cnd.where(ProposalUndertake::getId, "in", allUnitIds)) + List selectedUnitIds = new ArrayList<>(selectedUnitRoleMap.keySet()); + Map undertakeMap = dao().query(ProposalUndertake.class, Cnd.where(ProposalUndertake::getId, "in", selectedUnitIds)) .stream() .collect(Collectors.toMap(ProposalUndertake::getId, v -> v)); - List replyUnits = new ArrayList<>(); - for (String proposalId : proposalIds) { - replyUnits.addAll(buildReplyUnits(proposalId, masterUnitIds, undertakeMap, true)); - replyUnits.addAll(buildReplyUnits(proposalId, slaveUnitIds, undertakeMap, false)); + List existingReplyUnits = dao().query(ProposalReplyUnit.class, + Cnd.where(ProposalReplyUnit::getProposalId, "in", proposalIds)); + Map existingUnitMap = new LinkedHashMap<>(); + for (ProposalReplyUnit replyUnit : existingReplyUnits) { + existingUnitMap.putIfAbsent(replyUnit.getProposalId() + "_" + replyUnit.getUnitId(), replyUnit); } - if (!replyUnits.isEmpty()) { - dao().insert(replyUnits); + + List newReplyUnits = new ArrayList<>(); + for (String proposalId : proposalIds) { + for (Map.Entry selectedUnit : selectedUnitRoleMap.entrySet()) { + ProposalUndertake undertake = undertakeMap.get(selectedUnit.getKey()); + if (undertake == null) { + continue; + } + ProposalReplyUnit existingReplyUnit = existingUnitMap.get(proposalId + "_" + selectedUnit.getKey()); + if (existingReplyUnit == null) { + ProposalReplyUnit replyUnit = new ProposalReplyUnit(); + replyUnit.setProposalId(proposalId); + replyUnit.setUnitId(undertake.getId()); + replyUnit.setUnitCode(undertake.getCode()); + replyUnit.setUnitName(undertake.getName()); + replyUnit.setIsMaster(selectedUnit.getValue()); + newReplyUnits.add(replyUnit); + continue; + } + // 保留既有记录的 ID、是否答复和审计字段,只同步本次重新确认的单位信息与主协办身份。 + existingReplyUnit.setUnitCode(undertake.getCode()); + existingReplyUnit.setUnitName(undertake.getName()); + existingReplyUnit.setIsMaster(selectedUnit.getValue()); + dao().update(existingReplyUnit, "unitCode|unitName|isMaster"); + } + } + + // 本次未再选中的单位不再属于当前承办范围,删除其配置;其已完成流程任务历史仍保留可查看。 + dao().clear(ProposalReplyUnit.class, Cnd.where(ProposalReplyUnit::getProposalId, "in", proposalIds) + .and(ProposalReplyUnit::getUnitId, "not in", selectedUnitIds)); + if (!newReplyUnits.isEmpty()) { + dao().insert(newReplyUnits); } } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalControlServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalControlServiceImpl.java new file mode 100644 index 0000000..4a64abd --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalControlServiceImpl.java @@ -0,0 +1,154 @@ +package com.budwk.app.zhgh.democratic.proposal.service.impl; + +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.exception.BaseException; +import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.flow.constant.FlowConst; +import com.budwk.app.flow.entity.ProcessInstance; +import com.budwk.app.flow.entity.ProcessTask; +import com.budwk.app.flow.enums.ProcessTaskPerformTypeEnum; +import com.budwk.app.flow.enums.ProcessTaskStateEnum; +import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; +import com.budwk.app.flow.service.FlowCommonService; +import com.budwk.app.flow.service.ProcessTaskService; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; +import com.budwk.app.zhgh.democratic.proposal.service.ProposalControlService; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; + +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 教代会提案流程状态调整实现。 + * 迁移任务的数据库 ID 不代表真实流程顺序,统一按 JDHTA_NC 的节点顺序处理。 + */ +@IocBean(args = {"refer:dao"}) +public class ProposalControlServiceImpl extends BaseServiceImpl implements ProposalControlService { + + /** JDHTA_NC 流程节点的稳定业务顺序。 */ + private static final Map TASK_ORDERS = Map.ofEntries( + Map.entry("startTask", 10), + Map.entry("invite", 20), + Map.entry("second", 30), + Map.entry("secondaryUnionAudit", 40), + Map.entry("partyOrganizationAudit", 50), + Map.entry("committee", 60), + Map.entry("committeeFiling", 60), + Map.entry("committeeFilingUnit", 70), + Map.entry("unit_reply", 80), + Map.entry("unitReply", 80), + Map.entry("schoolLeader", 90), + Map.entry("feedback", 100), + Map.entry("feedbackEvaluation", 100), + Map.entry("caseCheck", 110) + ); + + @Inject + private ProcessTaskService processTaskService; + @Inject + private FlowCommonService flowCommonService; + + public ProposalControlServiceImpl(Dao dao) { + super(dao); + } + + @Override + public List listJumpNodes(Long instanceId) { + List doingTasks = getDoingTasks(instanceId); + int currentOrder = getCurrentOrder(doingTasks); + + List doneTasks = dao().query(ProcessTask.class, + Cnd.where(ProcessTask::getProcessInstanceId, "=", instanceId) + .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode()) + .asc(ProcessTask::getFinishTime) + .asc(ProcessTask::getId)); + Map nodes = new LinkedHashMap<>(); + for (ProcessTask task : doneTasks) { + Integer taskOrder = TASK_ORDERS.get(task.getTaskName()); + if (taskOrder == null || taskOrder >= currentOrder + || ProcessTaskPerformTypeEnum.COUNTERSIGN.getCode().equals(task.getPerformType())) { + continue; + } + nodes.putIfAbsent(task.getTaskName(), NutMap.NEW() + .setv("value", task.getTaskName()) + .setv("label", task.getDisplayName()) + .setv("seq", taskOrder)); + } + return nodes.values().stream() + .sorted(Comparator.comparingInt(node -> node.getInt("seq"))) + .toList(); + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public void jump(Long instanceId, String targetTaskName) { + if (StrUtil.isBlank(targetTaskName)) { + throw new BaseException("请选择目标节点"); + } + List doingTasks = getDoingTasks(instanceId); + int currentOrder = getCurrentOrder(doingTasks); + Integer targetOrder = TASK_ORDERS.get(targetTaskName); + if (targetOrder == null || targetOrder >= currentOrder) { + throw new BaseException("只能跳转到当前节点之前的流程节点"); + } + if (listJumpNodes(instanceId).stream().noneMatch(node -> targetTaskName.equals(node.getString("value")))) { + throw new BaseException("目标节点没有可用的历史审核记录"); + } + + ProcessTask sourceTask = doingTasks.get(0); + String operator = SecurityUtil.getUserId(); + if (!processTaskService.isAllowed(sourceTask, operator)) { + throw new BaseException("当前用户不能调整该流程"); + } + + Dict args = Dict.create(); + args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.JUMP.getCode()); + args.set(FlowConst.TASK_NAME, targetTaskName); + args.set("tf_sourceTaskKey", sourceTask.getTaskName()); + args.set("tf_audit", true); + + // 除执行入口外的并行待办必须先废弃,避免跳转后仍遗留承办单位答复等旧待办。 + for (int index = 1; index < doingTasks.size(); index++) { + processTaskService.abandonProcessTask(doingTasks.get(index).getId(), operator, args); + } + flowCommonService.executeTask(args.set(FlowConst.PROCESS_TASK_ID_KEY, sourceTask.getId())); + } + + /** 查询并校验流程实例的当前待办任务。 */ + private List getDoingTasks(Long instanceId) { + if (instanceId == null) { + throw new BaseException("流程实例不能为空"); + } + List doingTasks = dao().query(ProcessTask.class, + Cnd.where(ProcessTask::getProcessInstanceId, "=", instanceId) + .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.DOING.getCode()) + .asc(ProcessTask::getId)); + if (doingTasks.isEmpty()) { + throw new BaseException("流程没有进行中的待办"); + } + return doingTasks; + } + + /** 同一实例只允许存在同一业务节点的并行待办,防止异常混合状态被错误跳转。 */ + private int getCurrentOrder(List doingTasks) { + Set taskNames = doingTasks.stream().map(ProcessTask::getTaskName).collect(java.util.stream.Collectors.toSet()); + if (taskNames.size() != 1) { + throw new BaseException("流程存在多个不同节点的待办,请先处理异常待办"); + } + Integer taskOrder = TASK_ORDERS.get(doingTasks.get(0).getTaskName()); + if (taskOrder == null) { + throw new BaseException("当前节点不支持流程跳转"); + } + return taskOrder; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalFeedbackEvaluationServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalFeedbackEvaluationServiceImpl.java index a38a86d..3fa39c3 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalFeedbackEvaluationServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalFeedbackEvaluationServiceImpl.java @@ -18,6 +18,7 @@ import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake; import com.budwk.app.zhgh.democratic.proposal.param.ProposalFeedbackEvaluationParam; import com.budwk.app.zhgh.democratic.proposal.service.ProposalFeedbackEvaluationService; +import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.ioc.loader.annotation.Inject; @@ -34,6 +35,8 @@ public class ProposalFeedbackEvaluationServiceImpl extends BaseServiceImpl @@ -36,7 +36,10 @@ const UNIT_LEADER_MANAGE_TEMPLATE = { - + + + + - - + + diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/config/unit/user.js b/src/main/resources/views/platform/zhgh/democratic/proposal/config/unit/user.js index 730400b..3dd9bef 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/config/unit/user.js +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/config/unit/user.js @@ -1,10 +1,15 @@ const USER_COMPONENT = { template: ` - + + + + 搜索 + 共{{leaderUserTotalCount}}人,默认展示前50人 + { - this.selectUserIds = res.data.selectUserIds - this.allUsers = res.data.allUsers + .post("/platform/proposal/config/unit/branchSchoolLeaderUserPageData", { + id: this.unitId, + keyword: this.leaderUserKeyword }) - this.$refs.transferRef.clearQuery("left") - this.$refs.transferRef.clearQuery("right") + .then((res) => { + this.$set(this, "leaderUserTotalCount", res.code === 0 ? res.data.totalCount : 0) + this.applyTransferData(res) + }) + }, + + loadUnitManagerUsers() { + this.$axios + .post("/platform/proposal/config/unit/unitUsersTransferData", { id: this.unitId }) + .then((res) => { + this.$set(this, "leaderUserTotalCount", 0) + this.applyTransferData(res) + }) + }, + + applyTransferData(res) { + if (res.code !== 0) { + return + } + // 穿梭框必须先拿到候选人员,再设置已选人员,已选项才能稳定回显。 + this.$set(this, "allUsers", res.data.allUsers || []) + this.$set(this, "selectUserIds", res.data.selectUserIds || []) + this.$nextTick(() => { + if (this.$refs.transferRef) { + this.$refs.transferRef.clearQuery("left") + this.$refs.transferRef.clearQuery("right") + } + }) }, doSubmit() { diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/export/comprehensive/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/export/comprehensive/index.html index d758245..b268064 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/export/comprehensive/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/export/comprehensive/index.html @@ -206,7 +206,8 @@ layout("/layouts/platform.html"){ }, tableColumns: [ {label: "提案编号", prop: "code"}, - {label: "立案编号", prop: "caseFilingCode"}, + // 按当前业务要求屏蔽立案编号,避免列表和自定义导出带出该字段。 + // {label: "立案编号", prop: "caseFilingCode"}, {label: "提案名称", prop: "name"}, {label: "提案人", prop: "createUserName"}, {label: "提案类别", prop: "typeName"}, diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/control/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/control/index.html index 58517fc..304173d 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/control/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/control/index.html @@ -114,18 +114,15 @@ layout("/layouts/platform.html"){ }, openJumpDialog(row) { - this.jumpCurrentRow = row - this.jumpForm = { + this.$set(this, "jumpCurrentRow", row) + this.$set(this, "jumpForm", { targetTaskName: "", tf_audit: true - } - this.jumpDialogVisible = true + }) + this.$set(this, "jumpDialogVisible", true) this.$axios.post('/platform/proposal/control/jumpNodes', {instanceId: row.instanceId}).then(res => { if (res.code === 0) { - const nodeList = res.data || [] - const currentNode = nodeList.find(item => item.value === row.taskKey) - const currentSeq = currentNode ? Number(currentNode.seq) : Number.MAX_SAFE_INTEGER - this.jumpNodeOptions = nodeList.filter(item => item.value !== row.taskKey && Number(item.seq) < currentSeq) + this.$set(this, "jumpNodeOptions", res.data || []) } }) }, @@ -139,15 +136,10 @@ layout("/layouts/platform.html"){ type: "warning" }).then(() => { const params = { - processTaskId: this.jumpCurrentRow.taskId, - submitType: 4, - taskName: this.jumpForm.targetTaskName, - tf_sourceTaskKey: this.jumpCurrentRow.taskKey, - tf_audit: true + instanceId: this.jumpCurrentRow.instanceId, + targetTaskName: this.jumpForm.targetTaskName } - this.$axios.post("/flow/common/executeTask", { - data: JSON.stringify(params) - }).then((res) => { + this.$axios.post("/platform/proposal/control/jump", params).then((res) => { if (res.code === 0) { this.jumpDialogVisible = false this.$message.success(res.msg) diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/mine/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/mine/index.html index e619064..178213e 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/mine/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/mine/index.html @@ -23,9 +23,11 @@ layout("/layouts/platform.html"){ - 导入提案 - + @@ -76,8 +78,8 @@ layout("/layouts/platform.html"){ 撤回 - - + 删除 diff --git a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/seconded/index.html b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/seconded/index.html index f55200b..b0eeb58 100644 --- a/src/main/resources/views/platform/zhgh/democratic/proposal/transact/seconded/index.html +++ b/src/main/resources/views/platform/zhgh/democratic/proposal/transact/seconded/index.html @@ -82,7 +82,7 @@ layout("/layouts/platform.html"){ - 返回返回 + 返回 不同意 同意 diff --git a/src/main/resources/views/platform/zhghh5/democratic/proposal/common/info.js b/src/main/resources/views/platform/zhghh5/democratic/proposal/common/info.js index cb32181..9ac46a4 100644 --- a/src/main/resources/views/platform/zhghh5/democratic/proposal/common/info.js +++ b/src/main/resources/views/platform/zhghh5/democratic/proposal/common/info.js @@ -241,7 +241,36 @@ const PROPOSAL_INFO = { getDoneTasks() { this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => { if (res.code === 0) { - this.doneTasks = res.data + const tasks = res.data || [] + // 按提案业务流程展示历史记录,避免迁移或并行任务受数据库写入顺序影响。 + const taskOrders = { + startTask: 10, + invite: 20, + second: 30, + secondaryUnionAudit: 40, + partyOrganizationAudit: 50, + committee: 60, + committeeFiling: 60, + committeeFilingUnit: 70, + unit_reply: 80, + unitReply: 80, + schoolLeader: 90, + feedback: 100, + feedbackEvaluation: 100 + } + tasks.sort((first, second) => { + const orderDifference = (taskOrders[first.taskName] || 999) - (taskOrders[second.taskName] || 999) + if (orderDifference !== 0) { + return orderDifference + } + const firstTime = first.finishTime || '' + const secondTime = second.finishTime || '' + if (firstTime !== secondTime) { + return firstTime.localeCompare(secondTime) + } + return first.id - second.id + }) + this.$set(this, "doneTasks", tasks) } }) }, diff --git a/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/committeeFiling/index.html b/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/committeeFiling/index.html index 1f53701..bfbaba4 100644 --- a/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/committeeFiling/index.html +++ b/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/committeeFiling/index.html @@ -32,6 +32,9 @@ layout("/layouts/platform_h5.html"){
立案
+
+ 撤回 +
@@ -50,10 +53,11 @@ layout("/layouts/platform_h5.html"){ :placeholder="masterUnitLabel === '办理单位' ? '请选择办理单位' : '请选择主办单位'" @click="openMasterUnitPicker" :rules="[{ required: showUndertakeUnit, message: '请选择承办单位' }]" required> - -
+ +
{{masterUnitLabel}}
- + + @@ -66,10 +70,11 @@ layout("/layouts/platform_h5.html"){ - -
+ +
协办单位
- + + @@ -109,7 +114,9 @@ layout("/layouts/platform_h5.html"){ showApprovalForm: false, showResultPicker: false, showMasterUnitPicker: false, - showSlaveUnitPicker: false + showSlaveUnitPicker: false, + masterUnitKeyword: "", + slaveUnitKeyword: "" } }, computed: { @@ -131,10 +138,14 @@ layout("/layouts/platform_h5.html"){ return this.getUnitNames(this.formData.tf_slaveUnitIds) }, availableMasterUnits() { - return this.underTakeOptions.filter((item) => !this.formData.tf_slaveUnitIds.includes(item.id)) + return this.underTakeOptions.filter((item) => { + return !this.formData.tf_slaveUnitIds.includes(item.id) && this.matchUnitKeyword(item.name, this.masterUnitKeyword) + }) }, availableSlaveUnits() { - return this.underTakeOptions.filter((item) => !this.formData.tf_masterUnitIds.includes(item.id)) + return this.underTakeOptions.filter((item) => { + return !this.formData.tf_masterUnitIds.includes(item.id) && this.matchUnitKeyword(item.name, this.slaveUnitKeyword) + }) } }, methods: { @@ -149,17 +160,31 @@ layout("/layouts/platform_h5.html"){ this.$set(this, "showResultPicker", false) }, openMasterUnitPicker() { + this.$set(this, "masterUnitKeyword", "") this.$set(this, "showMasterUnitPicker", true) }, closeMasterUnitPicker() { + this.$set(this, "masterUnitKeyword", "") this.$set(this, "showMasterUnitPicker", false) }, openSlaveUnitPicker() { + this.$set(this, "slaveUnitKeyword", "") this.$set(this, "showSlaveUnitPicker", true) }, closeSlaveUnitPicker() { + this.$set(this, "slaveUnitKeyword", "") this.$set(this, "showSlaveUnitPicker", false) }, + // 选择弹层按单位名称实时筛选,不影响已选单位的值。 + matchUnitKeyword(unitName, keyword) { + return !keyword || (unitName || "").toLowerCase().includes(keyword.toLowerCase()) + }, + changeMasterUnitKeyword(value) { + this.$set(this, "masterUnitKeyword", value) + }, + changeSlaveUnitKeyword(value) { + this.$set(this, "slaveUnitKeyword", value) + }, listSession() { this.$axios.post("/platform/proposal/common/listSession").then((res) => { if (res.code === 0) { @@ -262,6 +287,19 @@ layout("/layouts/platform_h5.html"){ }).catch(function () {}) } catch (error) {} }, + onRevoke(row) { + this.$dialog.confirm({title: "提示", message: "您确定要撤回吗?"}).then(() => { + this.$axios.post("/platform/proposal/committeeFiling/h5/revokeTask", { + taskId: row.taskId, + proposalId: row.id + }).then((res) => { + if (res.code === 0) { + this.$toast.success(res.msg) + this.doSearch() + } + }) + }).catch(function () {}) + }, doSearch() { this.$nextTick(() => { this.$set(this.pageForm, "pageNumber", 1) diff --git a/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/committeeFilingUnit/index.html b/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/committeeFilingUnit/index.html index 30adb41..6c26b0b 100644 --- a/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/committeeFilingUnit/index.html +++ b/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/committeeFilingUnit/index.html @@ -32,6 +32,9 @@ layout("/layouts/platform_h5.html"){
确认
+
+ 撤回 +
@@ -50,10 +53,11 @@ layout("/layouts/platform_h5.html"){ :placeholder="masterUnitLabel === '办理单位' ? '请选择办理单位' : '请选择主办单位'" @click="openMasterUnitPicker" :rules="[{ required: showUndertakeUnit, message: '请选择承办单位' }]" required> - -
+ +
{{masterUnitLabel}}
- + + @@ -66,10 +70,11 @@ layout("/layouts/platform_h5.html"){ - -
+ +
协办单位
- + + @@ -109,7 +114,9 @@ layout("/layouts/platform_h5.html"){ showApprovalForm: false, showResultPicker: false, showMasterUnitPicker: false, - showSlaveUnitPicker: false + showSlaveUnitPicker: false, + masterUnitKeyword: "", + slaveUnitKeyword: "" } }, computed: { @@ -131,10 +138,14 @@ layout("/layouts/platform_h5.html"){ return this.getUnitNames(this.formData.tf_slaveUnitIds) }, availableMasterUnits() { - return this.underTakeOptions.filter((item) => !this.formData.tf_slaveUnitIds.includes(item.id)) + return this.underTakeOptions.filter((item) => { + return !this.formData.tf_slaveUnitIds.includes(item.id) && this.matchUnitKeyword(item.name, this.masterUnitKeyword) + }) }, availableSlaveUnits() { - return this.underTakeOptions.filter((item) => !this.formData.tf_masterUnitIds.includes(item.id)) + return this.underTakeOptions.filter((item) => { + return !this.formData.tf_masterUnitIds.includes(item.id) && this.matchUnitKeyword(item.name, this.slaveUnitKeyword) + }) } }, methods: { @@ -149,17 +160,31 @@ layout("/layouts/platform_h5.html"){ this.$set(this, "showResultPicker", false) }, openMasterUnitPicker() { + this.$set(this, "masterUnitKeyword", "") this.$set(this, "showMasterUnitPicker", true) }, closeMasterUnitPicker() { + this.$set(this, "masterUnitKeyword", "") this.$set(this, "showMasterUnitPicker", false) }, openSlaveUnitPicker() { + this.$set(this, "slaveUnitKeyword", "") this.$set(this, "showSlaveUnitPicker", true) }, closeSlaveUnitPicker() { + this.$set(this, "slaveUnitKeyword", "") this.$set(this, "showSlaveUnitPicker", false) }, + // 选择弹层按单位名称实时筛选,不影响已选单位的值。 + matchUnitKeyword(unitName, keyword) { + return !keyword || (unitName || "").toLowerCase().includes(keyword.toLowerCase()) + }, + changeMasterUnitKeyword(value) { + this.$set(this, "masterUnitKeyword", value) + }, + changeSlaveUnitKeyword(value) { + this.$set(this, "slaveUnitKeyword", value) + }, listSession() { this.$axios.post("/platform/proposal/common/listSession").then((res) => { if (res.code === 0) { @@ -298,6 +323,19 @@ layout("/layouts/platform_h5.html"){ }).catch(function () {}) } catch (error) {} }, + onRevoke(row) { + this.$dialog.confirm({title: "提示", message: "您确定要撤回吗?"}).then(() => { + this.$axios.post("/platform/proposal/committeeFilingUnit/h5/revokeTask", { + taskId: row.taskId, + proposalId: row.id + }).then((res) => { + if (res.code === 0) { + this.$toast.success(res.msg) + this.doSearch() + } + }) + }).catch(function () {}) + }, closeApproval() { this.$refs.proposalInfoRef.onClose() }, diff --git a/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/mine/index.html b/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/mine/index.html index 70c20db..e015feb 100644 --- a/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/mine/index.html +++ b/src/main/resources/views/platform/zhghh5/democratic/proposal/transact/mine/index.html @@ -18,7 +18,7 @@ layout("/layouts/platform_h5.html"){ -