diff --git a/src/main/java/com/budwk/app/sys/controller/SysUnionController.java b/src/main/java/com/budwk/app/sys/controller/SysUnionController.java index 7d09a7fa..60154bdb 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysUnionController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysUnionController.java @@ -7,6 +7,8 @@ import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.TreeNode; import cn.hutool.core.lang.tree.TreeUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.page.Pagination; @@ -263,7 +265,7 @@ public class SysUnionController { if (Lang.isEmpty(branchUnionRoles)) { return Result.success(); } - List branchUnionRoleCodes = branchUnionRoles.stream().map(Sys_dict::getCode).toList(); + List branchUnionRoleCodes = getBranchUnionRoleCodes(branchUnionRoles); Sql sql = Sqls.create(""" SELECT @@ -338,7 +340,7 @@ public class SysUnionController { if (Lang.isEmpty(branchUnionRoles)) { return Result.success(List.of()); } - List branchUnionRoleCodes = branchUnionRoles.stream().map(Sys_dict::getCode).toList(); + List branchUnionRoleCodes = getBranchUnionRoleCodes(branchUnionRoles); Sql sql = Sqls.create(""" SELECT DISTINCT @@ -371,6 +373,41 @@ public class SysUnionController { return Result.success(usedJCodes); } + /** + * 查询分工会干部角色选项,并补充按单位授权的二级党委书记角色。 + * + * @return 角色编码和名称 + */ + @At + @SaCheckPermission("sys.manager.union.branchOfficer") + public Result branchUnionRoleOptions() { + List roleOptions = sysDictService.getSubListByCode("BRANCH_UNION_ROLES").stream() + .map(item -> NutMap.NEW().addv("code", item.getCode()).addv("name", item.getName())) + .collect(Collectors.toCollection(ArrayList::new)); + if (sysRoleService.getByCode(RoleConstant.UNIT_PARTY_SECRETARY) != null + && roleOptions.stream().noneMatch(item -> RoleConstant.UNIT_PARTY_SECRETARY.name().equals(item.getString("code")))) { + roleOptions.add(NutMap.NEW().addv("code", RoleConstant.UNIT_PARTY_SECRETARY.name()).addv("name", "二级党委书记")); + } + return Result.success(roleOptions); + } + + /** + * 查询当前分工会可配置二级党委书记的组成单位。 + * + * @param unionId 分工会ID + * @return 当前分工会的二级单位 + */ + @At + @SaCheckPermission("sys.manager.union.branchOfficer") + public Result branchUnionPartySecretaryUnitOptions(String unionId) { + if (StrUtil.isBlank(unionId)) { + return Result.error("分工会参数不能为空"); + } + List units = dao.query(Sys_unit.class, Cnd.where("unionId", "=", unionId) + .and("unitLevel", "=", 2).asc("unitcode")); + return Result.success(units); + } + @At @SaCheckPermission("sys.manager.union.branchOfficer") @ApiOperation("添加分工会人员角色") @@ -444,6 +481,76 @@ public class SysUnionController { return Result.success(); } + /** + * 发起二级党委书记授权审批。一个审批申请可关联当前分工会下的多个单位。 + * 审核通过后由流程拦截器按单位写入角色,避免影响普通分工会角色的授权方式。 + * + * @param userId 人员ID + * @param unionId 分工会ID + * @param unitIds 组成单位ID集合 + * @param j 届次 + * @return 处理结果 + */ + @At + @SaCheckPermission("sys.manager.union.branchOfficer") + @ApiOperation("添加二级党委书记角色") + @Aop(TransAop.READ_COMMITTED) + public Result insertBranchUnionPartySecretaryRole(String userId, String unionId, @Param("unitIds") String[] unitIds, String j) { + if (StrUtil.isBlank(userId) || StrUtil.isBlank(unionId) || Lang.isEmpty(unitIds)) { + return Result.error("人员、分工会和所属单位不能为空"); + } + Sys_role role = sysRoleService.getByCode(RoleConstant.UNIT_PARTY_SECRETARY); + if (role == null) { + return Result.error("未配置二级党委书记角色"); + } + View_user user = dao.fetch(View_user.class, Cnd.where("id", "=", userId)); + if (user == null || !unionId.equals(user.getUnionId())) { + return Result.error("所选人员不属于当前分工会"); + } + List distinctUnitIds = java.util.Arrays.stream(unitIds) + .filter(StrUtil::isNotBlank) + .distinct() + .toList(); + List units = dao.query(Sys_unit.class, Cnd.where("id", "in", distinctUnitIds) + .and("unionId", "=", unionId).and("unitLevel", "=", 2)); + if (units.size() != distinctUnitIds.size()) { + return Result.error("所属单位包含非当前分工会的单位"); + } + int roleCount = dao.count(Sys_user_role.class, Cnd.where("roleId", "=", role.getId()) + .and("userId", "=", userId).and("unionId", "=", unionId)); + if (roleCount > 0) { + return Result.error("该人员已设置二级党委书记,请先删除后再重新设置"); + } + + Sys_union_cadre unionCadre = new Sys_union_cadre(); + unionCadre.setUserId(userId); + unionCadre.setUnionId(unionId); + unionCadre.setMobile(user.getMobile()); + unionCadre.setUnionName(user.getUnionName()); + unionCadre.setLoginName(user.getLoginname()); + unionCadre.setUserName(user.getUsername()); + unionCadre.setRoleCode(RoleConstant.UNIT_PARTY_SECRETARY.name()); + unionCadre.setJ(j); + unionCadre.setApplyDate(new Date()); + unionCadre.setIsServing(true); + unionCadre.setIsJoin(true); + dao.insert(unionCadre); + + JSONObject formData = JSONUtil.parseObj(unionCadre); + formData.set("unitIds", distinctUnitIds); + Dict args = Dict.create(); + boolean isAdmin = AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()); + args.set("submit", isAdmin ? "admin" : "branch"); + args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode()); + args.set(FlowConst.FORM_DATA, formData.toString()); + ProcessInstance instance = flowEngine.startProcessInstanceByKey("JCGHWY", unionCadre.getId(), SecurityUtil.getUserId(), args); + List doingTaskList = flowEngine.processTaskService().getDoingTaskList(instance.getId(), null); + for (ProcessTask task : doingTaskList) { + flowEngine.executeProcessTask(task.getId(), SecurityUtil.getUserId(), args); + } + return Result.success(); + } + @At @ApiOperation("分工会干部离任") @SaCheckPermission("sys.manager.union.branchOfficer") @@ -504,6 +611,18 @@ public class SysUnionController { return Result.success(); } + /** + * 将字典维护的分工会角色与单位级二级党委书记角色合并为列表查询范围。 + */ + private List getBranchUnionRoleCodes(List branchUnionRoles) { + List roleCodes = new ArrayList<>(branchUnionRoles.stream().map(Sys_dict::getCode).toList()); + if (sysRoleService.getByCode(RoleConstant.UNIT_PARTY_SECRETARY) != null + && !roleCodes.contains(RoleConstant.UNIT_PARTY_SECRETARY.name())) { + roleCodes.add(RoleConstant.UNIT_PARTY_SECRETARY.name()); + } + return roleCodes; + } + @At @ApiOperation("分工会组成单位分页") diff --git a/src/main/java/com/budwk/app/sys/interceptor/SysUnionSchoolAuditInterceptor.java b/src/main/java/com/budwk/app/sys/interceptor/SysUnionSchoolAuditInterceptor.java index 39de1e1f..560b6ac7 100644 --- a/src/main/java/com/budwk/app/sys/interceptor/SysUnionSchoolAuditInterceptor.java +++ b/src/main/java/com/budwk/app/sys/interceptor/SysUnionSchoolAuditInterceptor.java @@ -1,6 +1,10 @@ package com.budwk.app.sys.interceptor; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; +import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.flow.constant.FlowConst; import com.budwk.app.flow.engine.FlowInterceptor; import com.budwk.app.flow.engine.core.Execution; @@ -13,6 +17,9 @@ import com.budwk.app.sys.services.SysUserService; import org.nutz.dao.Chain; import org.nutz.dao.Cnd; +import java.util.ArrayList; +import java.util.List; + /** * @version 1.0 * @Author FKY @@ -35,14 +42,37 @@ public class SysUnionSchoolAuditInterceptor implements FlowInterceptor { SysUserService sysUserService = ServiceContext.find(SysUserService.class); // 表单数据 - Sys_union_cadre unionBean = JSONUtil.toBean(formDataStr, Sys_union_cadre.class); + JSONObject formData = JSONUtil.parseObj(formDataStr); + Sys_union_cadre unionBean = JSONUtil.toBean(formData, Sys_union_cadre.class); // 清除对应角色然后再新增 Sys_role role = sysRoleService.getByCode(unionBean.getRoleCode()); sysRoleService.clear("sys_user_role", Cnd.where("roleId", "=", role.getId()) .and("userId", "=", unionBean.getUserId()).and("unionId", "=", unionBean.getUnionId())); - sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId()) - .add("userId", unionBean.getUserId()).add("unionId", unionBean.getUnionId())); + if (RoleConstant.UNIT_PARTY_SECRETARY.name().equals(unionBean.getRoleCode())) { + // 二级党委书记按申请中选定的单位分别授权,保障流程可按申请人所属单位找到办理人。 + JSONArray unitIdArray = formData.getJSONArray("unitIds"); + List unitIds = new ArrayList<>(); + if (unitIdArray != null) { + for (Object unitId : unitIdArray) { + String value = StrUtil.toString(unitId); + if (StrUtil.isNotBlank(value) && !unitIds.contains(value)) { + unitIds.add(value); + } + } + } + if (unitIds.isEmpty()) { + throw new IllegalArgumentException("二级党委书记未选择所属单位"); + } + for (String unitId : unitIds) { + sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId()) + .add("userId", unionBean.getUserId()).add("unionId", unionBean.getUnionId()).add("unitId", unitId)); + } + } else { + // 其他分工会角色保持原有按分工会单条授权的逻辑。 + sysRoleService.insert("sys_user_role", Chain.make("roleId", role.getId()) + .add("userId", unionBean.getUserId()).add("unionId", unionBean.getUnionId())); + } // 清除缓存 sysRoleService.clearCache(); diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysRoleServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysRoleServiceImpl.java index b33dc62e..86e79a79 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysRoleServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysRoleServiceImpl.java @@ -148,6 +148,11 @@ public class SysRoleServiceImpl extends BaseServiceImpl implements Sys @Override @Aop(TransAop.READ_COMMITTED) public void saveMenu(String[] menuIds, String roleId, String platform) { + // 保存前确认权限均属于当前平台,防止PC端与H5端权限混写。 + List menus = sysMenuService.query(Cnd.where("id", "in", menuIds).and("platform", "=", platform)); + if (menus.size() != menuIds.length) { + throw new BaseException("存在不属于当前平台的权限,无法保存"); + } //只清除对应平台的即可 Sql sql = Sqls.queryString(""" SELECT diff --git a/src/main/java/com/budwk/app/zhgh/activity/sports/controller/ActivitySportsResultsController.java b/src/main/java/com/budwk/app/zhgh/activity/sports/controller/ActivitySportsResultsController.java index 91d193a4..edce0183 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/sports/controller/ActivitySportsResultsController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/sports/controller/ActivitySportsResultsController.java @@ -17,7 +17,7 @@ import com.budwk.app.zhgh.activity.sports.service.ActivitySportsApplyUserService import org.nutz.dao.Cnd; import org.nutz.dao.Sqls; import org.nutz.dao.sql.Sql; -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; import org.nutz.lang.random.R; @@ -26,8 +26,6 @@ import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import org.nutz.trans.Trans; -import java.util.Arrays; - /** * @author zhf * @date 2021/5/19 14:58 @@ -57,7 +55,8 @@ public class ActivitySportsResultsController { PageForm page, String activityId, String eventId, - String[] isMenWomen) { + Integer isMenWomen, + Integer projectType) { Cnd cnd = Cnd.NEW(); Sql sql = Sqls.create(""" SELECT @@ -79,22 +78,10 @@ public class ActivitySportsResultsController { cnd.and("ase.activityId", "=", activityId); cnd.andEX("abs.`name`", "=", groupName); cnd.andEX("ae.`id`", "=", eventId); - SqlExpressionGroup sqlExpressionGroup = new SqlExpressionGroup(); - if (Arrays.stream(isMenWomen).anyMatch(a -> a.equals("2"))) { - sqlExpressionGroup.and("ae.isMenWomen", "=", 1); - } - if (Arrays.stream(isMenWomen).anyMatch(a -> a.equals("3"))) { - sqlExpressionGroup.or("ae.isMenWomen", "=", 2); - } - if (Arrays.stream(isMenWomen).anyMatch(a -> a.equals("4"))) { - sqlExpressionGroup.and("ae.projectType", "=", 1); - } - if (Arrays.stream(isMenWomen).anyMatch(a -> a.equals("5"))) { - sqlExpressionGroup.or("ae.projectType", "=", 2); - } - if (sqlExpressionGroup.getExps().size() > 0) { - cnd.and(sqlExpressionGroup); - } + // 页面男子/女子选项值与活动项目性别字段保持一致:1 为男子,2 为女子。 + cnd.andEX("ae.isMenWomen", "=", isMenWomen); + // 页面项目类型选项值与活动项目类型字段保持一致:1 为单项,2 为团体。 + cnd.andEX("ae.projectType", "=", projectType); cnd.desc("allName"); sql.setCondition(cnd); return Result.success(baseService.listPageMap(page.getPageNumber(), page.getPageSize(), sql)); @@ -192,8 +179,9 @@ public class ActivitySportsResultsController { cnd.and("asa.activityId", "=", activityId); cnd.and("asa.eventId", "=", eventId); cnd.and("asa.awardsMode", "=", 1); - if (isMenWomen != null && isMenWomen == 1) cnd.and("u.sex", "=", "男性"); - if (isMenWomen != null && isMenWomen == 2) cnd.and("u.sex", "=", "女性"); + // 兼容历史人员“男性/女性”和临时添加人员“男/女”的性别值。 + if (isMenWomen != null && isMenWomen == 1) cnd.and(new Static("u.sex IN ('男', '男性')")); + if (isMenWomen != null && isMenWomen == 2) cnd.and(new Static("u.sex IN ('女', '女性')")); sql.setCondition(cnd); return Result.success(activitySchoolApplyViService.listMap(sql)); } @@ -268,8 +256,9 @@ public class ActivitySportsResultsController { """); cnd.and("ar.activityId", "=", activityId); cnd.and("ar.eventId", "=", eventId); - if (isMenWomen != null && isMenWomen == 1) cnd.and("u.sex", "=", "男性"); - if (isMenWomen != null && isMenWomen == 2) cnd.and("u.sex", "=", "女性"); + // 兼容历史人员“男性/女性”和临时添加人员“男/女”的性别值。 + if (isMenWomen != null && isMenWomen == 1) cnd.and(new Static("u.sex IN ('男', '男性')")); + if (isMenWomen != null && isMenWomen == 2) cnd.and(new Static("u.sex IN ('女', '女性')")); cnd.asc("ar.ranking"); sql.setCondition(cnd); return Result.success(baseService.listMap(sql)); diff --git a/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/ActivityWorksCollectionManageController.java b/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/ActivityWorksCollectionManageController.java index 399b6989..070ddec9 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/ActivityWorksCollectionManageController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/workscollection/controller/ActivityWorksCollectionManageController.java @@ -22,6 +22,7 @@ import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collect import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collection_upload; import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_subjectType; import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_worksType; +import com.budwk.app.zhgh.activity.workscollection.service.ActivityWorksCollectionService; import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Chain; import org.nutz.dao.Cnd; @@ -50,6 +51,8 @@ public class ActivityWorksCollectionManageController { @Inject private Dao dao; @Inject + private ActivityWorksCollectionService activityWorksCollectionService; + @Inject private BaseService baseService; @Inject private SysMsgService sysMsgService; @@ -202,6 +205,10 @@ public class ActivityWorksCollectionManageController { @SaCheckPermission(value = {"activity.workscollection.manage", "activity.workscollection.new"}, mode = SaMode.OR) @Aop(TransAop.READ_COMMITTED) public Result update(@Param("data") @Valid Activity_works_collection worksCollection) { + String validateMessage = activityWorksCollectionService.validateReferencedTypesBeforeUpdate(worksCollection); + if (StrUtil.isNotBlank(validateMessage)) { + return Result.error(validateMessage); + } dao.update(worksCollection); dao.updateLinks(worksCollection, "subjectTypes"); dao.insertLinks(worksCollection, "subjectTypes"); diff --git a/src/main/java/com/budwk/app/zhgh/activity/workscollection/service/ActivityWorksCollectionService.java b/src/main/java/com/budwk/app/zhgh/activity/workscollection/service/ActivityWorksCollectionService.java new file mode 100644 index 00000000..8d1983b0 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/workscollection/service/ActivityWorksCollectionService.java @@ -0,0 +1,17 @@ +package com.budwk.app.zhgh.activity.workscollection.service; + +import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collection; + +/** + * 作品征集活动配置服务。 + */ +public interface ActivityWorksCollectionService { + + /** + * 校验编辑活动时删除的主题类型、作品类型是否已被投稿引用。 + * + * @param worksCollection 前端提交的活动及其主题、作品类型配置 + * @return 校验通过返回 {@code null};校验不通过返回提示信息 + */ + String validateReferencedTypesBeforeUpdate(Activity_works_collection worksCollection); +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/workscollection/service/impl/ActivityWorksCollectionServiceImpl.java b/src/main/java/com/budwk/app/zhgh/activity/workscollection/service/impl/ActivityWorksCollectionServiceImpl.java new file mode 100644 index 00000000..99962208 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/activity/workscollection/service/impl/ActivityWorksCollectionServiceImpl.java @@ -0,0 +1,125 @@ +package com.budwk.app.zhgh.activity.workscollection.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collection; +import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_collection_upload; +import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_subjectType; +import com.budwk.app.zhgh.activity.workscollection.models.Activity_works_worksType; +import com.budwk.app.zhgh.activity.workscollection.service.ActivityWorksCollectionService; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 作品征集活动配置服务实现。 + */ +@IocBean(args = {"refer:dao"}) +public class ActivityWorksCollectionServiceImpl implements ActivityWorksCollectionService { + + private final Dao dao; + + public ActivityWorksCollectionServiceImpl(Dao dao) { + this.dao = dao; + } + + @Override + public String validateReferencedTypesBeforeUpdate(Activity_works_collection worksCollection) { + if (worksCollection == null || StrUtil.isBlank(worksCollection.getId())) { + return null; + } + List persistedSubjectTypes = dao.query(Activity_works_subjectType.class, + Cnd.where(Activity_works_subjectType::getActivityId, "=", worksCollection.getId())); + List submittedSubjectTypes = CollUtil.defaultIfEmpty(worksCollection.getSubjectTypes(), Collections.emptyList()); + Set submittedSubjectIds = submittedSubjectTypes.stream() + .map(Activity_works_subjectType::getId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + + List deletedSubjectTypes = persistedSubjectTypes.stream() + .filter(subjectType -> !submittedSubjectIds.contains(subjectType.getId())) + .toList(); + String subjectError = getReferencedSubjectTypeError(worksCollection.getId(), deletedSubjectTypes); + if (subjectError != null) { + return subjectError; + } + + for (Activity_works_subjectType submittedSubjectType : submittedSubjectTypes) { + if (submittedSubjectType == null || StrUtil.isBlank(submittedSubjectType.getId())) { + continue; + } + List persistedWorksTypes = dao.query(Activity_works_worksType.class, + Cnd.where(Activity_works_worksType::getSubjectId, "=", submittedSubjectType.getId())); + List submittedWorksTypes = CollUtil.defaultIfEmpty(submittedSubjectType.getWorksTypes(), Collections.emptyList()); + Set submittedWorksIds = submittedWorksTypes.stream() + .map(Activity_works_worksType::getId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + List deletedWorksTypes = persistedWorksTypes.stream() + .filter(worksType -> !submittedWorksIds.contains(worksType.getId())) + .toList(); + String worksError = getReferencedWorksTypeError(worksCollection.getId(), deletedWorksTypes); + if (worksError != null) { + return worksError; + } + } + return null; + } + + /** + * 校验待删除主题类型是否已有投稿,防止投稿的主题类型关联被清空。 + */ + private String getReferencedSubjectTypeError(String activityId, List deletedSubjectTypes) { + if (CollUtil.isEmpty(deletedSubjectTypes)) { + return null; + } + List deletedSubjectIds = deletedSubjectTypes.stream() + .map(Activity_works_subjectType::getId) + .collect(Collectors.toList()); + List uploads = dao.query(Activity_works_collection_upload.class, + Cnd.where(Activity_works_collection_upload::getActivityId, "=", activityId) + .and(Activity_works_collection_upload::getSubjectId, "in", deletedSubjectIds)); + if (CollUtil.isEmpty(uploads)) { + return null; + } + Set referencedSubjectIds = uploads.stream() + .map(Activity_works_collection_upload::getSubjectId) + .collect(Collectors.toSet()); + String typeNames = deletedSubjectTypes.stream() + .filter(subjectType -> referencedSubjectIds.contains(subjectType.getId())) + .map(Activity_works_subjectType::getTypeName) + .collect(Collectors.joining("、")); + return "主题类型“" + typeNames + "”下已有投稿,不能删除"; + } + + /** + * 校验待删除作品类型是否已有投稿,防止投稿的作品类型关联被清空。 + */ + private String getReferencedWorksTypeError(String activityId, List deletedWorksTypes) { + if (CollUtil.isEmpty(deletedWorksTypes)) { + return null; + } + List deletedWorksIds = deletedWorksTypes.stream() + .map(Activity_works_worksType::getId) + .collect(Collectors.toList()); + List uploads = dao.query(Activity_works_collection_upload.class, + Cnd.where(Activity_works_collection_upload::getActivityId, "=", activityId) + .and(Activity_works_collection_upload::getWorksId, "in", deletedWorksIds)); + if (CollUtil.isEmpty(uploads)) { + return null; + } + Set referencedWorksIds = uploads.stream() + .map(Activity_works_collection_upload::getWorksId) + .collect(Collectors.toSet()); + String typeNames = deletedWorksTypes.stream() + .filter(worksType -> referencedWorksIds.contains(worksType.getId())) + .map(Activity_works_worksType::getWorksTypeName) + .collect(Collectors.joining("、")); + return "作品类型“" + typeNames + "”下已有投稿,不能删除"; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubInfoManageController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubInfoManageController.java index dfe8b67d..d8889a44 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubInfoManageController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubInfoManageController.java @@ -200,6 +200,7 @@ public class ClubInfoManageController { @SLog(tag = "社团管理系统-信息管理", msg = "修改身份") public Result updateRoleCode(@Param("id") String id, @Valid String[] roleCodes, @Valid String clubId) { List roleCodeList = Arrays.asList(roleCodes); + ClubUser clubUser = clubInfoManageService.dao().fetch(ClubUser.class, id); //查询社团是否存在会长或者秘书长 if(Arrays.asList(roleCodes).contains(RoleConstant.CLUB_PRESIDENT.name())) { int count = dao.count(ClubUser.class, Cnd.where("clubId", "=", clubId) @@ -210,14 +211,14 @@ public class ClubInfoManageController { } if(Arrays.asList(roleCodes).contains(RoleConstant.CLUB_SECRETARY.name())) { int count = dao.count(ClubUser.class, Cnd.where("clubId", "=", clubId) + .and("id", "!=", id) .and(new Static("JSON_CONTAINS(roleCode, '\"%s\"')".formatted(RoleConstant.CLUB_SECRETARY.name())))); if (count > 0) { // return Result.error("秘书长只能有一位"); + return Result.error("秘书长只能有一位"); } } - ClubUser clubUser = clubInfoManageService.dao().fetch(ClubUser.class, id); - // 先清除所有的角色 dao.clear(Sys_user_role.class, Cnd.where("userId", "=", clubUser.getUserId()).and("clubId", "=", clubUser.getClubId())); // 再根据传过来的赋值 diff --git a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubExamineServiceImpl.java b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubExamineServiceImpl.java index e90aca78..9b2521b7 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubExamineServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubExamineServiceImpl.java @@ -291,7 +291,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl getJgUser(String clubId) { - // 构建社团管理人员查询SQL(排除普通成员,按职务排序) + // 构建社团成员查询SQL,理事机构成员优先展示,普通成员随后展示。 Sql sql = Sqls.create(""" SELECT cl.*, @@ -304,7 +304,6 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl detailedList = dao().query(SysClubExamineRegisterDetailed.class, Cnd.where("registerId", "=", id).asc("location")); clubExamineVo.setDetailedList(detailedList); + // V3年审审核记录已迁入audit表,作为V4工作流历史为空时的详情回显数据。 + Sql legacyAuditSql = Sqls.create(""" + SELECT + a.id, + a.username AS userName, + a.loginname AS loginName, + a.auditTime, + a.auditPass, + a.auditOpinion, + a.auditSign, + CASE + WHEN a.id = scer.clubAuditId THEN '社团审核' + WHEN a.id = scer.clubLeaderAuditId THEN '会长审核' + WHEN a.id = scer.schoolAuditId THEN '校工会审核' + END AS auditName + FROM + sys_club_examine_register scer + INNER JOIN audit a ON a.id IN (scer.clubAuditId, scer.clubLeaderAuditId, scer.schoolAuditId) + WHERE + scer.id = @id + ORDER BY + CASE + WHEN a.id = scer.clubAuditId THEN 1 + WHEN a.id = scer.clubLeaderAuditId THEN 2 + WHEN a.id = scer.schoolAuditId THEN 3 + ELSE 99 + END + """).setParam("id", id); + clubExamineVo.setLegacyAuditRecords(listMap(legacyAuditSql)); } return clubExamineVo; } diff --git a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java index c4ad1c2f..7a146145 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java @@ -86,7 +86,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl exitManagePageData(ClubUserPageForm pageForm) { Sql sql = Sqls.create(""" SELECT - COALESCE(info.id, scu.id) AS id, - scu.clubId, - scu.userId, - COALESCE(info.roleCode, scu.roleCode) AS applyRoleCode, - COALESCE(info.clubPosition, scu.position) AS clubPosition, - COALESCE(info.email, scu.email, u.email) AS email, - COALESCE(info.mobile, u.mobile) AS mobile, - COALESCE(info.birthday, u.birthday) AS birthday, - COALESCE(info.avatar, scu.avatar, u.avatar) AS avatar, - COALESCE(info.sameTimeJoinOtherClubSituation, scu.sameTimeJoinOtherClubSituation) AS sameTimeJoinOtherClubSituation, - COALESCE(info.awardsExperience, scu.awardsExperience) AS awardsExperience, - info.signature, - COALESCE( - info.applyDate, - STR_TO_DATE(scu.changeTime, '%Y-%m-%d %H:%i:%s'), - STR_TO_DATE(JSON_UNQUOTE(JSON_EXTRACT(scu.exitAuditList, '$[0].auditTime')), '%Y-%m-%d %H:%i:%s') - ) AS applyDate, + exitInfo.id, + exitInfo.clubId, + exitInfo.userId, + exitInfo.applyRoleCode, + exitInfo.clubPosition, + COALESCE(exitInfo.email, u.email) AS email, + COALESCE(exitInfo.mobile, u.mobile) AS mobile, + COALESCE(exitInfo.birthday, u.birthday) AS birthday, + COALESCE(exitInfo.avatar, u.avatar) AS avatar, + exitInfo.sameTimeJoinOtherClubSituation, + exitInfo.awardsExperience, + exitInfo.signature, + exitInfo.applyDate, u.username AS userName, u.loginname AS loginName, u.sex, @@ -253,32 +249,14 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl pagination = listPageVO(pageForm, sql, ClubUserCommonPageVo.class); @@ -307,55 +285,110 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl clubManagePersonAuditPageData(ClubUserPageForm pageForm) { Sql sql = Sqls.create(""" diff --git a/src/main/java/com/budwk/app/zhgh/club/vo/ClubExamineVo.java b/src/main/java/com/budwk/app/zhgh/club/vo/ClubExamineVo.java index ec2057b8..16a11dd9 100644 --- a/src/main/java/com/budwk/app/zhgh/club/vo/ClubExamineVo.java +++ b/src/main/java/com/budwk/app/zhgh/club/vo/ClubExamineVo.java @@ -6,6 +6,7 @@ import com.budwk.app.zhgh.club.model.SysClubExamineRegisterDetailed; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; +import org.nutz.lang.util.NutMap; import java.util.List; @@ -18,4 +19,9 @@ public class ClubExamineVo extends SysClubExamineRegister { private String typeName; @ApiModelProperty("节点审批记录") private List nodeTasks; + + /** + * V3年审已迁入但未转换为V4工作流任务的历史审核记录。 + */ + private List legacyAuditRecords; } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationApplyListController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationApplyListController.java index 58612fcc..827ffb36 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationApplyListController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationApplyListController.java @@ -84,6 +84,8 @@ public class EnrollmentRegistrationApplyListController { Cnd cnd = Cnd.NEW(); cnd.andEX("YEAR(info.applyTime)", "=", year); cnd.and("info.userId", "=", SecurityUtil.getUserId()); + // 同一登记可能关联多个流程任务参与人,按业务主表ID分组后仅展示一条申请记录。 + cnd.groupBy("info.id"); sql.setCondition(cnd); Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); return Result.success(pagination); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateActivityController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateActivityController.java index deb8a0ee..4b322b6e 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateActivityController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateActivityController.java @@ -74,6 +74,7 @@ public class EvaluateActivityController { Cnd cnd = Cnd.NEW(); cnd.andEX("year", "=", year); cnd.and(Cnd.likeEX("eva.name", searchKeyword)); + cnd.groupBy("eva.id"); sql.setCondition(cnd); return Result.success(baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql)); } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateApplyController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateApplyController.java index 4321534f..0be00358 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateApplyController.java @@ -83,6 +83,7 @@ public class EvaluateApplyController { Cnd cnd = Cnd.NEW(); cnd.and("enable", "=", 1); cnd.andEX("year", "=", year); + cnd.groupBy("eva.id"); sql.setCondition(cnd); Pagination pagination = evaluateActivityService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); return Result.success(pagination); @@ -108,6 +109,10 @@ public class EvaluateApplyController { @ApiOperation("保存") @SLog(tag = "评优评先申请", msg = "保存申请") public Result save(@Param("data") EvaluateApply evaluateApply) { + String validateMessage = evaluateService.validateCollectiveHonorApply(evaluateApply); + if (validateMessage != null) { + return Result.error(validateMessage); + } evaluateApply.setApplyDateTime(new Date()); evaluateService.insertOrUpdate(evaluateApply); return Result.success(); @@ -119,6 +124,10 @@ public class EvaluateApplyController { @Aop(TransAop.READ_COMMITTED) @SLog( tag = "评优评先申请", msg = "重新提交申请") public Result submitAgain(@Param("data") EvaluateApply evaluateApply, @Param("taskId") Long taskId) { + String validateMessage = evaluateService.validateCollectiveHonorApply(evaluateApply); + if (validateMessage != null) { + return Result.error(validateMessage); + } evaluateService.insertOrUpdate(evaluateApply); Dict dict = Dict.create(); @@ -134,6 +143,10 @@ public class EvaluateApplyController { @Aop(TransAop.READ_COMMITTED) @SLog(tag = "评优评先申请", msg = "提交申请") public Result submit(@Param("data") EvaluateApply evaluateApply) { + String validateMessage = evaluateService.validateCollectiveHonorApply(evaluateApply); + if (validateMessage != null) { + return Result.error(validateMessage); + } evaluateApply.setApplyDateTime(new Date()); evaluateService.insertOrUpdate(evaluateApply); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateMineController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateMineController.java index 2550ee8b..f640fbd4 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateMineController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/controller/EvaluateMineController.java @@ -80,6 +80,7 @@ public class EvaluateMineController { Cnd cnd = Cnd.NEW(); cnd.andEX("YEAR(info.year)", "=", year); cnd.and("info.loginName", "=", SecurityUtil.getUserLoginname()); + cnd.groupBy("info.id"); cnd.desc("info.createdAt"); sql.setCondition(cnd); Pagination pagination = evaluateService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/service/EvaluateService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/service/EvaluateService.java index 4f4a6336..026909fb 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/service/EvaluateService.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/service/EvaluateService.java @@ -10,4 +10,12 @@ public interface EvaluateService extends BaseService { Sql getSummarySql(EvaluatePageForm pageForm); + + /** + * 校验集体荣誉是否已由当前分工会申请。 + * + * @param evaluateApply 待保存或提交的申请 + * @return 校验通过返回 {@code null};重复申请时返回提示信息 + */ + String validateCollectiveHonorApply(EvaluateApply evaluateApply); } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/service/impl/EvaluateServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/service/impl/EvaluateServiceImpl.java index 87b4611a..59fcc01a 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/service/impl/EvaluateServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/evaluation/service/impl/EvaluateServiceImpl.java @@ -4,9 +4,12 @@ import cn.hutool.core.util.StrUtil; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.base.utils.PageUtil; import com.budwk.app.flow.enums.ProcessTaskStateEnum; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; +import com.budwk.app.zhgh.dayofficework.evaluation.models.EvaluateActivity; import com.budwk.app.zhgh.dayofficework.evaluation.models.EvaluateApply; import com.budwk.app.zhgh.dayofficework.evaluation.service.EvaluateService; import com.budwk.app.zhgh.dayofficework.evaluation.vo.EvaluatePageForm; +import com.budwk.app.zhgh.dayofficework.honor.models.HonorBasicSettings; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; @@ -67,6 +70,7 @@ public class EvaluateServiceImpl extends BaseServiceImpl implemen seg.orLike("info.loginName", pageForm.getSearchKeyword()); cnd.and(seg); } + cnd.groupBy("info.id"); if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { cnd.desc("t.createdAt").desc("info.applyDateTime"); } else { @@ -76,4 +80,30 @@ public class EvaluateServiceImpl extends BaseServiceImpl implemen sql.setCondition(cnd); return sql; } + + @Override + public String validateCollectiveHonorApply(EvaluateApply evaluateApply) { + if (evaluateApply == null || StrUtil.isBlank(evaluateApply.getEvaluateId())) { + return "评优评先活动不能为空"; + } + EvaluateActivity activity = dao().fetch(EvaluateActivity.class, evaluateApply.getEvaluateId()); + if (activity == null) { + return "评优评先活动不存在"; + } + HonorBasicSettings honorType = dao().fetch(HonorBasicSettings.class, activity.getHonorTypeId()); + if (honorType == null || !StrUtil.equals("集体荣誉", honorType.getName())) { + return null; + } + + Cnd cnd = Cnd.where(EvaluateApply::getUnionId, "=", SecurityUtil.getUnionId()) + .and(EvaluateApply::getHonorId, "=", activity.getHonorId()) + .and("YEAR(applyDateTime)", "=", activity.getYear()); + if (StrUtil.isNotBlank(evaluateApply.getId())) { + cnd.and(EvaluateApply::getId, "!=", evaluateApply.getId()); + } + if (dao().count(EvaluateApply.class, cnd) > 0) { + return "本分工会已申请该集体荣誉,同一类型只能申请一次"; + } + return null; + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java index 846ed2dd..57d5f74a 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java @@ -420,12 +420,32 @@ public class TourSignupController { a.agencyName AS travelAgencyName, IFNULL(lot.allowOverReimbursement, 0) AS allowOverReimbursement, s.allowFamily, - IFNULL(s.fillBedInfo, 1) AS fillBedInfo + IFNULL(s.fillBedInfo, 1) AS fillBedInfo, + IFNULL(sc.signupCount, 0) AS signupCount, + IFNULL(sc.familyCount, 0) AS familyCount FROM tour_matter m INNER JOIN tour_line l ON l.id = m.lineId LEFT JOIN tour_travel_agency a ON a.id = l.travelAgencyId LEFT JOIN tour_setting_lot lot ON lot.id = l.lotId LEFT JOIN tour_setting s ON s.id = m.settingId + LEFT JOIN ( + /* 与PC报名列表保持一致:已报人数包含教职工本人和有效家属。 */ + SELECT + t.matterId, + COUNT(1) + SUM(IFNULL(f.familyCount, 0)) AS signupCount, + SUM(IFNULL(f.familyCount, 0)) AS familyCount + FROM tour_ledger t + LEFT JOIN ( + SELECT ledgerId, COUNT(1) AS familyCount + FROM tour_ledger_family + WHERE delFlag = 0 + GROUP BY ledgerId + ) f ON f.ledgerId = t.id + WHERE t.delFlag = 0 + AND t.matterId IS NOT NULL + AND t.matterId <> '' + GROUP BY t.matterId + ) sc ON sc.matterId = m.id WHERE m.delFlag = 0 AND m.enabled = 1 AND m.id = @matterId diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/dashboard/ProposalDashboardController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/dashboard/ProposalDashboardController.java index 8dfce38c..2cbd8a50 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/dashboard/ProposalDashboardController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/dashboard/ProposalDashboardController.java @@ -128,27 +128,13 @@ public class ProposalDashboardController { nodes.addAll(taskNodes); nodes.addAll(feedbackNodes); - // 查询待办任务 - Sql todoSql = Sqls.create(""" - SELECT - t.taskName, - t.taskState - FROM - wf_process_task t - INNER JOIN wf_process_instance ins ON ins.id = t.processInstanceId - INNER JOIN proposal_info info ON info.id = ins.businessNo - WHERE - info.sessionId = @sessionId - """); - todoSql.setParam("sessionId", sessionId); - List todoTasks = processDefineService.listMap(todoSql); + Map taskProposalCounts = proposalCommonService.countDashboardTaskProposals(sessionId); for (NutMap node : nodes) { if (node.getString("type").equals("task")) { - long todoCount = todoTasks.stream().filter(task -> task.getInt("taskState") == ProcessTaskStateEnum.DOING.getCode() && task.getString("taskName").equals(node.getString("id"))).count(); - node.put("todoCount", todoCount); - long doneCount = todoTasks.stream().filter(task -> task.getInt("taskState") == ProcessTaskStateEnum.FINISHED.getCode() && task.getString("taskName").equals(node.getString("id"))).count(); - node.put("doneCount", doneCount); + NutMap taskCount = taskProposalCounts.get(node.getString("id")); + node.put("todoCount", taskCount == null ? 0 : taskCount.getLong("todoCount", 0L)); + node.put("doneCount", taskCount == null ? 0 : taskCount.getLong("doneCount", 0L)); } else if (node.getString("type").equals("total")) { int count = dao.count(ProposalInfo.class, Cnd.where(ProposalInfo::getSessionId, "=", sessionId)); node.put("count", count); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryCollectProgressController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryCollectProgressController.java index ab386417..3f1e5809 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryCollectProgressController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryCollectProgressController.java @@ -1,22 +1,13 @@ package com.budwk.app.zhgh.democratic.proposal.controller.query; import cn.dev33.satoken.annotation.SaCheckPermission; -import cn.hutool.core.util.ArrayUtil; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.result.Result; -import com.budwk.app.flow.entity.ProcessTask; -import com.budwk.app.flow.enums.ProcessInstanceStateEnum; -import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.zhgh.democratic.proposal.param.ProposalQueryComprehensiveParam; import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; -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.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.util.NutMap; @@ -27,7 +18,6 @@ import org.nutz.mvc.annotation.Param; import javax.validation.Valid; import java.util.ArrayList; import java.util.List; -import java.util.Map; @IocBean @@ -37,20 +27,6 @@ import java.util.Map; @Api(tags = "征集进度查询") public class ProposalQueryCollectProgressController { - /** - * 征集进度表格允许排序的字段映射,值为查询中的真实字段或安全别名。 - */ - private static final Map COLLECT_PROGRESS_ORDER_COLUMNS = Map.ofEntries( - Map.entry("code", "info.code"), - Map.entry("name", "info.name"), - Map.entry("createUserName", "info.createUserName"), - Map.entry("delegationName", "delegationName"), - Map.entry("inviteCount", "inviteCount"), - Map.entry("finishCount", "finishCount"), - Map.entry("curTaskName", "curTaskName"), - Map.entry("instanceState", "instanceState") - ); - static List states = new ArrayList<>() {{ add(NutMap.NEW().addv("code", 10).addv("id", 10).addv("name", "撰写提案")); add(NutMap.NEW().addv("code", 20).addv("id", 20).addv("name", "附议提案")); @@ -60,9 +36,6 @@ public class ProposalQueryCollectProgressController { @Inject private ProposalCommonService proposalCommonService; - @Inject - private Dao dao; - @At("") @Ok("beetl:/platform/zhgh/democratic/proposal/query/collectProgress/index.html") @@ -74,67 +47,7 @@ public class ProposalQueryCollectProgressController { @SaCheckPermission("proposal.query.collectProgress") @ApiOperation(value = "分页列表") public Result pageData(@Valid @Param("pageForm") ProposalQueryComprehensiveParam pageForm) { - Sql sql = Sqls.create(""" - SELECT - info.id, - info.name, - info.code, - info.createUserName, - type.NAME AS typeName, - tcs.fullName AS sessionName, - tcd.`name` AS delegationName, - (SELECT count(1) FROM proposal_second WHERE proposalId = info.id) AS inviteCount, - (SELECT count(1) FROM wf_process_task WHERE processInstanceId = ins.id AND taskName = 'second' AND taskState = 20) finishCount, - ins.id AS instanceId, - ins.state instanceState, - t.displayName curTaskName - FROM - proposal_info info - LEFT JOIN proposal_type type ON type.id = info.typeId - LEFT JOIN teacher_congress_session tcs ON tcs.id = info.sessionId - LEFT JOIN teacher_congress_delegation tcd ON tcd.id = info.delegationId - LEFT JOIN teacher_congress_delegate tcde on tcde.loginName = info.createUserLoginName - LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id - LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10 - $condition - """); - Cnd cnd = Cnd.NEW(); - // 征集进度必须按页面选中的教代会过滤,避免共享参数未处理sessionId导致跨届次查询。 - cnd.andEX("info.sessionId", "=", pageForm.getSessionId()); - ProposalQueryComprehensiveParam.buildSearch(cnd, pageForm); - - SqlExpressionGroup seg = new SqlExpressionGroup(); - if (ArrayUtil.contains(pageForm.getCollectIds(), 10)) { - seg.or("t.taskName", "=", "startTask"); - } - if (ArrayUtil.contains(pageForm.getCollectIds(), 20)) { - seg.or("t.taskName", "=", "second"); - } - if (ArrayUtil.contains(pageForm.getCollectIds(), 30)) { - seg.or("t.taskName", "=", "delegation"); - } - if (ArrayUtil.contains(pageForm.getCollectIds(), 40)) { - seg.or("t.taskName", "not in", List.of("startTask", "second", "delegation")); - seg.and("t.taskName", "is not", null); - seg.or("ins.state","=", ProcessInstanceStateEnum.FINISHED.getCode()); - } - if (!seg.isEmpty()) { - cnd.and(seg); - } - cnd.groupBy("info.id"); - // 征集进度包含关联字段和人数统计别名,只允许白名单字段进入排序条件。 - proposalCommonService.applySafePageOrder(cnd, pageForm, COLLECT_PROGRESS_ORDER_COLUMNS); - sql.setCondition(cnd); - Pagination pagination = proposalCommonService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); - List list = (List) pagination.getList(); -// for (NutMap row : list) { -// int count = dao.count(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", row.getString("instanceId")) -// .and(ProcessTask::getTaskName, "=", "second") -// .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode()) -// ); -// -// -// } + Pagination pagination = proposalCommonService.queryCollectProgress(pageForm); return Result.success(pagination); } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryDelegationController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryDelegationController.java index 65fe6e44..3ef367a3 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryDelegationController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/query/ProposalQueryDelegationController.java @@ -77,6 +77,10 @@ public class ProposalQueryDelegationController { @ApiOperation("代表团提案统计") @SaCheckPermission("proposal.query.delegation") public Result pageData(PageForm pageForm, String sessionId) { + // 统计必须明确限定届次,空参数不能退化为跨届次汇总,避免页面异步初始化时展示错误数据。 + if (StrUtil.isBlank(sessionId)) { + return Result.success(List.of()); + } List dictList = sysDictService.getSubListByCode("PROPOSAL_CASE_FILING_RESULT"); Sql sql = Sqls.create(""" SELECT @@ -90,19 +94,17 @@ public class ProposalQueryDelegationController { FROM teacher_congress_delegation dbt $condition - """); + """); Map summaryOrderColumns = new HashMap<>(DELEGATION_SUMMARY_ORDER_COLUMNS); List resultSqlParts = new ArrayList<>(); - int resultIndex = 0; for (Sys_dict dict : dictList) { String resultCode = dict.getCode(); - // 动态别名仅允许字母、数字和下划线,查询值使用参数绑定,避免字典内容进入SQL结构。 + // 动态别名和立案结果编码仅允许字母、数字和下划线;变量替换后新增的 @ 参数不会被 Nutz 再次解析, + // 因此这里使用已校验的字典编码字面量,确保统计条件能正确传递给数据库。 if (StrUtil.isBlank(resultCode) || !resultCode.matches("[A-Za-z0-9_]+")) { continue; } - String paramName = "caseFilingResult" + resultIndex++; - resultSqlParts.add(", (select count(1) from proposal_info info where info.delegationId = dbt.id and info.sessionId = dbt.sessionId and info.caseFilingResult = @" + paramName + ") AS `" + resultCode + "`"); - sql.setParam(paramName, resultCode); + resultSqlParts.add(", (select count(1) from proposal_info info where info.delegationId = dbt.id and info.sessionId = dbt.sessionId and info.caseFilingResult = '" + resultCode + "') AS `" + resultCode + "`"); summaryOrderColumns.put(resultCode, "`" + resultCode + "`"); } sql.setVar("resultSql", String.join("", resultSqlParts)); diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/senior/ProposalSeniorBasicController.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/senior/ProposalSeniorBasicController.java index 6b44aef9..492b2afb 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/senior/ProposalSeniorBasicController.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/controller/senior/ProposalSeniorBasicController.java @@ -97,7 +97,7 @@ public class ProposalSeniorBasicController { return Result.error("提案编号已存在"); } - dao.update(proposalInfo,"^code|name|typeId|brief|measures|excerpt|suggestUnits|files$"); + dao.update(proposalInfo,"^code|caseFilingCode|name|typeId|brief|measures|excerpt|suggestUnits|files$"); 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 f517360f..d77483ac 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 @@ -103,7 +103,7 @@ public class ProposalMineController { @At("/h5") @Ok("beetl:/platform/zhghh5/democratic/proposal/transact/mine/index.html") - @SaCheckPermission("proposal.mine") + @SaCheckPermission("h5.proposal.mine") public void h5Index() { } @@ -111,6 +111,18 @@ public class ProposalMineController { @SaCheckPermission("proposal.mine") @Aop(TransAop.READ_COMMITTED) public Result pageData(@Valid ProposalSearchParam pageForm, String sessionId) { + return queryPageData(pageForm, sessionId); + } + + /** + * 查询当前登录人的提案分页数据。 + * PC 与 H5 入口共享同一数据范围和排序规则,避免两个端的列表结果不一致。 + * + * @param pageForm 页面筛选及分页参数 + * @param sessionId 教代会届次ID + * @return 当前登录人的提案分页结果 + */ + private Result queryPageData(ProposalSearchParam pageForm, String sessionId) { Sql sql = Sqls.create(""" SELECT info.*, @@ -158,6 +170,20 @@ public class ProposalMineController { return Result.success(pagination); } + /** + * 我的提案 H5 分页列表。 + * H5 使用独立权限标识,但查询口径与 PC 端保持一致。 + * + * @param pageForm 页面筛选及分页参数 + * @param sessionId 教代会届次ID + * @return 当前登录人的提案分页结果 + */ + @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 b54cb81a..e88c210a 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 @@ -84,13 +84,25 @@ public class ProposalSecondedController { @At("/h5") @Ok("beetl:/platform/zhghh5/democratic/proposal/transact/seconded/index.html") - @SaCheckPermission("proposal.seconded") + @SaCheckPermission("h5.proposal.seconded") public void h5Index() { } @At @SaCheckPermission("proposal.seconded") public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval) { + return queryPageData(pageForm, approval); + } + + /** + * 查询当前登录附议人的提案分页数据。 + * PC 与 H5 入口共享同一待附议、已附议筛选口径。 + * + * @param pageForm 页面筛选及分页参数 + * @param approval 是否查询已附议记录 + * @return 附议提案分页结果 + */ + private Result queryPageData(ProposalSearchParam pageForm, boolean approval) { Sql sql = Sqls.create(""" SELECT info.*, @@ -165,6 +177,20 @@ public class ProposalSecondedController { return Result.success(pagination); } + /** + * 提案附议 H5 分页列表。 + * H5 使用独立权限标识,但查询口径与 PC 端保持一致。 + * + * @param pageForm 页面筛选及分页参数 + * @param approval 是否查询已附议记录 + * @return 附议提案分页结果 + */ + @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/service/common/ProposalCommonService.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/common/ProposalCommonService.java index 207a0ade..527d3a09 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 @@ -5,7 +5,9 @@ import com.budwk.app.base.param.PageForm; import com.budwk.app.base.param.ExportTableColumns; 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.ProposalQueryComprehensiveParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; +import com.budwk.app.base.page.Pagination; import org.nutz.dao.Cnd; import org.nutz.lang.util.NutMap; @@ -171,4 +173,21 @@ public interface ProposalCommonService extends BaseService { * 修改提案状态码 */ void updateStateCode(String id, Integer stateCode); + + /** + * 查询提案征集进度列表。 + * 查询结果与页面既有筛选、排序语义保持一致,内部使用聚合关联避免逐行执行统计子查询。 + * + * @param pageForm 征集进度页面的筛选、排序和分页参数 + * @return 包含征集进度统计字段的分页结果 + */ + Pagination queryCollectProgress(ProposalQueryComprehensiveParam pageForm); + + /** + * 按提案去重统计指定届次各流程节点的待办和已办数量。 + * + * @param sessionId 教代会届次ID + * @return 键为流程节点编码、值为待办和已办数量的统计结果 + */ + Map countDashboardTaskProposals(String sessionId); } 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 b19294ff..c24e380c 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 @@ -3,6 +3,7 @@ package com.budwk.app.zhgh.democratic.proposal.service.common; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Dict; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -15,6 +16,7 @@ import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.exception.BaseException; import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.param.PageForm; +import com.budwk.app.base.page.Pagination; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.base.utils.CommonDownloadUtil; import com.budwk.app.base.utils.PageUtil; @@ -56,6 +58,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; @@ -85,6 +88,18 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp /** 办理页面允许导出的列表字段,避免请求参数携带非页面字段。 */ private static final Map> WORKFLOW_EXPORT_COLUMNS = createWorkflowExportColumns(); + /** 征集进度页面允许排序的字段,避免排序参数直接拼接到SQL。 */ + private static final Map COLLECT_PROGRESS_ORDER_COLUMNS = Map.ofEntries( + Map.entry("code", "info.code"), + Map.entry("name", "info.name"), + Map.entry("createUserName", "info.createUserName"), + Map.entry("delegationName", "delegationName"), + Map.entry("inviteCount", "inviteCount"), + Map.entry("finishCount", "finishCount"), + Map.entry("curTaskName", "curTaskName"), + Map.entry("instanceState", "instanceState") + ); + //找出富文本里面上传的图片 static String IMG_SRC_REGEX = "]*src\\s*=\\s*([\"'])(/platform/sys/file/download\\?id=.*?)\\1[^>]*>"; @@ -107,6 +122,128 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp super(dao); } + /** + * 分页查询提案征集进度。 + * 附议人数和已附议人数使用分组结果关联,避免原查询对每一条提案重复执行统计子查询; + * 总数查询只保留筛选所需关联,避免分页统计重复执行列表中的统计计算。 + * + * @param pageForm 页面筛选、排序和分页参数 + * @return 征集进度分页数据 + */ + @Override + public Pagination queryCollectProgress(ProposalQueryComprehensiveParam pageForm) { + Sql sql = Sqls.create(""" + SELECT + info.id, + info.name, + info.code, + info.createUserName, + type.NAME AS typeName, + tcs.fullName AS sessionName, + tcd.`name` AS delegationName, + COALESCE(secondStat.inviteCount, 0) AS inviteCount, + COALESCE(taskStat.finishCount, 0) AS finishCount, + ins.id AS instanceId, + ins.state AS instanceState, + t.displayName AS curTaskName + FROM proposal_info info + LEFT JOIN proposal_type type ON type.id = info.typeId + LEFT JOIN teacher_congress_session tcs ON tcs.id = info.sessionId + LEFT JOIN teacher_congress_delegation tcd ON tcd.id = info.delegationId + LEFT JOIN teacher_congress_delegate tcde ON tcde.loginName = info.createUserLoginName + LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id + LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10 + LEFT JOIN ( + SELECT proposalId, COUNT(1) AS inviteCount + FROM proposal_second + GROUP BY proposalId + ) secondStat ON secondStat.proposalId = info.id + LEFT JOIN ( + SELECT processInstanceId, COUNT(1) AS finishCount + FROM wf_process_task + WHERE taskName = 'second' AND taskState = 20 + GROUP BY processInstanceId + ) taskStat ON taskStat.processInstanceId = ins.id + $condition + """); + Cnd dataCnd = buildCollectProgressCondition(pageForm); + dataCnd.groupBy("info.id"); + applySafePageOrder(dataCnd, pageForm, COLLECT_PROGRESS_ORDER_COLUMNS); + sql.setCondition(dataCnd); + + Sql countSql = Sqls.create(""" + SELECT COUNT(DISTINCT info.id) + FROM proposal_info info + LEFT JOIN teacher_congress_delegate tcde ON tcde.loginName = info.createUserLoginName + LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id + LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10 + $condition + """); + countSql.setCondition(buildCollectProgressCondition(pageForm)); + return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql, countSql); + } + + /** + * 按提案ID去重统计数据看板的流程节点数量。 + * 同一提案因撤回、退回等再次经过同一节点时,任务表会保留多条已办记录; + * 此处按提案统计,使看板数量与下方按提案展示的列表保持一致。 + * + * @param sessionId 教代会届次ID + * @return 以流程节点编码为键的待办、已办统计结果 + */ + @Override + public Map countDashboardTaskProposals(String sessionId) { + Sql sql = Sqls.create(""" + SELECT + t.taskName, + COUNT(DISTINCT CASE WHEN t.taskState = @doingState THEN info.id END) AS todoCount, + COUNT(DISTINCT CASE WHEN t.taskState = @finishedState THEN info.id END) AS doneCount + FROM wf_process_task t + INNER JOIN wf_process_instance ins ON ins.id = t.processInstanceId + INNER JOIN proposal_info info ON info.id = ins.businessNo + WHERE info.sessionId = @sessionId + GROUP BY t.taskName + """); + sql.setParam("sessionId", sessionId); + sql.setParam("doingState", ProcessTaskStateEnum.DOING.getCode()); + sql.setParam("finishedState", ProcessTaskStateEnum.FINISHED.getCode()); + List taskCounts = listMap(sql); + return taskCounts.stream().collect(Collectors.toMap(task -> task.getString("taskName"), task -> task)); + } + + /** + * 构建征集进度列表和总数查询共用的筛选条件,确保两次查询返回同一数据范围。 + * 征集阶段条件保持页面原有的布尔组合方式,避免优化时改变既有查询结果。 + * + * @param pageForm 页面筛选参数 + * @return 不含分组和排序的查询条件 + */ + private Cnd buildCollectProgressCondition(ProposalQueryComprehensiveParam pageForm) { + Cnd cnd = Cnd.NEW(); + cnd.andEX("info.sessionId", "=", pageForm.getSessionId()); + ProposalQueryComprehensiveParam.buildSearch(cnd, pageForm); + + SqlExpressionGroup collectStageCondition = new SqlExpressionGroup(); + if (ArrayUtil.contains(pageForm.getCollectIds(), 10)) { + collectStageCondition.or("t.taskName", "=", "startTask"); + } + if (ArrayUtil.contains(pageForm.getCollectIds(), 20)) { + collectStageCondition.or("t.taskName", "=", "second"); + } + if (ArrayUtil.contains(pageForm.getCollectIds(), 30)) { + collectStageCondition.or("t.taskName", "=", "delegation"); + } + if (ArrayUtil.contains(pageForm.getCollectIds(), 40)) { + collectStageCondition.or("t.taskName", "not in", List.of("startTask", "second", "delegation")); + collectStageCondition.and("t.taskName", "is not", null); + collectStageCondition.or("ins.state", "=", ProcessInstanceStateEnum.FINISHED.getCode()); + } + if (!collectStageCondition.isEmpty()) { + cnd.and(collectStageCondition); + } + return cnd; + } + /** * 使用业务页面提供的字段白名单构建排序条件,非法字段或非法排序方向将被忽略。 * @@ -121,8 +258,14 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp return false; } + // H5 列表首次加载时可能不传排序字段;不可变 Map 不接受 null 键,直接保留页面默认排序。 + String pageOrderName = pageForm.getPageOrderName(); + if (StrUtil.isBlank(pageOrderName)) { + return false; + } + // 仅允许白名单中的字段参与排序,防止构造任意SQL排序字段。 - String orderColumn = allowedOrderColumns.get(pageForm.getPageOrderName()); + String orderColumn = allowedOrderColumns.get(pageOrderName); String orderBy = PageUtil.getOrder(pageForm.getPageOrderBy()); if (StrUtil.isBlank(orderColumn) || StrUtil.isBlank(orderBy)) { return false; @@ -814,35 +957,35 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl imp // 获取流程实例 ProcessInstance processInstance = dao().fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", id)); + if (processInstance != null) { + // 主办办理情况 + ProcessTask masterUnitReplyTask = dao().fetch(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstance.getId()) + .and(ProcessTask::getTaskName, "in", List.of("master_reply", "opinion_master_reply")) + .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode()) + .desc(ProcessTask::getFinishTime) + ); + if (masterUnitReplyTask != null) { + // 办理解决情况 + String implementState = FlowUtil.variableToDict(masterUnitReplyTask.getVariable()).getStr("tf_implementState"); + info.put(implementState, "√"); + // 承办单位名称 + info.put("underTakeName", FlowUtil.variableToDict(masterUnitReplyTask.getVariable()).getStr("underTakeName")); + } - // 主办办理情况 - ProcessTask masterUnitReplyTask = dao().fetch(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstance.getId()) - .and(ProcessTask::getTaskName, "in", List.of("master_reply", "opinion_master_reply")) - .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode()) - .desc(ProcessTask::getFinishTime) - ); - if (masterUnitReplyTask != null) { - // 办理解决情况 - String implementState = FlowUtil.variableToDict(masterUnitReplyTask.getVariable()).getStr("tf_implementState"); - info.put(implementState, "√"); - // 承办单位名称 - info.put("underTakeName", FlowUtil.variableToDict(masterUnitReplyTask.getVariable()).getStr("underTakeName")); - } - - - // 反馈评价 - ProcessTask feedbackTask = dao().fetch(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstance.getId()) - .and(ProcessTask::getTaskName, "=", "feedback") - .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode()) - .desc(ProcessTask::getFinishTime) - ); - if (feedbackTask != null) { - info.put(FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_feedback"), "√"); - info.put("tf_feedback", FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_feedback")); - info.put("tf_opinion", FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_opinion")); - info.put("tf_phone", FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_phone")); - info.put("tf_address", FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_address")); - info.put("tf_postcode", FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_postcode")); + // 反馈评价 + ProcessTask feedbackTask = dao().fetch(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstance.getId()) + .and(ProcessTask::getTaskName, "=", "feedback") + .and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode()) + .desc(ProcessTask::getFinishTime) + ); + if (feedbackTask != null) { + info.put(FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_feedback"), "√"); + info.put("tf_feedback", FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_feedback")); + info.put("tf_opinion", FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_opinion")); + info.put("tf_phone", FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_phone")); + info.put("tf_address", FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_address")); + info.put("tf_postcode", FlowUtil.variableToDict(feedbackTask.getVariable()).getStr("tf_postcode")); + } } HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy(); 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 8266ada4..c8bc59b2 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 @@ -82,12 +82,12 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl proposalIds = proposalCommonService.mergeProposal(proposalId); if (ObjectUtil.isEmpty(proposalIds)) { proposalIds = List.of(proposalId); } + validateAndNormalizeCaseFilingCode(args, proposalIds); /* * 先同步 proposal_info 和承办单位,再执行 WF 流转,使查看页面及下一节点读取到的都是本次最新数据。 @@ -111,12 +111,14 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl excludedProposalIds) { String caseFilingResultKey = FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingResult"; String caseFilingCodeKey = FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingCode"; String caseFilingTypeKey = FlowConst.TASK_FORM_DATA_PREFIX + "caseFilingType"; @@ -133,6 +135,13 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl 20) { throw new BaseException("立案编号不能超过20个字符"); } + Cnd duplicateCondition = Cnd.where("caseFilingCode", "=", caseFilingCode); + if (!ObjectUtil.isEmpty(excludedProposalIds)) { + duplicateCondition.and("id", "not in", excludedProposalIds); + } + if (dao().count(ProposalInfo.class, duplicateCondition) > 0) { + throw new BaseException("立案编号已存在,请重新填写"); + } args.put(caseFilingCodeKey, caseFilingCode); } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalExportServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalExportServiceImpl.java index 721fdc83..05bb0af8 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalExportServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/proposal/service/impl/ProposalExportServiceImpl.java @@ -654,9 +654,12 @@ public class ProposalExportServiceImpl extends BaseServiceImpl imp LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId LEFT JOIN teacher_congress_delegate tcde on tcde.loginName = info.createUserLoginName + LEFT JOIN proposal_reply_unit pru ON pru.proposalId = info.id $condition """); Cnd cnd = Cnd.NEW(); + // 综合导出页面按当前选择的届次导出,避免公共查询参数的跨届次场景影响导出范围。 + cnd.andEX("info.sessionId", "=", pageForm.getSessionId()); ProposalQueryComprehensiveParam.buildSearch(cnd, pageForm); cnd.groupBy("info.id"); cnd.asc("info.code"); @@ -687,46 +690,45 @@ public class ProposalExportServiceImpl extends BaseServiceImpl imp return StrUtil.blankToDefault(name, "unnamed").replaceAll("[\\\\/:*?\"<>|\\r\\n]+", "_"); } + /** + * 生成 ZIP 内唯一且合法的 Word 文件名,避免提案名称中的非法字符或重名条目导致整个压缩包缺文件。 + * + * @param prefix 文档类型前缀 + * @param proposalRow 提案基础信息 + * @param entryNameCounts 已生成的基础文件名及其出现次数 + * @return 可以直接用于 ZipEntry 的文件名 + */ + private String createUniqueZipDocxEntryName(String prefix, NutMap proposalRow, Map entryNameCounts) { + String baseName = sanitizeZipFileName(prefix + "-" + proposalRow.getString("code", "未编号") + "-" + proposalRow.getString("name", "未命名提案")); + int sameNameCount = entryNameCounts.merge(baseName, 1, Integer::sum); + return baseName + (sameNameCount > 1 ? "-" + sameNameCount : "") + ".docx"; + } + @Override public void exportFeedBackAsZip(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response) { - Sql sql = Sqls.create(""" - SELECT - info.id, - info.code, - info.name - FROM - proposal_info info - LEFT JOIN proposal_type type on type.id = info.typeId - LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId - LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId - LEFT JOIN teacher_congress_delegate tcde on tcde.loginName = info.createUserLoginName - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.groupBy("info.id"); - ProposalQueryComprehensiveParam.buildSearch(cnd, pageForm); - sql.setCondition(cnd); - List list = listMap(sql); - - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ZipOutputStream zipOutputStream = new ZipOutputStream(bos); + List list = queryProposalBaseList(pageForm); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ZipOutputStream zipOutputStream = new ZipOutputStream(bos)) { + Map entryNameCounts = new HashMap<>(); for (NutMap proposalRow : list) { try (ByteArrayOutputStream docxByteArrayOutputStream = new ByteArrayOutputStream()) { proposalCommonService.exportProposalFeedBackAsDocx(proposalRow.getString("id"), docxByteArrayOutputStream); - ZipEntry zipEntry = new ZipEntry("反馈表-" + proposalRow.getString("code") + "-" + proposalRow.getString("name") + ".docx"); + if (docxByteArrayOutputStream.size() == 0) { + throw new BaseException("提案【{}】反馈表生成失败", proposalRow.getString("code")); + } + ZipEntry zipEntry = new ZipEntry(createUniqueZipDocxEntryName("反馈表", proposalRow, entryNameCounts)); zipOutputStream.putNextEntry(zipEntry); docxByteArrayOutputStream.writeTo(zipOutputStream); zipOutputStream.closeEntry(); } catch (IOException e) { - // 处理单个文件生成失败的情况 - e.printStackTrace(); + throw new BaseException("提案【{}】反馈表写入压缩包失败", proposalRow.getString("code")); } } - zipOutputStream.close(); + zipOutputStream.finish(); CommonDownloadUtil.download("提案反馈表压缩包.zip", bos.toByteArray(), response); } catch (Exception e) { - e.printStackTrace(); + log.error("导出提案反馈表压缩包失败", e); + throw new BaseException("导出提案反馈表压缩包失败:{}", e.getMessage()); } } @@ -834,43 +836,29 @@ public class ProposalExportServiceImpl extends BaseServiceImpl imp @Override public void exportCollectZip(ProposalQueryComprehensiveParam pageForm, HttpServletResponse response) { - Sql sql = Sqls.create(""" - SELECT - info.id, - info.code, - info.name - FROM - proposal_info info - LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId - LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId - LEFT JOIN teacher_congress_delegate tcde on tcde.loginName = info.createUserLoginName - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.groupBy("info.id"); - ProposalQueryComprehensiveParam.buildSearch(cnd, pageForm); - sql.setCondition(cnd); - List list = listMap(sql); - - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ZipOutputStream zipOutputStream = new ZipOutputStream(bos); + List list = queryProposalBaseList(pageForm); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ZipOutputStream zipOutputStream = new ZipOutputStream(bos)) { + Map entryNameCounts = new HashMap<>(); for (NutMap proposalRow : list) { try (ByteArrayOutputStream docxByteArrayOutputStream = new ByteArrayOutputStream()) { exportCollectDocx(proposalRow.getString("id"), docxByteArrayOutputStream); - ZipEntry zipEntry = new ZipEntry("征集表-" + proposalRow.getString("code") + "-" + proposalRow.getString("name") + ".docx"); + if (docxByteArrayOutputStream.size() == 0) { + throw new BaseException("提案【{}】征集表生成失败", proposalRow.getString("code")); + } + ZipEntry zipEntry = new ZipEntry(createUniqueZipDocxEntryName("征集表", proposalRow, entryNameCounts)); zipOutputStream.putNextEntry(zipEntry); docxByteArrayOutputStream.writeTo(zipOutputStream); zipOutputStream.closeEntry(); } catch (IOException e) { - // 处理单个文件生成失败的情况 - e.printStackTrace(); + throw new BaseException("提案【{}】征集表写入压缩包失败", proposalRow.getString("code")); } } - zipOutputStream.close(); + zipOutputStream.finish(); CommonDownloadUtil.download("提案征集表压缩包.zip", bos.toByteArray(), response); } catch (Exception e) { - e.printStackTrace(); + log.error("导出提案征集表压缩包失败", e); + throw new BaseException("导出提案征集表压缩包失败:{}", e.getMessage()); } } diff --git a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/service/impl/TeacherCongressInstitutionUserServiceImpl.java b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/service/impl/TeacherCongressInstitutionUserServiceImpl.java index 5c466297..e2816eb9 100644 --- a/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/service/impl/TeacherCongressInstitutionUserServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/democratic/teachercongress/institution/service/impl/TeacherCongressInstitutionUserServiceImpl.java @@ -17,6 +17,8 @@ import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import java.util.List; + @IocBean(args = {"refer:dao"}) public class TeacherCongressInstitutionUserServiceImpl extends BaseServiceImpl implements TeacherCongressInstitutionUserService { @Inject @@ -49,21 +51,9 @@ public class TeacherCongressInstitutionUserServiceImpl extends BaseServiceImpl dao().fetch(Teacher_congress_institution.class, institutionUser.getInstitutionId())) + .anyMatch(this::isProposalReviewCommittee); + if (stillDirector) { + return; + } + Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DIRECTOR); + if (sysRole == null) { + return; + } + dao().clear(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", userId) + .and(Sys_user_role::getTcSessionId, "=", sessionId) + .and(Sys_user_role::getRoleId, "=", sysRole.getId())); + sysRoleService.clearCache(); + sysUserService.clearCache(); } } diff --git a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareListController.java b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareListController.java index d071875a..635be843 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareListController.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareListController.java @@ -92,6 +92,55 @@ public class WelfareListController { return Result.success(); } + /** + * 统计当前搜索条件下的福利名单人数,用于批量删除前二次确认。 + * + * @param pageForm 福利名单搜索条件 + * @return 命中人数 + */ + @At + @SaCheckPermission("welfare.list.mange") + public Result countByPageForm(@Valid @Param("pageForm") WelfareListPageForm pageForm) { + if (StrUtil.isBlank(pageForm.getProjectId())) { + return Result.error("请选择福利项目"); + } + return Result.success(welfareListService.countByPageForm(pageForm)); + } + + /** + * 按当前搜索条件删除福利名单及对应的用户已选福利记录。 + * + * @param pageForm 福利名单搜索条件 + * @return 删除结果 + */ + @At + @Aop(TransAop.READ_COMMITTED) + @ApiOperation("按搜索条件删除福利名单人员") + @SaCheckPermission("welfare.list.mange") + @SLog(tag = "福利名单管理", msg = "按搜索条件批量删除福利名单人员") + public Result deleteByPageForm(@Valid @Param("pageForm") WelfareListPageForm pageForm) { + if (StrUtil.isBlank(pageForm.getProjectId())) { + return Result.error("请选择福利项目"); + } + int deletedCount = welfareListService.deleteByPageForm(pageForm); + return Result.success("成功删除" + deletedCount + "人"); + } + + /** + * 根据人员当前所属单位同步指定福利项目名单的单位和工会快照。 + * + * @param projectId 福利项目 ID + * @return 同步结果 + */ + @At + @Aop(TransAop.READ_COMMITTED) + @ApiOperation("同步福利名单工会") + @SaCheckPermission("welfare.list.mange") + @SLog(tag = "福利名单管理", msg = "同步了福利名单单位和工会") + public Result syncWelfareListUnitAndUnion(String projectId) { + return welfareListService.syncWelfareListUnitAndUnion(projectId); + } + @At @SaCheckPermission("welfare.list.mange") @ApiOperation("分页查询非福利会员") diff --git a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareProjectMangeController.java b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareProjectMangeController.java index 6fad271e..36daca99 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareProjectMangeController.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareProjectMangeController.java @@ -97,6 +97,17 @@ public class WelfareProjectMangeController { return Result.success(projectService.projectInfo(id)); } + /** + * 查询可沿用的往期福利项目。 + * + * @return 往期福利项目列表 + */ + @At + @SaCheckPermission("welfare.project.mange") + public Result historyProjectList() { + return Result.success(projectService.listHistoryProject()); + } + @At @SaCheckPermission("welfare.project.mange") @@ -117,6 +128,24 @@ public class WelfareProjectMangeController { return Result.success(); } + /** + * 清空指定福利项目的名单及用户已选福利记录。 + * + * @param id 福利项目 ID + * @return 清空结果 + */ + @At + @Aop(TransAop.READ_COMMITTED) + @SLog(tag = "福利", msg = "清空了福利名单") + @SaCheckPermission("welfare.project.mange") + public Result clearWelfareList(String id) { + if (StrUtil.isBlank(id) || projectService.fetch(id) == null) { + return Result.error("福利项目不存在"); + } + projectService.clearWelfareList(id); + return Result.success("福利名单已清空"); + } + @At @SaCheckPermission("welfare.mine") public Result getWelfareList(Integer year) { diff --git a/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareListPageForm.java b/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareListPageForm.java index 1ba712dc..2b07edb9 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareListPageForm.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareListPageForm.java @@ -43,6 +43,9 @@ public class WelfareListPageForm extends PageForm { @ApiModelProperty("单位id") private String[] unitIds; + @ApiModelProperty("三级单位id") + private String[] threeUnitIds; + @ApiModelProperty("单位名称") private String unitName; @@ -59,6 +62,6 @@ public class WelfareListPageForm extends PageForm { private String aidFundMemberUserType; @ApiModelProperty("人员属性") - private String userAttribute; + private String[] userAttributes; } diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareListService.java b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareListService.java index 6b231b34..04eb36b2 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareListService.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareListService.java @@ -33,6 +33,30 @@ public interface WelfareListService extends BaseService { */ Pagination pageData(WelfareListPageForm pageForm); + /** + * 统计当前搜索条件命中的福利名单人数。 + * + * @param pageForm 福利名单搜索条件 + * @return 命中人数 + */ + int countByPageForm(WelfareListPageForm pageForm); + + /** + * 按当前搜索条件删除福利名单及对应的用户已选福利记录。 + * + * @param pageForm 福利名单搜索条件 + * @return 删除的名单人数 + */ + int deleteByPageForm(WelfareListPageForm pageForm); + + /** + * 根据人员当前所属单位同步福利名单的单位和工会快照。 + * + * @param projectId 福利项目 ID + * @return 同步结果及变更人数 + */ + Result syncWelfareListUnitAndUnion(String projectId); + /** * 查询非福利会员用户 * @param pageForm diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareProjectService.java b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareProjectService.java index a061f95a..389a53a0 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareProjectService.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareProjectService.java @@ -17,6 +17,13 @@ public interface WelfareProjectService extends BaseService { */ WelfareProject projectInfo(String projectId); + /** + * 查询可用于沿用配置的历史福利项目。 + * + * @return 历史福利项目列表 + */ + List listHistoryProject(); + /** * 校验福利选择是否保留系统默认福利。 * @@ -44,6 +51,13 @@ public interface WelfareProjectService extends BaseService { */ void deleteWelfare(String id); + /** + * 清空指定福利项目的名单及用户已选福利记录,保留项目配置。 + * + * @param projectId 福利项目 ID + */ + void clearWelfareList(String projectId); + /** * 创建福利名单 * @param projectId diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java index 3574af35..0d065412 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java @@ -28,11 +28,13 @@ import com.budwk.app.zhgh.welfare.service.WelfareListService; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.nutz.aop.interceptor.ioc.TransAop; 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.json.Json; import org.nutz.lang.Lang; @@ -390,12 +392,130 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme t2.loginname, t2.username, t2.sex, - t2.birthday + t2.birthday, + threeUnit.name AS threeUnitName FROM `welfare_list` t1 LEFT JOIN `vw_user` t2 ON t2.id = t1.userId + LEFT JOIN sys_unit threeUnit ON threeUnit.id = t2.threeUnitId $condition """); + Cnd cnd = buildPageDataCnd(pageForm); + if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { + cnd.asc("t1.welfareUnionName"); + cnd.asc("t1.welfareUnitName"); + } else { + cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); + } + sql.setCondition(cnd); + Pagination pagination = listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); + return pagination; + } + + @Override + public int countByPageForm(WelfareListPageForm pageForm) { + Sql sql = Sqls.create("SELECT COUNT(1) FROM welfare_list t1 LEFT JOIN vw_user t2 ON t2.id = t1.userId $condition"); + sql.setCondition(buildPageDataCnd(pageForm)); + sql.setCallback(Sqls.callback.integer()); + dao().execute(sql); + return sql.getInt(); + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public int deleteByPageForm(WelfareListPageForm pageForm) { + Sql sql = Sqls.create("SELECT t1.id, t1.userId FROM welfare_list t1 LEFT JOIN vw_user t2 ON t2.id = t1.userId $condition"); + sql.setCondition(buildPageDataCnd(pageForm)); + List welfareLists = listMap(sql); + if (welfareLists.isEmpty()) { + return 0; + } + + List welfareListIds = welfareLists.stream().map(item -> item.getString("id")).collect(Collectors.toList()); + List userIds = welfareLists.stream().map(item -> item.getString("userId")).filter(StrUtil::isNotBlank).distinct().collect(Collectors.toList()); + // 先清理当前项目中这些人员的已选福利记录,确保名单与选择数据保持一致。 + if (!userIds.isEmpty()) { + dao().clear(WelfareUserSelection.class, Cnd.where("welfareId", "=", pageForm.getProjectId()).and("selectUserId", "in", userIds)); + } + dao().clear(WelfareList.class, Cnd.where("id", "in", welfareListIds)); + return welfareListIds.size(); + } + + @Override + @Aop(TransAop.READ_COMMITTED) + public Result syncWelfareListUnitAndUnion(String projectId) { + if (StrUtil.isBlank(projectId)) { + return Result.error("请选择福利项目"); + } + WelfareProject project = dao().fetch(WelfareProject.class, projectId); + if (project == null) { + return Result.error("福利项目不存在"); + } + Date currentTime = new Date(); + if (project.getChoiceTimeStart() == null || project.getChoiceTimeEnd() == null + || currentTime.before(project.getChoiceTimeStart()) || currentTime.after(project.getChoiceTimeEnd())) { + return Result.error("仅可在福利项目选择时间内同步工会"); + } + + String unionScopeSql = ""; + boolean branchUnionRole = AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WENTI_SPORTS.name()); + if (branchUnionRole) { + unionScopeSql = " AND unit.unionId = @currentUnionId"; + } + + String changedConditionSql = """ + (IFNULL(wl.welfareUnitId, '') <> IFNULL(currentUser.unitId, '') + OR IFNULL(wl.welfareUnitName, '') <> IFNULL(unit.name, '') + OR IFNULL(wl.welfareUnionId, '') <> IFNULL(unionInfo.id, '') + OR IFNULL(wl.welfareUnionName, '') <> IFNULL(unionInfo.name, '')) + """; + Sql countSql = Sqls.create(""" + SELECT COUNT(1) + FROM welfare_list wl + INNER JOIN vw_user currentUser ON currentUser.id = wl.userId + LEFT JOIN sys_unit unit ON unit.id = currentUser.unitId + LEFT JOIN sys_union unionInfo ON unionInfo.id = unit.unionId + WHERE wl.projectId = @projectId + AND """ + changedConditionSql + unionScopeSql); + countSql.setParam("projectId", projectId); + if (branchUnionRole) { + countSql.setParam("currentUnionId", SecurityUtil.getUnionId()); + } + countSql.setCallback(Sqls.callback.integer()); + dao().execute(countSql); + int changedCount = countSql.getInt(); + if (changedCount == 0) { + return Result.success("没有需要同步的人员"); + } + + Sql updateSql = Sqls.create(""" + UPDATE welfare_list wl + INNER JOIN vw_user currentUser ON currentUser.id = wl.userId + LEFT JOIN sys_unit unit ON unit.id = currentUser.unitId + LEFT JOIN sys_union unionInfo ON unionInfo.id = unit.unionId + SET wl.welfareUnitId = currentUser.unitId, + wl.welfareUnitName = unit.name, + wl.welfareUnionId = unionInfo.id, + wl.welfareUnionName = unionInfo.name + WHERE wl.projectId = @projectId + AND """ + changedConditionSql + unionScopeSql); + updateSql.setParam("projectId", projectId); + if (branchUnionRole) { + updateSql.setParam("currentUnionId", SecurityUtil.getUnionId()); + } + dao().execute(updateSql); + return Result.success("成功同步" + changedCount + "人"); + } + + /** + * 构建福利名单分页、统计和批量删除共用的筛选条件,确保三种操作范围一致。 + * + * @param pageForm 福利名单搜索条件 + * @return 数据库查询条件 + */ + private Cnd buildPageDataCnd(WelfareListPageForm pageForm) { Cnd cnd = Cnd.NEW(); cnd.and("t1.projectId", "=", pageForm.getProjectId()); if (StrUtil.isNotBlank(pageForm.getUserName())) { @@ -413,21 +533,14 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme cnd.and("t2.unionId", "=", SecurityUtil.getUnionId()); } cnd.andEX("t1.welfareUnitId", "in", pageForm.getUnitIds()); + cnd.andEX("t2.threeUnitId", "in", pageForm.getThreeUnitIds()); cnd.andEX("t1.welfareUnionId", "=", pageForm.getUnionId()); cnd.andEX("t1.personType", "in", pageForm.getPersonTypes()); cnd.andEX("t1.preparedBy", "in", pageForm.getPreparedBys()); cnd.andEX("t1.userState", "in", pageForm.getUserStates()); cnd.andEX("t2.aidFundMemberUserType", "=", pageForm.getAidFundMemberUserType()); - cnd.andEX("t2.userAttribute", "=", pageForm.getUserAttribute()); - if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { - cnd.asc("t1.welfareUnionName"); - cnd.asc("t1.welfareUnitName"); - } else { - cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); - } - sql.setCondition(cnd); - Pagination pagination = listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); - return pagination; + cnd.andEX("t2.userAttribute", "in", pageForm.getUserAttributes()); + return cnd; } @Override @@ -572,10 +685,12 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme t2.loginname, t2.username, t2.sex, - DATE_FORMAT(t2.birthday, '%Y-%m-%d') AS birthday + DATE_FORMAT(t2.birthday, '%Y-%m-%d') AS birthday, + threeUnit.name AS threeUnitName FROM `welfare_list` t1 LEFT JOIN sys_user t2 ON t2.id = t1.userId + LEFT JOIN sys_unit threeUnit ON threeUnit.id = t2.threeUnitId $condition """); Cnd cnd = Cnd.NEW(); @@ -592,10 +707,13 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme cnd.andEX("MONTH(t2.birthday)", "in", pageForm.getBirthMonths()); } cnd.andEX("t1.welfareUnitId", "in", pageForm.getUnitIds()); + cnd.andEX("t2.threeUnitId", "in", pageForm.getThreeUnitIds()); cnd.andEX("t1.welfareUnionId", "=", pageForm.getUnionId()); cnd.andEX("t1.personType", "in", pageForm.getPersonTypes()); cnd.andEX("t1.preparedBy", "in", pageForm.getPreparedBys()); cnd.andEX("t1.userState", "in", pageForm.getUserStates()); + cnd.andEX("t2.aidFundMemberUserType", "=", pageForm.getAidFundMemberUserType()); + cnd.andEX("t2.userAttribute", "in", pageForm.getUserAttributes()); if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { cnd.asc("t1.welfareUnionName"); @@ -617,6 +735,7 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme entities.add(new ExcelExportEntity("在职状态", "userState", 20)); entities.add(new ExcelExportEntity("所属工会", "welfareUnionName", 20)); entities.add(new ExcelExportEntity("所属单位", "welfareUnitName", 20)); + entities.add(new ExcelExportEntity("三级单位", "threeUnitName", 20)); entities.add(new ExcelExportEntity("备注", "remark", 20)); // 设置导出参数 diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareProjectServiceImpl.java b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareProjectServiceImpl.java index cae1c2b5..da2c6b73 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareProjectServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareProjectServiceImpl.java @@ -49,6 +49,11 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl i return project; } + @Override + public List listHistoryProject() { + return dao().query(WelfareProject.class, Cnd.NEW().desc(WelfareProject::getYear).desc(WelfareProject::getChoiceTimeStart)); + } + @Override public String validateSystemDefaultOptionSelection(String projectId, WelfareUserSelection[] selections) { List systemDefaultOptions = dao().query(WelfareProjectSubjectOption.class, @@ -164,6 +169,14 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl i dao().clear(Sys_home_activity.class, Cnd.where("id", "=", id)); } + @Override + @Aop(TransAop.READ_COMMITTED) + public void clearWelfareList(String projectId) { + // 先清空用户选择记录,避免名单清空后保留无归属的福利选择数据。 + dao().clear(WelfareUserSelection.class, Cnd.where(WelfareUserSelection::getWelfareId, "=", projectId)); + dao().clear(WelfareList.class, Cnd.where(WelfareList::getProjectId, "=", projectId)); + } + @Override public void createList(String projectId, boolean created) { WelfareProject project = projectInfo(projectId); diff --git a/src/main/resources/views/platform/sys/role/index.html b/src/main/resources/views/platform/sys/role/index.html index 527fce91..a115829d 100644 --- a/src/main/resources/views/platform/sys/role/index.html +++ b/src/main/resources/views/platform/sys/role/index.html @@ -237,17 +237,18 @@ layout("/layouts/platform.html"){ - - - - - - - - - - - + + + + + 全选 清空 @@ -257,7 +258,7 @@ layout("/layouts/platform.html"){ @@ -444,6 +446,7 @@ layout("/layouts/platform.html"){ addMenuData: [], doMenuData: [], doMenuCheckedData: [], //已分配的权限选中状态 + menuCheckedMap: {}, //完整权限树的勾选状态,搜索隐藏节点时仍需保留 doCmsData: [], doCmsCheckedData: [], //已分配的CMS权限 doCmsForm: {}, @@ -494,7 +497,16 @@ layout("/layouts/platform.html"){ methods: { // 搜索处理 handleSearch(value) { - this.$refs.doMenuTree.filter(value); + // 弹框初次渲染完成后再执行筛选,避免树实例尚未创建。 + this.$nextTick(() => { + this.$refs.doMenuTree.filter(value) + }) + }, + + // 更新单个权限的勾选状态,避免搜索隐藏的权限在保存时丢失。 + handleMenuCheck(data, checked) { + const isChecked = checked.checkedKeys.indexOf(data.id) !== -1 + this.$set(this.menuCheckedMap, data.id, isChecked) }, // 过滤节点方法 @@ -578,8 +590,15 @@ layout("/layouts/platform.html"){ doMenuLoad() { this.$axios.post("/platform/sys/role/menuRole/" + this.roleId + "/" + this.platform, {}).then((res) => { if (res.code === 0) { - this.doMenuData = res.data.menu - this.doMenuCheckedData = res.data.cmenu + const menuCheckedMap = {} + res.data.cmenu.forEach((menuId) => { + menuCheckedMap[menuId] = true + }) + this.$set(this, "doMenuData", res.data.menu) + this.$set(this, "doMenuCheckedData", res.data.cmenu) + this.$set(this, "menuCheckedMap", menuCheckedMap) + // 权限树数据刷新后清空上次搜索产生的筛选状态。 + this.handleSearch(this.searchText) } }) }, @@ -669,7 +688,10 @@ layout("/layouts/platform.html"){ // return; // } - const ids = this.$refs["doMenuTree"].getCheckedKeys() + // 从完整权限树的状态中组装提交数据,搜索隐藏节点不会被遗漏。 + const allMenuIds = [] + this.getTreeAllIds(allMenuIds, this.doMenuData) + const ids = allMenuIds.filter((menuId) => this.menuCheckedMap[menuId]) if (!ids || ids.length === 0) { this.$message.warning("请选择菜单或数据权限") return @@ -721,6 +743,7 @@ layout("/layouts/platform.html"){ this.menuDialogVisible = true this.platform = "PC" this.roleId = command.id + this.$set(this, "searchText", "") this.doMenuLoad() } if ("h5_menu" === command.type) { @@ -728,6 +751,7 @@ layout("/layouts/platform.html"){ this.menuDialogVisible = true this.platform = "H5" this.roleId = command.id + this.$set(this, "searchText", "") this.doMenuLoad() } @@ -837,9 +861,15 @@ layout("/layouts/platform.html"){ menuRoleSelAll() { const ids = [] this.getTreeAllIds(ids, this.doMenuData) + const menuCheckedMap = {} + ids.forEach((menuId) => { + menuCheckedMap[menuId] = true + }) + this.$set(this, "menuCheckedMap", menuCheckedMap) this.$refs["doMenuTree"].setCheckedKeys(ids) }, menuRoleSelClear() { + this.$set(this, "menuCheckedMap", {}) this.$refs["doMenuTree"].setCheckedKeys([]) }, clearButtons(list) { diff --git a/src/main/resources/views/platform/sys/union/branchUnionUserManage.js b/src/main/resources/views/platform/sys/union/branchUnionUserManage.js index 87e1ce61..81e4fffc 100644 --- a/src/main/resources/views/platform/sys/union/branchUnionUserManage.js +++ b/src/main/resources/views/platform/sys/union/branchUnionUserManage.js @@ -75,7 +75,14 @@ const branchUnionUserManage = { - + + + + + + + + @@ -138,6 +145,8 @@ const branchUnionUserManage = { }, dialogFormVisible: false, formData: {}, + roleOptions: [], + partySecretaryUnitOptions: [], leaveDialogVisible: false, leaveForm: { id: "", @@ -200,6 +209,32 @@ const branchUnionUserManage = { isLeft(row) { return row.isServing === false || row.isServing === 0 || row.displayStatus === "离任" }, + isUnitPartySecretary() { + return this.formData.roleCode === "UNIT_PARTY_SECRETARY" + }, + loadRoleOptions() { + return $.get("/platform/sys/union/branchUnionRoleOptions").then((res) => { + if (res.code === 0) { + this.$set(this, "roleOptions", res.data || []) + } + }) + }, + loadPartySecretaryUnitOptions() { + return $.get("/platform/sys/union/branchUnionPartySecretaryUnitOptions", { + unionId: this.union_id + }).then((res) => { + if (res.code === 0) { + this.$set(this, "partySecretaryUnitOptions", res.data || []) + } + }) + }, + handleRoleChange() { + if (this.isUnitPartySecretary()) { + this.loadPartySecretaryUnitOptions() + return + } + this.$set(this.formData, "unitIds", []) + }, openAdd() { this.dialogFormVisible = true this.$nextTick(() => { @@ -218,8 +253,16 @@ const branchUnionUserManage = { this.$message.error("请选择届数") return } - $.post("/platform/sys/union/insertBranchUnionUserRole", { + if (this.isUnitPartySecretary() && (!this.formData.unitIds || this.formData.unitIds.length === 0)) { + this.$message.error("请选择所属单位") + return + } + const submitUrl = this.isUnitPartySecretary() + ? "/platform/sys/union/insertBranchUnionPartySecretaryRole" + : "/platform/sys/union/insertBranchUnionUserRole" + $.post(submitUrl, { ...this.formData, + unitIds: JSON.stringify(this.formData.unitIds || []), unionId: this.union_id }).then((res) => { if (res.code === 0) { @@ -279,6 +322,7 @@ const branchUnionUserManage = { } }, created() { + this.loadRoleOptions() this.initJSearch() }, style: /*language=CSS*/ ` diff --git a/src/main/resources/views/platform/zhgh/activity/sports/ActivityResults/index.html b/src/main/resources/views/platform/zhgh/activity/sports/ActivityResults/index.html index d885c3fe..1255c3eb 100644 --- a/src/main/resources/views/platform/zhgh/activity/sports/ActivityResults/index.html +++ b/src/main/resources/views/platform/zhgh/activity/sports/ActivityResults/index.html @@ -780,13 +780,13 @@ layout("/layouts/platform.html"){ return data; }, - async getUnionList(row) { - const {activityId, eventId} = row - const {data} = await this.$axios.post(loc() + "/getUnionList", { - activityId: activityId, - eventId: eventId + async getUnionList() { + // 团体成绩可录入任意分工会,使用全量分工会列表而非项目报名分工会。 + const unionList = await this.$businessTool.listUnion() + return unionList.map(item => { + this.$set(item, "unionname", item.name) + return item }) - return data; }, openAddUser() { this.userData.push({}) diff --git a/src/main/resources/views/platform/zhgh/club/evaluate/clubAudit/index.html b/src/main/resources/views/platform/zhgh/club/evaluate/clubAudit/index.html index 318fa13d..8023caac 100644 --- a/src/main/resources/views/platform/zhgh/club/evaluate/clubAudit/index.html +++ b/src/main/resources/views/platform/zhgh/club/evaluate/clubAudit/index.html @@ -73,7 +73,7 @@ layout("/layouts/platform.html"){
- {{formData.taskName}} + 会长审核
diff --git a/src/main/resources/views/platform/zhgh/club/evaluate/common/evaluateInfo.js b/src/main/resources/views/platform/zhgh/club/evaluate/common/evaluateInfo.js index 20bfb23a..a719146e 100644 --- a/src/main/resources/views/platform/zhgh/club/evaluate/common/evaluateInfo.js +++ b/src/main/resources/views/platform/zhgh/club/evaluate/common/evaluateInfo.js @@ -29,7 +29,7 @@ const EVALUATE_INFO_COMPONENT = {