This commit is contained in:
=
2026-08-05 08:35:17 +08:00
parent ff3e8075b5
commit de5f8dc9ba
55 changed files with 1667 additions and 449 deletions
@@ -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<String> branchUnionRoleCodes = branchUnionRoles.stream().map(Sys_dict::getCode).toList();
List<String> 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<String> branchUnionRoleCodes = branchUnionRoles.stream().map(Sys_dict::getCode).toList();
List<String> 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<NutMap> 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<Sys_unit> 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<String> distinctUnitIds = java.util.Arrays.stream(unitIds)
.filter(StrUtil::isNotBlank)
.distinct()
.toList();
List<Sys_unit> 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<ProcessTask> 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<String> getBranchUnionRoleCodes(List<Sys_dict> branchUnionRoles) {
List<String> 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("分工会组成单位分页")
@@ -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<String> 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();
@@ -148,6 +148,11 @@ public class SysRoleServiceImpl extends BaseServiceImpl<Sys_role> implements Sys
@Override
@Aop(TransAop.READ_COMMITTED)
public void saveMenu(String[] menuIds, String roleId, String platform) {
// 保存前确认权限均属于当前平台,防止PC端与H5端权限混写。
List<Sys_menu> menus = sysMenuService.query(Cnd.where("id", "in", menuIds).and("platform", "=", platform));
if (menus.size() != menuIds.length) {
throw new BaseException("存在不属于当前平台的权限,无法保存");
}
//只清除对应平台的即可
Sql sql = Sqls.queryString("""
SELECT
@@ -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));
@@ -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");
@@ -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);
}
@@ -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<Activity_works_subjectType> persistedSubjectTypes = dao.query(Activity_works_subjectType.class,
Cnd.where(Activity_works_subjectType::getActivityId, "=", worksCollection.getId()));
List<Activity_works_subjectType> submittedSubjectTypes = CollUtil.defaultIfEmpty(worksCollection.getSubjectTypes(), Collections.emptyList());
Set<String> submittedSubjectIds = submittedSubjectTypes.stream()
.map(Activity_works_subjectType::getId)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet());
List<Activity_works_subjectType> 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<Activity_works_worksType> persistedWorksTypes = dao.query(Activity_works_worksType.class,
Cnd.where(Activity_works_worksType::getSubjectId, "=", submittedSubjectType.getId()));
List<Activity_works_worksType> submittedWorksTypes = CollUtil.defaultIfEmpty(submittedSubjectType.getWorksTypes(), Collections.emptyList());
Set<String> submittedWorksIds = submittedWorksTypes.stream()
.map(Activity_works_worksType::getId)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet());
List<Activity_works_worksType> 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<Activity_works_subjectType> deletedSubjectTypes) {
if (CollUtil.isEmpty(deletedSubjectTypes)) {
return null;
}
List<String> deletedSubjectIds = deletedSubjectTypes.stream()
.map(Activity_works_subjectType::getId)
.collect(Collectors.toList());
List<Activity_works_collection_upload> 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<String> 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<Activity_works_worksType> deletedWorksTypes) {
if (CollUtil.isEmpty(deletedWorksTypes)) {
return null;
}
List<String> deletedWorksIds = deletedWorksTypes.stream()
.map(Activity_works_worksType::getId)
.collect(Collectors.toList());
List<Activity_works_collection_upload> 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<String> 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 + "”下已有投稿,不能删除";
}
}
@@ -200,6 +200,7 @@ public class ClubInfoManageController {
@SLog(tag = "社团管理系统-信息管理", msg = "修改身份")
public Result updateRoleCode(@Param("id") String id, @Valid String[] roleCodes, @Valid String clubId) {
List<String> 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()));
// 再根据传过来的赋值
@@ -291,7 +291,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
@Override
public List<NutMap> getJgUser(String clubId) {
// 构建社团管理人员查询SQL(排除普通成员,按职务排序)
// 构建社团员查询SQL,理事机构成员优先展示,普通成员随后展示。
Sql sql = Sqls.create("""
SELECT
cl.*,
@@ -304,7 +304,6 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
LEFT JOIN `vw_user` u ON cl.userId = u.id
WHERE
cl.clubId = @clubId
AND NOT JSON_CONTAINS(cl.roleCode, '"CLUB_MEMBER"')
ORDER BY
CASE
WHEN JSON_CONTAINS(cl.roleCode, '"CLUB_PRESIDENT"') THEN 1
@@ -480,6 +479,35 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
List<SysClubExamineRegisterDetailed> 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;
}
@@ -86,7 +86,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
scu.clubId = c.id
) currentNum,
GROUP_CONCAT(DISTINCT presidentUser.username) AS clubLeader,
GROUP_CONCAT(secretaryUser.username) AS clubSecretary
GROUP_CONCAT(DISTINCT secretaryUser.username) AS clubSecretary
FROM
sys_club c
LEFT JOIN club_user presidentCu on presidentCu.clubId = c.id AND JSON_CONTAINS(presidentCu.roleCode, '"CLUB_PRESIDENT"')
@@ -229,23 +229,19 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
public Pagination<ClubUserCommonPageVo> 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<ClubCommonPage
u.userState,
club.clubName,
u.unitname AS unitName,
COALESCE(scu.joinTime, DATE_FORMAT(club.foundTime, '%Y-%m-%d %H:%i:%s')) AS joinTime,
COALESCE(
DATE_FORMAT(info.exitTime, '%Y-%m-%d %H:%i:%s'),
JSON_UNQUOTE(JSON_EXTRACT(scu.exitAuditList, '$[0].auditTime')),
scu.changeTime
) AS exitTime,
IF(info.id IS NULL, 'DIRECT_REMOVE', 'AUDIT_EXIT') AS exitType,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId
FROM
sys_club_user scu
LEFT JOIN club_user_apply info ON info.clubId = scu.clubId
AND info.userId = scu.userId
AND info.mode = false
LEFT JOIN sys_club club ON club.id = scu.clubId
INNER JOIN vw_user u ON u.id = scu.userId
LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id
AND ins.state = 20
DATE_FORMAT(COALESCE(exitInfo.joinTime, STR_TO_DATE(club.foundTime, '%Y-%m-%d')), '%Y-%m-%d %H:%i:%s') AS joinTime,
DATE_FORMAT(exitInfo.exitTime, '%Y-%m-%d %H:%i:%s') AS exitTime,
exitInfo.exitType
""" + getExitManageBaseSql() + """
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("scu.isNormal", "=", false);
cnd.andEX("scu.clubId", "=", pageForm.getClubId());
cnd.andEX("exitInfo.clubId", "=", pageForm.getClubId());
cnd.andEX("u.personType", "=", pageForm.getPersonType());
cnd.andEX("u.userState", "=", pageForm.getUserState());
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
@@ -287,9 +265,9 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name())) {
List<String> clubIdList = commonService.findUserRoleByRoleCode(List.of(RoleConstant.CLUB_PRESIDENT.name(), RoleConstant.CLUB_VICE_PRESIDENT.name(), RoleConstant.CLUB_SECRETARY.name(), RoleConstant.CLUB_VICE_SECRETARY.name(), RoleConstant.CLUB_OPERATOR.name()));
List<Sys_user_role> clubList = dao().query(Sys_user_role.class, Cnd.where("roleId", "in", clubIdList).and("userId", "=", SecurityUtil.getUserId()).and("clubId", "is not", null));
cnd.and("scu.clubId", "in", clubList.stream().map(Sys_user_role::getClubId).toList());
cnd.and("exitInfo.clubId", "in", clubList.stream().map(Sys_user_role::getClubId).toList());
}
cnd.groupBy("scu.id");
cnd.groupBy("exitInfo.id");
cnd.desc("exitTime");
sql.setCondition(cnd);
Pagination<ClubUserCommonPageVo> pagination = listPageVO(pageForm, sql, ClubUserCommonPageVo.class);
@@ -307,55 +285,110 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
public ClubUserJoinVo exitManageInfo(String id) {
Sql sql = Sqls.create("""
SELECT
COALESCE(cua.id, scu.id) AS id,
scu.clubId,
scu.userId,
COALESCE(cua.roleCode, scu.roleCode) AS roleCode,
COALESCE(cua.clubPosition, scu.position) AS clubPosition,
COALESCE(cua.email, scu.email, u.email) AS email,
COALESCE(cua.mobile, u.mobile) AS mobile,
COALESCE(cua.birthday, u.birthday) AS birthday,
COALESCE(cua.avatar, scu.avatar, u.avatar) AS avatar,
COALESCE(cua.sameTimeJoinOtherClubSituation, scu.sameTimeJoinOtherClubSituation) AS sameTimeJoinOtherClubSituation,
COALESCE(cua.awardsExperience, scu.awardsExperience) AS awardsExperience,
cua.signature,
COALESCE(
cua.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,
STR_TO_DATE(scu.joinTime, '%Y-%m-%d %H:%i:%s') AS joinTime,
COALESCE(
cua.exitTime,
STR_TO_DATE(JSON_UNQUOTE(JSON_EXTRACT(scu.exitAuditList, '$[0].auditTime')), '%Y-%m-%d %H:%i:%s'),
STR_TO_DATE(scu.changeTime, '%Y-%m-%d %H:%i:%s')
) AS exitTime,
IF(cua.id IS NULL, 'DIRECT_REMOVE', 'AUDIT_EXIT') AS exitType,
exitInfo.id,
exitInfo.clubId,
exitInfo.userId,
exitInfo.applyRoleCode AS roleCode,
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,
COALESCE(exitInfo.joinTime, STR_TO_DATE(club.foundTime, '%Y-%m-%d')) AS joinTime,
exitInfo.exitTime,
exitInfo.exitType,
club.clubName,
u.loginname AS loginName,
u.username AS userName,
u.sex,
u.mobile,
u.unitName,
u.unionName,
u.technicalTitle,
u.education,
u.academicDegree,
u.position
FROM
sys_club_user scu
LEFT JOIN club_user_apply cua ON cua.clubId = scu.clubId
AND cua.userId = scu.userId
AND cua.mode = false
LEFT JOIN vw_user u ON u.id = scu.userId
LEFT JOIN sys_club club ON club.id = scu.clubId
WHERE scu.isNormal = false
AND COALESCE(cua.id, scu.id) = @id
""" + getExitManageBaseSql() + """
WHERE exitInfo.id = @id
""");
sql.setParam("id", id);
return fetchVO(sql, ClubUserJoinVo.class);
}
/**
* 退会申请审批完成后会删除club_user成员关系,因此退会管理以退会申请记录为主数据源。
* 同时保留旧sys_club_user表中已退会的数据,避免历史直接移出记录无法查询。
*
* @return 退会管理列表和详情共用的数据来源SQL
*/
private String getExitManageBaseSql() {
return """
FROM (
SELECT
cua.id,
cua.clubId,
cua.userId,
cua.roleCode AS applyRoleCode,
cua.clubPosition,
cua.email,
cua.mobile,
cua.birthday,
cua.avatar,
cua.sameTimeJoinOtherClubSituation,
cua.awardsExperience,
cua.signature,
cua.applyDate,
cua.joinTime,
cua.exitTime,
'AUDIT_EXIT' AS exitType
FROM
club_user_apply cua
INNER JOIN wf_process_instance ins ON ins.businessNo = cua.id AND ins.state = 20
WHERE cua.mode = false
UNION ALL
SELECT
scu.id,
scu.clubId,
scu.userId,
scu.roleCode AS applyRoleCode,
scu.position AS clubPosition,
scu.email,
NULL AS mobile,
NULL AS birthday,
scu.avatar,
scu.sameTimeJoinOtherClubSituation,
scu.awardsExperience,
NULL AS signature,
COALESCE(
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,
STR_TO_DATE(scu.joinTime, '%Y-%m-%d %H:%i:%s') AS joinTime,
COALESCE(
STR_TO_DATE(JSON_UNQUOTE(JSON_EXTRACT(scu.exitAuditList, '$[0].auditTime')), '%Y-%m-%d %H:%i:%s'),
STR_TO_DATE(scu.changeTime, '%Y-%m-%d %H:%i:%s')
) AS exitTime,
'DIRECT_REMOVE' AS exitType
FROM
sys_club_user scu
WHERE scu.isNormal = false
AND NOT EXISTS (
SELECT 1
FROM club_user_apply cua
INNER JOIN wf_process_instance ins ON ins.businessNo = cua.id AND ins.state = 20
WHERE cua.mode = false
AND cua.clubId = scu.clubId
AND cua.userId = scu.userId
)
) exitInfo
LEFT JOIN sys_club club ON club.id = exitInfo.clubId
INNER JOIN vw_user u ON u.id = exitInfo.userId
""";
}
@Override
public Pagination<ClubUserCommonPageVo> clubManagePersonAuditPageData(ClubUserPageForm pageForm) {
Sql sql = Sqls.create("""
@@ -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<BpmTaskApprovalRecordVo> nodeTasks;
/**
* V3年审已迁入但未转换为V4工作流任务的历史审核记录。
*/
private List<NutMap> legacyAuditRecords;
}
@@ -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);
@@ -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));
}
@@ -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);
@@ -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);
@@ -10,4 +10,12 @@ public interface EvaluateService extends BaseService<EvaluateApply> {
Sql getSummarySql(EvaluatePageForm pageForm);
/**
* 校验集体荣誉是否已由当前分工会申请
*
* @param evaluateApply 待保存或提交的申请
* @return 校验通过返回 {@code null}重复申请时返回提示信息
*/
String validateCollectiveHonorApply(EvaluateApply evaluateApply);
}
@@ -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<EvaluateApply> 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<EvaluateApply> 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;
}
}
@@ -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
@@ -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<NutMap> todoTasks = processDefineService.listMap(todoSql);
Map<String, NutMap> 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);
@@ -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<String, String> 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<NutMap> 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<NutMap> list = (List<NutMap>) 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);
}
@@ -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<Sys_dict> 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<String, String> summaryOrderColumns = new HashMap<>(DELEGATION_SUMMARY_ORDER_COLUMNS);
List<String> 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));
@@ -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();
}
@@ -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)
@@ -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)
@@ -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<ProposalInfo> {
* 修改提案状态码
*/
void updateStateCode(String id, Integer stateCode);
/**
* 查询提案征集进度列表
* 查询结果与页面既有筛选排序语义保持一致内部使用聚合关联避免逐行执行统计子查询
*
* @param pageForm 征集进度页面的筛选排序和分页参数
* @return 包含征集进度统计字段的分页结果
*/
Pagination queryCollectProgress(ProposalQueryComprehensiveParam pageForm);
/**
* 按提案去重统计指定届次各流程节点的待办和已办数量
*
* @param sessionId 教代会届次ID
* @return 键为流程节点编码值为待办和已办数量的统计结果
*/
Map<String, NutMap> countDashboardTaskProposals(String sessionId);
}
@@ -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<ProposalInfo> imp
/** 办理页面允许导出的列表字段,避免请求参数携带非页面字段。 */
private static final Map<String, Set<String>> WORKFLOW_EXPORT_COLUMNS = createWorkflowExportColumns();
/** 征集进度页面允许排序的字段,避免排序参数直接拼接到SQL。 */
private static final Map<String, String> 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 = "<img\\s+[^>]*src\\s*=\\s*([\"'])(/platform/sys/file/download\\?id=.*?)\\1[^>]*>";
@@ -107,6 +122,128 @@ public class ProposalCommonServiceImpl extends BaseServiceImpl<ProposalInfo> 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<String, NutMap> 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<NutMap> 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<ProposalInfo> 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<ProposalInfo> 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();
@@ -82,12 +82,12 @@ public class ProposalCommitteeFilingUnitServiceImpl extends BaseServiceImpl<Prop
throw new BaseException("提案信息不存在");
}
args.put("proposalId", proposalId);
validateAndNormalizeCaseFilingCode(args);
List<String> 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<Prop
}
/**
* 校验并规范化立案编号确定立案时必须传入不超过 20 个字符的编号
* 校验并规范化立案编号确定立案时必须传入不超过 20 个字符且不与历史立案编号重复的编号
* 当前提案及并案提案不参与重复校验保证流程回退后可按原编号重新提交
* 其他立案结果不保存编号避免切换选项后把历史输入带入业务表和流程变量
*
* @param args 流程提交参数其中 tf_caseFilingResult 为立案结果tf_caseFilingCode 为手工填写的立案编号
* @param excludedProposalIds 当前办理的提案及并案提案主键不参与历史重复判断
*/
private void validateAndNormalizeCaseFilingCode(Dict args) {
private void validateAndNormalizeCaseFilingCode(Dict args, List<String> 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<Prop
if (caseFilingCode.length() > 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);
}
@@ -654,9 +654,12 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> 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<ProposalInfo> 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<String, Integer> 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<NutMap> list = listMap(sql);
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(bos);
List<NutMap> list = queryProposalBaseList(pageForm);
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(bos)) {
Map<String, Integer> 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<ProposalInfo> 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<NutMap> list = listMap(sql);
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(bos);
List<NutMap> list = queryProposalBaseList(pageForm);
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(bos)) {
Map<String, Integer> 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());
}
}
@@ -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<Teacher_congress_institution_user> implements TeacherCongressInstitutionUserService {
@Inject
@@ -49,21 +51,9 @@ public class TeacherCongressInstitutionUserServiceImpl extends BaseServiceImpl<T
dao().insert(institutionUser);
Teacher_congress_institution institution = dao().fetch(Teacher_congress_institution.class, institutionId);
if (institution.getCode().contains("TEACHER_CONGRESS_INSTITUTION_PROPOSAL_COMMITTEE") && identity.equals("主任")) {
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DIRECTOR);
int existsRole = dao().count(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", userId)
.and(Sys_user_role::getTcSessionId, "=", sessionId)
.and(Sys_user_role::getRoleId, "=", sysRole.getId())
);
if (existsRole == 0) {
Sys_user_role insertSysUserRole = new Sys_user_role();
insertSysUserRole.setRoleId(sysRole.getId());
insertSysUserRole.setUserId(userId);
insertSysUserRole.setTcSessionId(sessionId);
dao().insert(insertSysUserRole);
sysRoleService.clearCache();
sysUserService.clearCache();
}
// 提案审查委员会主任按届次同步提案委员会主任角色兼容历史届次的旧机构编码
if (isProposalReviewCommittee(institution) && identity.equals("主任")) {
grantProposalCommitteeDirectorRole(userId, sessionId);
}
if (institution.getCode().contains("TEACHER_CONGRESS_INSTITUTION_PROPOSAL_COMMITTEE") && identity.equals("副主任")) {
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DEPUTY_DIRECTOR);
@@ -135,15 +125,8 @@ public class TeacherCongressInstitutionUserServiceImpl extends BaseServiceImpl<T
Teacher_congress_institution_user institutionUser = fetch(id);
String institutionId = institutionUser.getInstitutionId();
Teacher_congress_institution institution = dao().fetch(Teacher_congress_institution.class, institutionId);
if (institution.getName().contains("提案工作委员会") && institutionUser.getIdentity().equals("主任")) {
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DIRECTOR);
dao().clear(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", institutionUser.getUserId())
.and(Sys_user_role::getTcSessionId, "=", institutionUser.getSessionId())
.and(Sys_user_role::getRoleId, "=", sysRole.getId())
);
sysRoleService.clearCache();
sysUserService.clearCache();
}
boolean proposalReviewCommitteeDirector = isProposalReviewCommittee(institution)
&& institutionUser.getIdentity().equals("主任");
if (institution.getName().contains("提案工作委员会") && institutionUser.getIdentity().equals("副主任")) {
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DEPUTY_DIRECTOR);
dao().clear(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", institutionUser.getUserId())
@@ -173,5 +156,78 @@ public class TeacherCongressInstitutionUserServiceImpl extends BaseServiceImpl<T
sysUserService.clearCache();
}
dao().delete(institutionUser);
// 删除主任后仅在同届次不存在其他提案审查委员会主任时撤销系统角色
if (proposalReviewCommitteeDirector) {
revokeProposalCommitteeDirectorRoleIfUnused(institutionUser.getUserId(), institutionUser.getSessionId());
}
}
/**
* 判断机构是否为提案审查委员会ZXT001 为当前机构配置编码旧编码用于兼容历史届次
*
* @param institution 教代会机构
* @return 是否属于提案审查委员会
*/
private boolean isProposalReviewCommittee(Teacher_congress_institution institution) {
if (institution == null) {
return false;
}
return "ZXT001".equals(institution.getCode())
|| "TEACHER_CONGRESS_INSTITUTION_PROPOSAL_COMMITTEE".equals(institution.getCode());
}
/**
* 为提案审查委员会主任授予当前届次的提案委员会主任角色已有角色不重复写入
*
* @param userId 用户主键
* @param sessionId 教代会届次主键
*/
private void grantProposalCommitteeDirectorRole(String userId, String sessionId) {
Sys_role sysRole = sysRoleService.getByCode(RoleConstant.PROPOSAL_COMMITTEE_DIRECTOR);
if (sysRole == null) {
throw new BaseException("提案委员会主任角色不存在");
}
int existsRole = dao().count(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", userId)
.and(Sys_user_role::getTcSessionId, "=", sessionId)
.and(Sys_user_role::getRoleId, "=", sysRole.getId())
);
if (existsRole > 0) {
return;
}
Sys_user_role insertSysUserRole = new Sys_user_role();
insertSysUserRole.setRoleId(sysRole.getId());
insertSysUserRole.setUserId(userId);
insertSysUserRole.setTcSessionId(sessionId);
dao().insert(insertSysUserRole);
sysRoleService.clearCache();
sysUserService.clearCache();
}
/**
* 当用户在当前届次不再担任任何提案审查委员会主任时撤销对应系统角色
*
* @param userId 用户主键
* @param sessionId 教代会届次主键
*/
private void revokeProposalCommitteeDirectorRoleIfUnused(String userId, String sessionId) {
List<Teacher_congress_institution_user> institutionUsers = dao().query(Teacher_congress_institution_user.class,
Cnd.where(Teacher_congress_institution_user::getUserId, "=", userId)
.and(Teacher_congress_institution_user::getSessionId, "=", sessionId)
.and(Teacher_congress_institution_user::getIdentity, "=", "主任"));
boolean stillDirector = institutionUsers.stream()
.map(institutionUser -> 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();
}
}
@@ -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("分页查询非福利会员")
@@ -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) {
@@ -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;
}
@@ -33,6 +33,30 @@ public interface WelfareListService extends BaseService<WelfareList> {
*/
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
@@ -17,6 +17,13 @@ public interface WelfareProjectService extends BaseService<WelfareProject> {
*/
WelfareProject projectInfo(String projectId);
/**
* 查询可用于沿用配置的历史福利项目
*
* @return 历史福利项目列表
*/
List<WelfareProject> listHistoryProject();
/**
* 校验福利选择是否保留系统默认福利
*
@@ -44,6 +51,13 @@ public interface WelfareProjectService extends BaseService<WelfareProject> {
*/
void deleteWelfare(String id);
/**
* 清空指定福利项目的名单及用户已选福利记录保留项目配置
*
* @param projectId 福利项目 ID
*/
void clearWelfareList(String projectId);
/**
* 创建福利名单
* @param projectId
@@ -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<WelfareList> 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<NutMap> welfareLists = listMap(sql);
if (welfareLists.isEmpty()) {
return 0;
}
List<String> welfareListIds = welfareLists.stream().map(item -> item.getString("id")).collect(Collectors.toList());
List<String> 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<WelfareList> 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<WelfareList> 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<WelfareList> 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<WelfareList> 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));
// 设置导出参数
@@ -49,6 +49,11 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> i
return project;
}
@Override
public List<WelfareProject> listHistoryProject() {
return dao().query(WelfareProject.class, Cnd.NEW().desc(WelfareProject::getYear).desc(WelfareProject::getChoiceTimeStart));
}
@Override
public String validateSystemDefaultOptionSelection(String projectId, WelfareUserSelection[] selections) {
List<WelfareProjectSubjectOption> systemDefaultOptions = dao().query(WelfareProjectSubjectOption.class,
@@ -164,6 +169,14 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> 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);