Compare commits

...
Author SHA1 Message Date
c-chenxs1 4567ad5728 feat: 测试数据 2026-11-13 18:45:27 +08:00
c-chenxs1 d5077d092c feat: 定时任务失效优化,并补充功能 2026-11-13 17:56:58 +08:00
c-chenxs1 63fc01cbb3 feat: 信息查询加姓名查询 2026-11-12 10:23:43 +08:00
c-chenxs1 902ad7e0e9 feat: 社团统一改为协会 2026-11-11 18:21:26 +08:00
c-chenxs1 651435d568 feat: 职工福利优化3 2026-11-11 17:59:50 +08:00
c-chenxs1 2ee171b4f3 feat: 职工福利优化2 2026-11-11 17:55:30 +08:00
c-chenxs1 a7a7aa5e3a feat: 职工福利优化 2026-11-11 17:39:15 +08:00
c-chenxs1 336bea15bc feat: 普惠疗休养优化6 2026-11-11 09:35:29 +08:00
c-chenxs1 49237dc2ea feat: 普惠疗休养优化5 2026-11-10 20:48:58 +08:00
c-chenxs1 faafb72eee feat: 普惠疗休养优化4 2026-11-10 14:29:19 +08:00
c-chenxs1 e4e856f6b2 feat: 普惠疗休养优化3 2026-11-10 11:45:05 +08:00
c-chenxs1 933daac134 feat: 普惠疗休养优化2 2026-11-09 18:09:05 +08:00
c-chenxs1 803333feac feat: 普惠疗休养优化 2026-11-09 11:42:38 +08:00
c-chenxs1 22a18986d0 feat: 工会报销优化2 2026-11-06 18:35:51 +08:00
c-chenxs1 d6e3815b3d feat: 工会报销优化 2026-11-06 18:08:21 +08:00
c-chenxs1 1ddfa7e609 feat: 经费预算管理2 2026-11-05 19:39:22 +08:00
c-chenxs1 e7fbf4021e feat: 经费预算管理 2026-11-05 19:23:13 +08:00
c-chenxs1 c21e92e418 feat: 疗休养UI优化2 2026-11-05 17:42:34 +08:00
c-chenxs1 134a11da09 feat: 疗休养UI优化 2026-11-05 16:17:06 +08:00
c-chenxs1 506121ac5f feat: 疗休养优化站内消息发送 2026-11-05 10:23:11 +08:00
c-chenxs1 8c401a5132 feat: 疗休养优化布局 2026-11-05 10:09:39 +08:00
c-chenxs1 4d9fd57d6c feat: 疗休养优化移动端 2026-11-05 08:37:27 +08:00
c-chenxs1 fd117b4fc3 feat: 疗休养优化2 2026-11-04 18:12:51 +08:00
c-chenxs1 060e8d5bc7 feat: 疗休养优化 2026-11-04 17:11:14 +08:00
c-chenxs1 894c720bb8 feat: 优化福利2 2026-11-03 20:35:12 +08:00
c-chenxs1 39ee74f0a9 feat: 优化福利 2026-11-03 20:20:59 +08:00
c-chenxs1 7faa2730b5 feat: 同步人员时按人员类型或在职状态设置工会会员标记 2026-11-03 17:41:57 +08:00
c-chenxs1 fa019786d6 feat: 完善人员数据同步与筛选
- 在人员来源表中保存专业技术职务代码 ZYJSZWDM
- 支持按所属单位、在职状态和人员类型筛选空数据
- 在获取信息中心数据和更新系统用户数据页面添加搜索重置按钮
- 同步人员时按人员类型或在职状态设置工会会员标记
2026-11-03 17:30:29 +08:00
c-chenxs1 ffe782e538 feat: 人员映射 2026-11-03 15:06:04 +08:00
c-chenxs1 3c65c9d561 feat: 同步职工慰问和困难帮扶 2026-11-02 18:12:23 +08:00
c-chenxs1 89a77c41af Merge branch 'master' into feat_本工会单独业务 2026-11-02 11:30:59 +08:00
c-chenxs1 f51b24686f Merge branch 'feat_可同步长江大学的优化_fix' 2026-11-02 11:30:17 +08:00
c-chenxs1 0f460e6b1a feat: 优化人员拉取和数据字典部分4 2026-11-02 11:22:39 +08:00
c-chenxs1 d1050db6b3 feat: 优化人员拉取和数据字典部分3 2026-10-29 16:14:08 +08:00
c-chenxs1 e3f32fe72b feat: 优化人员拉取和数据字典部分2 2026-10-29 15:52:02 +08:00
c-chenxs1 b885f449c7 feat: 优化人员拉取和数据字典部分 2026-10-29 15:29:55 +08:00
c-chenxs1 e944ebf85f Merge branch 'feat_本工会单独业务' 2026-10-28 17:58:29 +08:00
c-chenxs1 d5f1c642c4 Merge branch 'feat_可同步长江大学的优化_fix' 2026-10-28 15:21:46 +08:00
427 changed files with 9874 additions and 7155 deletions
+4
View File
@@ -50,3 +50,7 @@ Thumbs.db
.trae/update-docs/SKILL.md .trae/update-docs/SKILL.md
.trae/webapp-testing/SKILL.md .trae/webapp-testing/SKILL.md
outputs outputs
semgrep.json
trivy.json
.workbuddy
+12 -12
View File
@@ -5,6 +5,7 @@ import cn.dev33.satoken.config.SaTokenConfig;
import com.budwk.app.base.utils.AppIocUtil; import com.budwk.app.base.utils.AppIocUtil;
import com.budwk.app.flow.engine.FlowEngine; import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.sys.services.SysTaskService; import com.budwk.app.sys.services.SysTaskService;
import com.budwk.app.task.log.TaskExecutionListener;
import com.budwk.app.task.services.TaskPlatformService; import com.budwk.app.task.services.TaskPlatformService;
import com.budwk.app.web.commons.auth.satoken.SaTokenContextImpl; import com.budwk.app.web.commons.auth.satoken.SaTokenContextImpl;
import com.budwk.app.web.commons.auth.satoken.SaTokenDaoRedisImpl; import com.budwk.app.web.commons.auth.satoken.SaTokenDaoRedisImpl;
@@ -20,7 +21,6 @@ import org.beetl.core.GroupTemplate;
import org.nutz.boot.NbApp; import org.nutz.boot.NbApp;
import org.nutz.dao.Dao; import org.nutz.dao.Dao;
import org.nutz.dao.impl.FileSqlManager; import org.nutz.dao.impl.FileSqlManager;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos; import org.nutz.dao.util.Daos;
import org.nutz.integration.jedis.JedisAgent; import org.nutz.integration.jedis.JedisAgent;
import org.nutz.ioc.Ioc; import org.nutz.ioc.Ioc;
@@ -31,6 +31,7 @@ import org.nutz.lang.Mirror;
import org.nutz.mvc.Mvcs; import org.nutz.mvc.Mvcs;
import org.nutz.mvc.annotation.*; import org.nutz.mvc.annotation.*;
import org.quartz.Scheduler; import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import javax.management.MBeanServer; import javax.management.MBeanServer;
import javax.management.ObjectName; import javax.management.ObjectName;
@@ -39,7 +40,6 @@ import java.lang.management.ManagementFactory;
import java.sql.Driver; import java.sql.Driver;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List;
@IocBean(create = "init", depose = "depose") @IocBean(create = "init", depose = "depose")
@Localization(value = "locales/", defaultLocalizationKey = "zh_CN") @Localization(value = "locales/", defaultLocalizationKey = "zh_CN")
@@ -82,6 +82,7 @@ public class MainLauncher {
Mvcs.DISABLE_X_POWERED_BY = true; Mvcs.DISABLE_X_POWERED_BY = true;
Globals.AppBase = Mvcs.getServletContext().getContextPath(); Globals.AppBase = Mvcs.getServletContext().getContextPath();
Globals.AppRoot = Mvcs.getServletContext().getRealPath("/"); Globals.AppRoot = Mvcs.getServletContext().getRealPath("/");
init_task();
init_sys(); init_sys();
ioc.get(com.budwk.app.zhgh.staffbenefit.therapyRecuperation.TherapyRecuperationModule.class); ioc.get(com.budwk.app.zhgh.staffbenefit.therapyRecuperation.TherapyRecuperationModule.class);
// 2026年10月3日 12:01:28;操作人:wxg:先升级会员表及补齐历史资料,再处理缴费快照和启动计划任务;不依赖 DEBUG。 // 2026年10月3日 12:01:28;操作人:wxg:先升级会员表及补齐历史资料,再处理缴费快照和启动计划任务;不依赖 DEBUG。
@@ -93,7 +94,6 @@ public class MainLauncher {
ClubPayRecordService payRecords = ioc.get(ClubPayRecordService.class, "clubPayRecordServiceImpl"); ClubPayRecordService payRecords = ioc.get(ClubPayRecordService.class, "clubPayRecordServiceImpl");
payRecords.upgradePayRecordSchema(); payRecords.upgradePayRecordSchema();
payRecords.backfillPayRecordSnapshots(); payRecords.backfillPayRecordSnapshots();
init_task();
init_auth(); init_auth();
ioc.get(Globals.class); ioc.get(Globals.class);
ioc.get(FlowEngine.class); ioc.get(FlowEngine.class);
@@ -116,17 +116,17 @@ public class MainLauncher {
} }
private void init_task() { private void init_task() {
if (log.isDebugEnabled() && !dao.exists("sys_qrtz_triggers") && !dao.exists("sys_qrtz_triggers".toUpperCase())) { FileSqlManager schema = new FileSqlManager("db/quartz/mysql.sql");
//执行Quartz SQL脚本 for (String table : schema.keys()) {
String dbType = dao.getJdbcExpert().getDatabaseType(); if (!dao.exists(table)) {
log.debug("dbType:::" + dbType); dao.execute(schema.create(table));
FileSqlManager fmq = new FileSqlManager("db/quartz/" + dbType.toLowerCase() + ".sql");
List<Sql> sqlListq = fmq.createCombo(fmq.keys());
Sql[] sqlsq = sqlListq.toArray(new Sql[sqlListq.size()]);
for (Sql sql : sqlsq) {
dao.execute(sql);
} }
} }
try {
ioc.get(TaskExecutionListener.class).register();
} catch (SchedulerException e) {
throw new IllegalStateException("注册任务执行记录监听器失败", e);
}
} }
private void init_sys() { private void init_sys() {
@@ -29,13 +29,13 @@ public enum BpmProcessConstant {
MEMBER_BRANCH_UNION_CHANGE("会员分工会变更"), MEMBER_BRANCH_UNION_CHANGE("会员分工会变更"),
CLUB_JOIN("社团入会申请"), CLUB_JOIN("协会入会申请"),
CLUB_EXAMINE("社团考核"), CLUB_EXAMINE("协会考核"),
CLUB_EVALUATE("社团评优"), CLUB_EVALUATE("协会评优"),
CLUB_REGISTER("社团注册"), CLUB_REGISTER("协会注册"),
PERSON_EVALUATE("评优评先申请"), PERSON_EVALUATE("评优评先申请"),
@@ -20,16 +20,17 @@ RoleConstant {
SCHOOL_UNION_CHAIRMAN("校工会主席"), SCHOOL_UNION_CHAIRMAN("校工会主席"),
SCHOOL_UNION_VICE_CHAIRMAN("校工会副主席"), SCHOOL_UNION_VICE_CHAIRMAN("校工会副主席"),
SCHOOL_UNION_WELFARE_ADMIN("校工会福利管理员"), SCHOOL_UNION_WELFARE_ADMIN("校工会福利管理员"),
SCHOOL_UNION_WELFARE_COMMITTEE("校福利委员会"),
SCHOOL_UNION_MEMBER_ADMIN("校工会会员管理员"), SCHOOL_UNION_MEMBER_ADMIN("校工会会员管理员"),
SCHOOL_UNION_TC_ADMIN("校工会教代会管理员"), SCHOOL_UNION_TC_ADMIN("校工会教代会管理员"),
SCHOOL_UNION_WC_ADMIN("校工会工代会管理员"), SCHOOL_UNION_WC_ADMIN("校工会工代会管理员"),
SCHOOL_UNION_DC_ADMIN("校工会民主管理员"), SCHOOL_UNION_DC_ADMIN("校工会民主管理员"),
SCHOOL_UNION_PROPOSAL_ADMIN("校工会提案管理员"), SCHOOL_UNION_PROPOSAL_ADMIN("校工会提案管理员"),
SCHOOL_UNION_CLUB_ADMIN("校工会社团管理员"), SCHOOL_UNION_CLUB_ADMIN("校工会协会管理员"),
SCHOOL_UNION_ACTIVITY_ADMIN("校工会活动管理员"), SCHOOL_UNION_ACTIVITY_ADMIN("校工会活动管理员"),
/** 品牌活动统计、人员调整管理员,数据范围按 trainSignUp 子活动负责人关系控制。 */ /** 品牌活动统计、人员调整管理员,数据范围按 trainSignUp 子活动负责人关系控制。 */
SCHOOL_UNION_TRAIN_SIGN_UP_ADMIN("校工会品牌活动管理员"), SCHOOL_UNION_TRAIN_SIGN_UP_ADMIN("校工会品牌活动管理员"),
SCHOOL_UNION_ARTICLE_CLUB_ADMIN("校工会社团新闻审批管理员"), SCHOOL_UNION_ARTICLE_CLUB_ADMIN("校工会协会新闻审批管理员"),
SCHOOL_UNION_ARTICLE_FGH_ADMIN("校工会分工会新闻审批管理员"), SCHOOL_UNION_ARTICLE_FGH_ADMIN("校工会分工会新闻审批管理员"),
SCHOOL_UNION_ARTICLE_ADMIN("校工会新闻审批管理员"), SCHOOL_UNION_ARTICLE_ADMIN("校工会新闻审批管理员"),
SCHOOL_OUTLAY_ADMIN("校工会经费管理员"), SCHOOL_OUTLAY_ADMIN("校工会经费管理员"),
@@ -85,16 +86,16 @@ RoleConstant {
SCHOOL_HOSPITAL_LEADER("校医院负责人"), SCHOOL_HOSPITAL_LEADER("校医院负责人"),
/*社团模块*/ /*协会模块*/
CLUB_MEMBER("社团会员"), CLUB_MEMBER("协会会员"),
CLUB_MANAGER("社团负责人"), CLUB_MANAGER("协会负责人"),
CLUB_PRESIDENT("社团会长"), CLUB_PRESIDENT("协会会长"),
CLUB_VICE_PRESIDENT("社团副会长"), CLUB_VICE_PRESIDENT("协会副会长"),
CLUB_SECRETARY("社团秘书长"), CLUB_SECRETARY("协会秘书长"),
CLUB_VICE_SECRETARY("社团副秘书长"), CLUB_VICE_SECRETARY("协会副秘书长"),
// SCHOOL_UNION_CLUB_ADMIN("校工会社团管理员"), // SCHOOL_UNION_CLUB_ADMIN("校工会协会管理员"),
CLUB_AUDIT_LEADER("社团分管领导"), CLUB_AUDIT_LEADER("协会分管领导"),
CLUB_OPERATOR("社团操作员"), CLUB_OPERATOR("协会操作员"),
/*大病基金会员*/ /*大病基金会员*/
AID_FUND_MEMBER("大病基金会员"), AID_FUND_MEMBER("大病基金会员"),
@@ -110,7 +111,7 @@ RoleConstant {
LEGAL_DOCTOR("法律顾问"), LEGAL_DOCTOR("法律顾问"),
UNION_REIMBURSEMENT_MANAGER("分工会报销负责人"), UNION_REIMBURSEMENT_MANAGER("分工会报销负责人"),
CLUB_REIMBURSEMENT_MANAGER("社团报销负责人"), CLUB_REIMBURSEMENT_MANAGER("协会报销负责人"),
SCHOOL_REIMBURSEMENT_MANAGER("校工会报销负责人"), SCHOOL_REIMBURSEMENT_MANAGER("校工会报销负责人"),
; ;
@@ -22,7 +22,7 @@ import org.nutz.lang.util.NutMap;
import java.util.List; import java.util.List;
/** /**
* 获取社团审批人根据流程变量参数(clubId) 必传 * 获取协会审批人根据流程变量参数(clubId) 必传
*/ */
public class FlowClubPresidentByArgsAssignmentHandler implements AssignmentHandler { public class FlowClubPresidentByArgsAssignmentHandler implements AssignmentHandler {
@Override @Override
@@ -45,7 +45,7 @@ public class FlowClubPresidentByArgsAssignmentHandler implements AssignmentHandl
@Override @Override
public String getMessage() { public String getMessage() {
return "获取社团会长(根据args中的clubId)"; return "获取协会会长(根据args中的clubId)";
} }
@Override @Override
@@ -0,0 +1,47 @@
package com.budwk.app.flow.handler;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.flow.engine.AssignmentHandler;
import com.budwk.app.flow.engine.core.Execution;
import com.budwk.app.flow.engine.core.ServiceContext;
import com.budwk.app.flow.engine.model.TaskModel;
import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.services.SysRoleService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import java.util.List;
public class FlowSchoolUnionWelfareCommitteeHandler implements AssignmentHandler {
@Override
public List<String> assign(TaskModel model, Execution execution) {
Dao dao = ServiceContext.find(Dao.class);
SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class);
Sys_role role = sysRoleService.getByCode(RoleConstant.SCHOOL_UNION_WELFARE_COMMITTEE);
if (role == null || role.isDisabled()) {
throw new BaseException("校福利委员会角色未配置或已禁用,请联系校工会!");
}
List<Sys_user_role> relations = dao.query(Sys_user_role.class,
Cnd.where(Sys_user_role::getRoleId, "=", role.getId())
.and(Sys_user_role::getEnable, "=", true));
List<String> userIds = relations.stream().map(Sys_user_role::getUserId)
.filter(StrUtil::isNotBlank).distinct().toList();
if (userIds.isEmpty()) {
throw new BaseException("校福利委员没有设置,请联系校工会!");
}
return userIds;
}
@Override
public String getMessage() {
return "获取校福利委员";
}
@Override
public int getOrder() {
return 14;
}
}
@@ -14,6 +14,7 @@ import io.swagger.annotations.ApiOperation;
import org.nutz.dao.Cnd; import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls; import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql; import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.At;
@@ -47,10 +48,10 @@ public class SysDataUserPullController {
cnd.andEX(Sys_user_source::getPullTime, "=", pageForm.getPullTime()); cnd.andEX(Sys_user_source::getPullTime, "=", pageForm.getPullTime());
cnd.and(Cnd.likeEX(Sys_user_source::getUsername, pageForm.getUserName())); cnd.and(Cnd.likeEX(Sys_user_source::getUsername, pageForm.getUserName()));
cnd.and(Cnd.likeEX(Sys_user_source::getLoginname, pageForm.getLoginName())); cnd.and(Cnd.likeEX(Sys_user_source::getLoginname, pageForm.getLoginName()));
cnd.andEX(Sys_user_source::getUnitName, "=", pageForm.getUnitName()); addSourceFilter(cnd, "us.unitName", pageForm.getUnitName());
cnd.andEX(Sys_user_source::getUserState, "=", pageForm.getUserState()); addSourceFilter(cnd, "us.userState", pageForm.getUserState());
cnd.andEX(Sys_user_source::getPreparedBy, "=", pageForm.getPreparedBy()); cnd.andEX(Sys_user_source::getPreparedBy, "=", pageForm.getPreparedBy());
cnd.andEX(Sys_user_source::getPersonType, "=", pageForm.getPersonType()); addSourceFilter(cnd, "us.personType", pageForm.getPersonType());
if (StrUtil.isAllNotBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { if (StrUtil.isAllNotBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy())); cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
@@ -61,6 +62,15 @@ public class SysDataUserPullController {
return Result.success(pagination); return Result.success(pagination);
} }
private void addSourceFilter(Cnd cnd, String column, String value) {
if ("__EMPTY__".equals(value)) {
cnd.and(new Static("(%s IS NULL OR LOWER(%s) REGEXP '^[[:space:]]*(null)?[[:space:]]*$')"
.formatted(column, column)));
} else {
cnd.andEX(column, "=", value);
}
}
@At @At
@SaCheckPermission("sys.data.user.pull") @SaCheckPermission("sys.data.user.pull")
@ApiOperation("拉取用户数据") @ApiOperation("拉取用户数据")
@@ -0,0 +1,32 @@
package com.budwk.app.sys.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.sys.services.SysDataUserPullService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@At("/platform/sys/data/user/query")
@Ok("json:full")
public class SysDataUserQueryController {
@Inject
private SysDataUserPullService sysUserPullService;
@At("")
@Ok("beetl:/platform/sys/data/user/query.html")
@SaCheckPermission("sys.data.user.query")
public void index() {
}
@At
@SaCheckPermission("sys.data.user.query")
public Result queryData(@Param("zgh") String zgh, @Param("xm") String xm,
@Param("pageNumber") int pageNumber, @Param("pageSize") int pageSize) {
return Result.success(sysUserPullService.queryUsers(zgh, xm, pageNumber, pageSize));
}
}
@@ -147,12 +147,9 @@ public class SysLoginController {
// 解密密码 // 解密密码
LoginRsaKey rsaKey = LOGIN_RSA_KEY_RING.getPrivateKey(keyId); LoginRsaKey rsaKey = LOGIN_RSA_KEY_RING.getPrivateKey(keyId);
if (rsaKey == null) { if (rsaKey == null) {
throw new BaseException("Login key expired, please refresh the login page"); throw new BaseException("登录凭证已过期,请刷新登录页面。");
} }
String decryptPwd = RsaUtils.decrypt(password, rsaKey.privateKey); String decryptPwd = RsaUtils.decrypt(password, rsaKey.privateKey);
if (decryptPwd == null) {
throw new BaseException("用户登录失败");
}
decryptPwd = Base64Encoder.encode(decryptPwd); decryptPwd = Base64Encoder.encode(decryptPwd);
// 用户名密码校验 // 用户名密码校验
@@ -2,15 +2,18 @@ package com.budwk.app.sys.controller;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result; import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil; import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.sys.models.Sys_task; import com.budwk.app.sys.models.Sys_task;
import com.budwk.app.sys.models.Sys_task_execution;
import com.budwk.app.sys.services.SysTaskService; import com.budwk.app.sys.services.SysTaskService;
import com.budwk.app.task.services.TaskPlatformService; import com.budwk.app.task.services.TaskPlatformService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.dao.Chain;
import org.nutz.dao.Cnd; import org.nutz.dao.Cnd;
import org.nutz.ioc.aop.Aop; import org.nutz.dao.Dao;
import org.nutz.dao.FieldFilter;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings; import org.nutz.lang.Strings;
@@ -18,9 +21,12 @@ import org.nutz.log.Log;
import org.nutz.log.Logs; import org.nutz.log.Logs;
import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.POST;
import org.nutz.mvc.annotation.Param; import org.nutz.mvc.annotation.Param;
import org.nutz.trans.Trans;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.List;
; ;
@@ -32,6 +38,8 @@ public class SysTaskController {
private SysTaskService sysTaskService; private SysTaskService sysTaskService;
@Inject @Inject
private TaskPlatformService taskPlatformService; private TaskPlatformService taskPlatformService;
@Inject
private Dao dao;
@At("") @At("")
@Ok("beetl:/platform/sys/task/index.html") @Ok("beetl:/platform/sys/task/index.html")
@@ -41,10 +49,10 @@ public class SysTaskController {
} }
@At @At
@Ok("beetl:/platform/sys/task/cron.html") @Ok("beetl:/platform/sys/task/index.html")
@SaCheckPermission("sys.manager.task") @SaCheckPermission("sys.manager.task")
public void cron() { public void cron(HttpServletRequest req) {
req.setAttribute("openCron", true);
} }
@At @At
@@ -52,9 +60,9 @@ public class SysTaskController {
@SaCheckPermission("sys.manager.task") @SaCheckPermission("sys.manager.task")
public Object cronExp(@Param("cron") String cron) { public Object cronExp(@Param("cron") String cron) {
try { try {
return Result.success().addData(taskPlatformService.getCronExeTimes(cron)); return Result.success().addData(previewCron(normalizeCron(cron)));
} catch (Exception e) { } catch (Exception e) {
return Result.error(e.getMessage()); return taskError("获取执行时间失败", e);
} }
} }
@@ -73,20 +81,57 @@ public class SysTaskController {
} }
} }
@At("/history/?")
@Ok("json")
@SaCheckPermission("sys.manager.task")
public Object history(String taskId, @Param("pageNumber") int pageNumber) {
try {
requireTask(taskId);
int page = Math.max(1, Math.min(pageNumber, Integer.MAX_VALUE / 10));
Cnd condition = Cnd.where("taskId", "=", taskId);
condition.desc("startedAt").desc("id");
List<Sys_task_execution> list = dao.query(Sys_task_execution.class, condition, dao.createPager(page, 10),
"^(id|taskId|startedAt|finishedAt|status)$");
int total = dao.count(Sys_task_execution.class, Cnd.where("taskId", "=", taskId));
return Result.success(new Pagination<>(page, 10, total, list));
} catch (Exception e) {
return taskError("获取执行历史失败", e);
}
}
@At("/historyLog/?/?")
@Ok("json")
@SaCheckPermission("sys.manager.task")
public Object historyLog(String taskId, String executionId) {
try {
requireTask(taskId);
Sys_task_execution execution = dao.fetch(Sys_task_execution.class,
Cnd.where("id", "=", executionId).and("taskId", "=", taskId));
return execution == null ? Result.error("执行记录不存在") : Result.success(execution);
} catch (Exception e) {
return taskError("获取执行日志失败", e);
}
}
@At @At
@Ok("json") @Ok("json")
@SLog(tag = "新建任务", msg = "任务名称:${args[0].name}") @SLog(tag = "新建任务", msg = "任务名称:${args[0].name}")
@SaCheckPermission("sys.manager.task.add") @SaCheckPermission("sys.manager.task.add")
@Aop(TransAop.READ_COMMITTED)
public Object addDo(@Param("..") Sys_task task, HttpServletRequest req) { public Object addDo(@Param("..") Sys_task task, HttpServletRequest req) {
try { try {
task.setCron(validateCron(task.getCron()));
task.setCreatedBy(SecurityUtil.getUserId()); task.setCreatedBy(SecurityUtil.getUserId());
Sys_task sysTask = sysTaskService.insert(task); task.setExeAt(null);
taskPlatformService.add(sysTask.getId(), sysTask.getId(), sysTask.getJobClass(), sysTask.getCron(), task.setExeResult(null);
sysTask.getNote(), sysTask.getData()); Trans.exec(() -> {
Sys_task saved = sysTaskService.insert(task);
if (!saved.isDisabled()) {
schedule(saved);
}
});
return Result.success(); return Result.success();
} catch (Exception e) { } catch (Exception e) {
return Result.error(); return taskError("新建任务失败", e);
} }
} }
@@ -107,18 +152,24 @@ public class SysTaskController {
@SaCheckPermission("sys.manager.task.edit") @SaCheckPermission("sys.manager.task.edit")
public Object editDo(@Param("..") Sys_task task, HttpServletRequest req) { public Object editDo(@Param("..") Sys_task task, HttpServletRequest req) {
try { try {
if (task.getCron() != null) {
task.setCron(validateCron(task.getCron()));
}
task.setUpdatedBy(SecurityUtil.getUserId()); task.setUpdatedBy(SecurityUtil.getUserId());
sysTaskService.updateIgnoreNull(task); Trans.exec(() -> {
if (taskPlatformService.isExist(task.getId(), task.getId())) { requireTask(task.getId());
taskPlatformService.delete(task.getId(), task.getId()); FieldFilter.locked(Sys_task.class, "^(exeAt|exeResult)$")
} .run(() -> sysTaskService.updateIgnoreNull(task));
if (!task.isDisabled()) { Sys_task saved = requireTask(task.getId());
taskPlatformService.add(task.getId(), task.getId(), task.getJobClass(), task.getCron(), if (saved.isDisabled()) {
task.getNote(), task.getData()); taskPlatformService.delete(saved.getId(), saved.getId());
} } else {
schedule(saved);
}
});
return Result.success(); return Result.success();
} catch (Exception e) { } catch (Exception e) {
return Result.error(); return taskError("修改任务失败", e);
} }
} }
@@ -129,19 +180,35 @@ public class SysTaskController {
@SaCheckPermission("sys.manager.task.delete") @SaCheckPermission("sys.manager.task.delete")
public Object delete(String id, HttpServletRequest req) { public Object delete(String id, HttpServletRequest req) {
try { try {
Sys_task sysTask = sysTaskService.fetch(id); Trans.exec(() -> {
try { Sys_task task = requireTask(id);
if (taskPlatformService.isExist(sysTask.getId(), sysTask.getId())) { sysTaskService.delete(id);
taskPlatformService.delete(sysTask.getId(), sysTask.getId()); taskPlatformService.delete(id, id);
} req.setAttribute("name", task.getName());
} catch (Exception e) { });
log.error(e.getMessage());
}
sysTaskService.delete(id);
req.setAttribute("name", sysTask.getName());
return Result.success(); return Result.success();
} catch (Exception e) { } catch (Exception e) {
return Result.error(); return taskError("删除任务失败", e);
}
}
@At("/runOnce/?")
@POST
@Ok("json")
@SaCheckPermission("sys.manager.task.edit")
@SLog(tag = "执行任务一次", msg = "任务名:${args[1].getAttribute('name')}")
public Object runOnce(String id, HttpServletRequest req) {
Sys_task task = sysTaskService.fetch(id);
if (task == null) {
return Result.error("任务不存在");
}
req.setAttribute("name", task.getName());
try {
taskPlatformService.runOnce(task);
return Result.success("已提交执行");
} catch (Exception e) {
log.error("提交单次任务失败:" + task.getName(), e);
return Result.error("提交执行失败");
} }
} }
@@ -150,18 +217,7 @@ public class SysTaskController {
@SaCheckPermission("sys.manager.task.edit") @SaCheckPermission("sys.manager.task.edit")
@SLog(tag = "启用任务", msg = "任务名:${args[1].getAttribute('name')}") @SLog(tag = "启用任务", msg = "任务名:${args[1].getAttribute('name')}")
public Object enable(String id, HttpServletRequest req) { public Object enable(String id, HttpServletRequest req) {
try { return changeDisabled(id, false, req);
Sys_task sysTask = sysTaskService.fetch(id);
req.setAttribute("name", sysTask.getName());
sysTaskService.update(org.nutz.dao.Chain.make("disabled", false), Cnd.where("id", "=", id));
if (!taskPlatformService.isExist(sysTask.getId(), sysTask.getId())) {
taskPlatformService.add(sysTask.getId(), sysTask.getId(), sysTask.getJobClass(), sysTask.getCron(),
sysTask.getNote(), sysTask.getData());
}
return Result.success();
} catch (Exception e) {
return Result.error();
}
} }
@At("/disable/?") @At("/disable/?")
@@ -169,16 +225,66 @@ public class SysTaskController {
@SaCheckPermission("sys.manager.task.edit") @SaCheckPermission("sys.manager.task.edit")
@SLog(tag = "禁用任务", msg = "任务名:${args[1].getAttribute('name')}") @SLog(tag = "禁用任务", msg = "任务名:${args[1].getAttribute('name')}")
public Object disable(String id, HttpServletRequest req) { public Object disable(String id, HttpServletRequest req) {
return changeDisabled(id, true, req);
}
private Object changeDisabled(String id, boolean disabled, HttpServletRequest req) {
try { try {
Sys_task sysTask = sysTaskService.fetch(id); Trans.exec(() -> {
req.setAttribute("name", sysTask.getName()); sysTaskService.update(Chain.make("disabled", disabled), Cnd.where("id", "=", id));
sysTaskService.update(org.nutz.dao.Chain.make("disabled", true), Cnd.where("id", "=", id)); Sys_task task = requireTask(id);
if (taskPlatformService.isExist(sysTask.getId(), sysTask.getId())) { req.setAttribute("name", task.getName());
taskPlatformService.delete(sysTask.getId(), sysTask.getId()); if (disabled) {
} taskPlatformService.delete(id, id);
} else {
schedule(task);
}
});
return Result.success(); return Result.success();
} catch (Exception e) { } catch (Exception e) {
return Result.error(); return taskError(disabled ? "禁用任务失败" : "启用任务失败", e);
} }
} }
private Sys_task requireTask(String id) {
Sys_task task = Strings.isBlank(id) ? null : sysTaskService.fetch(id);
if (task == null) {
throw new IllegalArgumentException("任务不存在");
}
return task;
}
private void schedule(Sys_task task) {
taskPlatformService.add(task.getId(), task.getId(), task.getJobClass(), task.getCron(),
task.getNote(), task.getData());
}
private String normalizeCron(String cron) {
return Strings.sNull(cron).trim().replaceAll("\\s+", " ");
}
private String validateCron(String cron) throws Exception {
String normalized = normalizeCron(cron);
previewCron(normalized);
return normalized;
}
private List<String> previewCron(String normalized) throws Exception {
if (normalized.length() > 50) {
throw new IllegalArgumentException("Cron 表达式最多 50 个字符");
}
List<String> times = taskPlatformService.getCronExeTimes(normalized);
if (times.isEmpty()) {
throw new IllegalArgumentException("Cron 表达式没有未来执行时间");
}
return times;
}
private Result taskError(String message, Exception e) {
if (e instanceof IllegalArgumentException) {
return Result.error(e.getMessage());
}
log.error(message, e);
return Result.error(message);
}
} }
@@ -84,6 +84,7 @@ public class SysUnitController {
// cnd.and("parentId", "is not", null).andEX("unitLevel", "=", unitLevel).asc("unitLevel").asc("unitcode"); // cnd.and("parentId", "is not", null).andEX("unitLevel", "=", unitLevel).asc("unitLevel").asc("unitcode");
cnd.and("parentId", "is not", null); cnd.and("parentId", "is not", null);
cnd.andEX("parentId", "=", unitId); cnd.andEX("parentId", "=", unitId);
cnd.andEX("id", "!=", unitId);
// cnd.and("unitTypeCode", "=", "1"); // cnd.and("unitTypeCode", "=", "1");
cnd.asc("unitcode"); cnd.asc("unitcode");
cnd.and(Cnd.likeEX("name", unitName)); cnd.and(Cnd.likeEX("name", unitName));
@@ -313,15 +314,17 @@ public class SysUnitController {
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.asc("unitcode"); cnd.asc("unitcode");
List<Sys_unit> list = sysUnitService.query(cnd); List<Sys_unit> list = sysUnitService.query(cnd);
boolean defaultRoot = "0".equals(rootId);
boolean hasDefaultRoot = defaultRoot && list.stream().anyMatch(unit -> rootId.equals(unit.getId()));
List<TreeNode<String>> nodeList = CollUtil.newArrayList(); List<TreeNode<String>> nodeList = CollUtil.newArrayList();
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
Sys_unit unit = list.get(i); Sys_unit unit = list.get(i);
/* String parentId = unit.getParentId();
* 单位根节点存在 id 与 parentId 都为 0 的自引用数据。 if (rootId.equals(unit.getId()) || (defaultRoot && (StrUtil.isBlank(parentId)
* 构建树时把当前查询根挂到虚拟根下,避免“中国地质大学”和 parentId=0 的学院被构造成同级。 || (!hasDefaultRoot && rootId.equals(parentId))))) {
*/ parentId = virtualRootId;
String parentId = rootId.equals(unit.getId()) ? virtualRootId : unit.getParentId(); }
nodeList.add(new TreeNode<>(unit.getId(), parentId, unit.getName(), i) nodeList.add(new TreeNode<>(unit.getId(), parentId, unit.getName(), i)
.setExtra( .setExtra(
Map.of( Map.of(
@@ -0,0 +1,37 @@
package com.budwk.app.sys.models;
import lombok.Data;
import org.nutz.dao.entity.annotation.*;
@Data
@Table("sys_task_execution")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@TableIndexes(@Index(name = "IDX_SYS_TASK_EXECUTION_STARTED", fields = {"taskId", "startedAt"}))
public class Sys_task_execution {
public enum Status {
RUNNING, SUCCESS, FAILED, SKIPPED, INTERRUPTED
}
@Name
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String id;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
private String taskId;
@Column
private long startedAt;
@Column
private Long finishedAt;
@Column
@ColDefine(type = ColType.VARCHAR, width = 16)
private String status;
@Column
@ColDefine(customType = "MEDIUMTEXT")
private String executionLog;
}
@@ -98,6 +98,7 @@ public class Sys_user extends BaseModel implements Serializable {
@Column @Column
@Comment("证件类型") @Comment("证件类型")
@ColDefine(type = ColType.VARCHAR, width = 30) @ColDefine(type = ColType.VARCHAR, width = 30)
@DataCenterColumn(name = "证件类型", key = "SFZJLXDM", dict = "USER_ID_CARD_TYPE")
private String idCardType; private String idCardType;
@Column @Column
@@ -133,6 +134,7 @@ public class Sys_user extends BaseModel implements Serializable {
@Column @Column
@Comment("岗位类别") @Comment("岗位类别")
@ColDefine(type = ColType.VARCHAR, width = 50) @ColDefine(type = ColType.VARCHAR, width = 50)
@DataCenterColumn(name = "聘岗类型", key = "ZYJSGWLBDMMC")
private String jobCategory; private String jobCategory;
@Column @Column
@@ -222,7 +224,7 @@ public class Sys_user extends BaseModel implements Serializable {
private Integer pendingConfirmStatus; private Integer pendingConfirmStatus;
@Column @Column
@Comment("编制类别码") @Comment("编制类别")
@ColDefine(type = ColType.VARCHAR, width = 30) @ColDefine(type = ColType.VARCHAR, width = 30)
@DataCenterColumn(name = "编制类别", key = "BZLBDMMC") @DataCenterColumn(name = "编制类别", key = "BZLBDMMC")
private String preparedBy; private String preparedBy;
@@ -295,6 +297,7 @@ public class Sys_user extends BaseModel implements Serializable {
@Comment("是否双肩挑") @Comment("是否双肩挑")
@ColDefine(type = ColType.BOOLEAN) @ColDefine(type = ColType.BOOLEAN)
@Default(value = "0") @Default(value = "0")
@DataCenterColumn(name = "是否双肩挑", key = "SFSJT")
private Boolean manyUnit; private Boolean manyUnit;
@Column @Column
@@ -61,7 +61,7 @@ public class Sys_user_role {
@Column @Column
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("社团ID") @Comment("协会ID")
private String clubId; private String clubId;
@Column @Column
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
@@ -1,5 +1,6 @@
package com.budwk.app.sys.models; package com.budwk.app.sys.models;
import com.budwk.app.sys.annotation.DataCenterColumn;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@@ -31,6 +32,42 @@ public class Sys_user_source extends Sys_user {
@ColDefine(type = ColType.VARCHAR, width = 255) @ColDefine(type = ColType.VARCHAR, width = 255)
private String unitName; private String unitName;
@Column
@Comment("最高学历代码")
@ColDefine(type = ColType.VARCHAR, width = 20)
@DataCenterColumn(name = "最高学历代码", key = "ZGXLDM")
private String educationCode;
@Column
@Comment("最高学位代码")
@ColDefine(type = ColType.VARCHAR, width = 50)
@DataCenterColumn(name = "最高学位代码", key = "XWDM")
private String academicDegreeCode;
@Column
@Comment("编制类别代码")
@ColDefine(type = ColType.VARCHAR, width = 30)
@DataCenterColumn(name = "编制类别代码", key = "BZLBDM")
private String preparedByCode;
@Column
@Comment("聘岗类型代码")
@ColDefine(type = ColType.VARCHAR, width = 50)
@DataCenterColumn(name = "聘岗类型代码", key = "ZYJSGWLBDM")
private String jobCategoryCode;
@Column
@Comment("专业技术职务代码")
@ColDefine(type = ColType.VARCHAR, width = 50)
@DataCenterColumn(name = "专业技术职务代码", key = "ZYJSZWDM")
private String professionalTitleCode;
@Column
@Comment("当前状态代码")
@ColDefine(type = ColType.VARCHAR, width = 30)
@DataCenterColumn(name = "当前状态代码", key = "DQZTDM")
private String userStateCode;
@Column @Column
@Comment("拉取时间") @Comment("拉取时间")
@ColDefine(type = ColType.DATETIME) @ColDefine(type = ColType.DATETIME)
@@ -15,6 +15,12 @@ public interface SysDataUserPullService extends BaseService<Sys_user_source> {
*/ */
Date pull(); Date pull();
default NutMap queryUsers(String zgh, int pageNumber, int pageSize) {
return queryUsers(zgh, null, pageNumber, pageSize);
}
NutMap queryUsers(String zgh, String xm, int pageNumber, int pageSize);
/** /**
* 拉取人员博士后的信息 * 拉取人员博士后的信息
*/ */
@@ -7,4 +7,15 @@ import com.budwk.app.sys.param.SysDataUserUpdateParam;
*/ */
public interface SysDataUserUpdateService { public interface SysDataUserUpdateService {
String update(SysDataUserUpdateParam updateParam); String update(SysDataUserUpdateParam updateParam);
static boolean qualifiesAsUnionMember(String personType, String userState) {
if (!"在岗".equals(userState) || personType == null) {
return false;
}
return switch (personType) {
case "在编", "兰溪编制", "聘用合同制(享受人事代理待遇)",
"聘用合同制", "聘用制专家", "人事代理" -> true;
default -> false;
};
}
} }
@@ -136,6 +136,9 @@ public interface SysRoleService extends BaseService<Sys_role> {
*/ */
NutMap settingMemberRole(); NutMap settingMemberRole();
/** 为指定人员中 member=1 的用户批量补齐启用的会员角色。 */
NutMap settingMemberRole(List<String> userIds);
/** /**
* 清空缓存 * 清空缓存
*/ */
@@ -164,6 +164,9 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
// 创建用户对象 // 创建用户对象
Sys_user u = new Sys_user(); Sys_user u = new Sys_user();
BeanUtils.copyProperties(source, u); BeanUtils.copyProperties(source, u);
if (SysDataUserUpdateService.qualifiesAsUnionMember(u.getPersonType(), u.getUserState())) {
u.setMember(true);
}
if (user == null) { if (user == null) {
// 新增用户,初始化数据 // 新增用户,初始化数据
@@ -298,6 +301,13 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
return "更新失败: " + e.getMessage(); return "更新失败: " + e.getMessage();
} }
List<String> updatedUserIds = new ArrayList<>();
needInitUserList.forEach(user -> updatedUserIds.add(user.getId()));
needDoUpdateList.forEach(user -> updatedUserIds.add(user.getId()));
sysRoleService.settingMemberRole(updatedUserIds);
sysRoleService.clearCache();
sysUserService.clearCache();
/* // 4. 异步添加会员角色 - 不等待完成 /* // 4. 异步添加会员角色 - 不等待完成
if (Lang.isNotEmpty(addMemberUserIds)) { if (Lang.isNotEmpty(addMemberUserIds)) {
executorService.execute(() -> { executorService.execute(() -> {
@@ -14,6 +14,7 @@ import com.budwk.app.sys.param.SysDataUserUpdateParam;
import com.budwk.app.sys.services.SysDataUserUpdateService; import com.budwk.app.sys.services.SysDataUserUpdateService;
import com.budwk.app.sys.services.SysRoleService; import com.budwk.app.sys.services.SysRoleService;
import com.budwk.app.sys.services.SysUnitService; import com.budwk.app.sys.services.SysUnitService;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.sys.utils.DataCenterUtil; import com.budwk.app.sys.utils.DataCenterUtil;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig;
import com.budwk.app.zhgh.staffmanage.member.constant.MemberChangeOrigin; import com.budwk.app.zhgh.staffmanage.member.constant.MemberChangeOrigin;
@@ -44,6 +45,8 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
private SysRoleService sysRoleService; private SysRoleService sysRoleService;
@Inject @Inject
private SysUnitService sysUnitService; private SysUnitService sysUnitService;
@Inject
private SysUserService sysUserService;
@Override @Override
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@@ -123,6 +126,9 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
//加入到系统用户表 //加入到系统用户表
List<Sys_user> sysUsers = BeanUtil.copyToList(userSources, Sys_user.class); List<Sys_user> sysUsers = BeanUtil.copyToList(userSources, Sys_user.class);
for (Sys_user sysUser : sysUsers) { for (Sys_user sysUser : sysUsers) {
if (SysDataUserUpdateService.qualifiesAsUnionMember(sysUser.getPersonType(), sysUser.getUserState())) {
sysUser.setMember(true);
}
//重新设置 //重新设置
sysUser.setId(R.UU32()); sysUser.setId(R.UU32());
String salt = R.UU32(); String salt = R.UU32();
@@ -161,7 +167,9 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic
return sysUserRole; return sysUserRole;
}).toList(); }).toList();
dao.insert(sysUserRoles); dao.insert(sysUserRoles);
sysRoleService.settingMemberRole(sysUsers.stream().map(Sys_user::getId).toList());
sysRoleService.clearCache(); sysRoleService.clearCache();
sysUserService.clearCache();
// 6.1 更新提案的校领导角色,发送订阅 // 6.1 更新提案的校领导角色,发送订阅
ProposalConfig config = dao.fetch(ProposalConfig.class, Cnd.where("delFlag", "=", false).desc("updatedAt")); ProposalConfig config = dao.fetch(ProposalConfig.class, Cnd.where("delFlag", "=", false).desc("updatedAt"));
@@ -98,7 +98,7 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl<Sys_user_source>
synchronized (SysDataUserPullServiceImpl.class) { synchronized (SysDataUserPullServiceImpl.class) {
if (fieldMappings == null) { if (fieldMappings == null) {
List<FieldMapping> mappings = new ArrayList<>(); List<FieldMapping> mappings = new ArrayList<>();
Field[] fields = Sys_user.class.getDeclaredFields(); Field[] fields = cn.hutool.core.util.ReflectUtil.getFields(Sys_user_source.class);
for (Field field : fields) { for (Field field : fields) {
DataCenterColumn annotation = field.getAnnotation(DataCenterColumn.class); DataCenterColumn annotation = field.getAnnotation(DataCenterColumn.class);
if (annotation != null) { if (annotation != null) {
@@ -200,6 +200,8 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl<Sys_user_source>
mapping.field.set(sysUser, raw.getStr(mapping.key)); mapping.field.set(sysUser, raw.getStr(mapping.key));
} }
} }
} else if (mapping.field.getType() == Boolean.class) {
mapping.field.set(sysUser, raw.getBool(mapping.key));
} else if (mapping.field.getType() == Date.class) { } else if (mapping.field.getType() == Date.class) {
String dateText = StrUtil.trim(raw.getStr(mapping.key)); String dateText = StrUtil.trim(raw.getStr(mapping.key));
if (StrUtil.isNotBlank(dateText)) { if (StrUtil.isNotBlank(dateText)) {
@@ -332,12 +334,50 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl<Sys_user_source>
return url; return url;
} }
@Override
public NutMap queryUsers(String zgh, String xm, int pageNumber, int pageSize) {
if (pageNumber < 1 || pageSize < 1 || pageSize > 200) {
throw new BaseException("分页参数不合法");
}
String jobNumber = StrUtil.trim(zgh);
String name = StrUtil.trim(xm);
boolean tokenEnabled = dataCenterProperties.isTokenEnabled();
if (!tokenEnabled && StrUtil.isBlank(name)) {
JSONObject response = DataCenterUtil.queryPage(getDmpUrl(DMP_TEACHER_KEY), jobNumber, pageNumber, pageSize);
JSONObject pageInfo = response.getJSONObject("pageInfo");
JSONArray data = response.getJSONArray("dataSet");
if (pageInfo == null || pageInfo.getLong("total") == null || data == null) {
throw new BaseException("数据中心接口未返回有效的分页数据");
}
return NutMap.NEW().addv("list", data).addv("totalCount", pageInfo.getLong("total"));
}
List<JSONObject> records = new ArrayList<>();
if (tokenEnabled) {
String accessToken = getDmpAccessToken();
records.addAll(pullDmpUsers(getDmpUrl(DMP_TEACHER_KEY), accessToken, "在岗教职工", jobNumber));
records.addAll(pullDmpUsers(getDmpUrl(DMP_DISPATCH_KEY), accessToken, "在岗劳务派遣", jobNumber));
} else {
Map<String, String> params = StrUtil.isBlank(jobNumber) ? Map.of() : Map.of("zgh", jobNumber);
records = DataCenterUtil.pullData(getDmpUrl(DMP_TEACHER_KEY), params);
}
if (StrUtil.isNotBlank(name)) {
records.removeIf(record -> !StringUtils.contains(record.getStr("XM"), name));
}
int from = (int) Math.min((long) (pageNumber - 1) * pageSize, records.size());
return NutMap.NEW().addv("list", records.subList(from, Math.min(from + pageSize, records.size())))
.addv("totalCount", records.size());
}
/** /**
* 分页拉取单个人员接口数据。 * 分页拉取单个人员接口数据。
* 入参 url 为人员接口地址,accessToken 为信息中心令牌,sourceName 用于日志区分接口来源。 * 入参 url 为人员接口地址,accessToken 为信息中心令牌,sourceName 用于日志区分接口来源。
* 返回值为该接口所有分页合并后的原始人员 JSON 列表。 * 返回值为该接口所有分页合并后的原始人员 JSON 列表。
*/ */
private List<JSONObject> pullDmpUsers(String url, String accessToken, String sourceName) { private List<JSONObject> pullDmpUsers(String url, String accessToken, String sourceName) {
return pullDmpUsers(url, accessToken, sourceName, null);
}
private List<JSONObject> pullDmpUsers(String url, String accessToken, String sourceName, String zgh) {
List<JSONObject> rawDataList = new ArrayList<>(); List<JSONObject> rawDataList = new ArrayList<>();
int page = 1; int page = 1;
int maxPage = 1; int maxPage = 1;
@@ -347,11 +387,14 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl<Sys_user_source>
// 信息中心接口单页最多返回1000条,按page循环拉完当前接口全部数据。 // 信息中心接口单页最多返回1000条,按page循环拉完当前接口全部数据。
HttpRequest httpRequest = HttpUtil.createPost(url); HttpRequest httpRequest = HttpUtil.createPost(url);
httpRequest.header(Header.CONTENT_TYPE, "application/json"); httpRequest.header(Header.CONTENT_TYPE, "application/json");
httpRequest.body(JSONUtil.toJsonStr(Map.of( Map<String, Object> body = new HashMap<>();
"access_token", accessToken, body.put("access_token", accessToken);
"per_page", String.valueOf(DMP_PAGE_SIZE), body.put("per_page", String.valueOf(DMP_PAGE_SIZE));
"page", String.valueOf(page) body.put("page", String.valueOf(page));
))); if (StrUtil.isNotBlank(zgh)) {
body.put("zgh", zgh);
}
httpRequest.body(JSONUtil.toJsonStr(body));
log.info("请求{}人员数据,第{}页: {}", sourceName, page, httpRequest); log.info("请求{}人员数据,第{}页: {}", sourceName, page, httpRequest);
String resBody = httpRequest.execute().body(); String resBody = httpRequest.execute().body();
@@ -294,6 +294,27 @@ public class SysRoleServiceImpl extends BaseServiceImpl<Sys_role> implements Sys
@Override @Override
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public NutMap settingMemberRole() { public NutMap settingMemberRole() {
return settingMemberRole(Cnd.where("u.member", "=", 1));
}
@Override
@Aop(TransAop.READ_COMMITTED)
public NutMap settingMemberRole(List<String> userIds) {
int addedCount = 0;
int enabledCount = 0;
if (userIds != null && !userIds.isEmpty()) {
List<String> distinctIds = userIds.stream().distinct().toList();
for (int start = 0; start < distinctIds.size(); start += 500) {
NutMap counts = settingMemberRole(Cnd.where("u.member", "=", 1)
.and("u.id", "in", distinctIds.subList(start, Math.min(start + 500, distinctIds.size()))));
addedCount += counts.getInt("addedCount");
enabledCount += counts.getInt("enabledCount");
}
}
return NutMap.NEW().addv("addedCount", addedCount).addv("enabledCount", enabledCount);
}
private NutMap settingMemberRole(Cnd condition) {
// 锁定同一会员角色,使本接口的并发请求依次执行,避免重复插入用户角色关系。 // 锁定同一会员角色,使本接口的并发请求依次执行,避免重复插入用户角色关系。
Sql roleSql = Sqls.create("SELECT * FROM sys_role WHERE code=@code FOR UPDATE"); Sql roleSql = Sqls.create("SELECT * FROM sys_role WHERE code=@code FOR UPDATE");
roleSql.setParam("code", RoleConstant.MEMBER.name()); roleSql.setParam("code", RoleConstant.MEMBER.name());
@@ -312,14 +333,16 @@ public class SysRoleServiceImpl extends BaseServiceImpl<Sys_role> implements Sys
// 历史关联可能已停用或未填写 enable;恢复关联即可,无需重新插入。 // 历史关联可能已停用或未填写 enable;恢复关联即可,无需重新插入。
Sql enableSql = Sqls.create("UPDATE sys_user_role SET enable=1 WHERE roleId=@roleId " Sql enableSql = Sqls.create("UPDATE sys_user_role SET enable=1 WHERE roleId=@roleId "
+ "AND (enable IS NULL OR enable<>1) " + "AND (enable IS NULL OR enable<>1) "
+ "AND EXISTS (SELECT 1 FROM sys_user u WHERE u.id=sys_user_role.userId AND u.member=1)"); + "AND EXISTS (SELECT 1 FROM sys_user u $condition AND u.id=sys_user_role.userId)");
enableSql.setCondition(condition);
enableSql.setParam("roleId", role.getId()); enableSql.setParam("roleId", role.getId());
dao().execute(enableSql); dao().execute(enableSql);
// 按用户、角色判重,只为缺少会员关联的人员添加记录;不删除任何已有角色。 // 按用户、角色判重,只为缺少会员关联的人员添加记录;不删除任何已有角色。
Sql insertSql = Sqls.create("INSERT INTO sys_user_role (userId,roleId,enable) " Sql insertSql = Sqls.create("INSERT INTO sys_user_role (userId,roleId,enable) "
+ "SELECT u.id,@roleId,1 FROM sys_user u WHERE u.member=1 " + "SELECT u.id,@roleId,1 FROM sys_user u $condition "
+ "AND NOT EXISTS (SELECT 1 FROM sys_user_role ur WHERE ur.userId=u.id AND ur.roleId=@roleId)"); + "AND NOT EXISTS (SELECT 1 FROM sys_user_role ur WHERE ur.userId=u.id AND ur.roleId=@roleId)");
insertSql.setCondition(condition);
insertSql.setParam("roleId", role.getId()); insertSql.setParam("roleId", role.getId());
dao().execute(insertSql); dao().execute(insertSql);
return NutMap.NEW().addv("addedCount", insertSql.getUpdateCount()) return NutMap.NEW().addv("addedCount", insertSql.getUpdateCount())
@@ -14,6 +14,7 @@ import com.budwk.app.sys.models.Sys_unit;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -40,7 +41,11 @@ public class DataCenterUtil {
} }
public static List<JSONObject> pullData(String url) { public static List<JSONObject> pullData(String url) {
JSONObject firstResponse = requestData(url, Map.of("", "")); return pullData(url, Map.of("", ""));
}
public static List<JSONObject> pullData(String url, Map<String, ?> params) {
JSONObject firstResponse = requestData(url, params);
JSONObject pageInfo = firstResponse.getJSONObject("pageInfo"); JSONObject pageInfo = firstResponse.getJSONObject("pageInfo");
Integer pages = pageInfo == null ? null : pageInfo.getInt("pages"); Integer pages = pageInfo == null ? null : pageInfo.getInt("pages");
if (pages == null || pages < 0) { if (pages == null || pages < 0) {
@@ -48,7 +53,9 @@ public class DataCenterUtil {
} }
List<JSONObject> records = new ArrayList<>(); List<JSONObject> records = new ArrayList<>();
for (int page = 1; page <= pages; page++) { for (int page = 1; page <= pages; page++) {
JSONObject response = requestData(url, Map.of("pageNum", page)); Map<String, Object> body = new HashMap<>(params);
body.put("pageNum", page);
JSONObject response = requestData(url, body);
JSONArray data = response.getJSONArray("dataSet"); JSONArray data = response.getJSONArray("dataSet");
if (data == null) { if (data == null) {
throw new BaseException("数据中心接口第{}页缺少 dataSet", page); throw new BaseException("数据中心接口第{}页缺少 dataSet", page);
@@ -58,6 +65,16 @@ public class DataCenterUtil {
return records; return records;
} }
public static JSONObject queryPage(String url, String zgh, int pageNumber, int pageSize) {
Map<String, Object> body = new HashMap<>();
body.put("pageNum", pageNumber);
body.put("pageSize", pageSize);
if (StrUtil.isNotBlank(zgh)) {
body.put("zgh", zgh.trim());
}
return requestData(url, body);
}
private static JSONObject requestData(String url, Map<String, ?> body) { private static JSONObject requestData(String url, Map<String, ?> body) {
try (HttpResponse response = HttpUtil.createPost(url) try (HttpResponse response = HttpUtil.createPost(url)
.body(JSONUtil.toJsonStr(body)).execute()) { .body(JSONUtil.toJsonStr(body)).execute()) {
@@ -1,62 +1,50 @@
package com.budwk.app.task.job; package com.budwk.app.task.job;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.param.ConditionGroup; import com.budwk.app.base.param.ConditionGroup;
import com.budwk.app.sys.enums.SysDataUpdateMode; import com.budwk.app.sys.enums.SysDataUpdateMode;
import com.budwk.app.sys.models.Sys_user_source;
import com.budwk.app.sys.param.SysDataUserUpdateParam; import com.budwk.app.sys.param.SysDataUserUpdateParam;
import com.budwk.app.sys.services.SysDataUserPullService; import com.budwk.app.sys.services.SysDataUserPullService;
import com.budwk.app.sys.services.SysDataUserUpdateService; import com.budwk.app.sys.services.SysDataUserUpdateService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd; import org.nutz.ioc.Ioc;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json; import org.nutz.json.Json;
import org.nutz.lang.util.NutMap; import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job; import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext; import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException; import org.quartz.JobExecutionException;
import java.util.Date;
/** /**
* 人员全量更新任务 * 人员全量更新任务
*/ */
@IocBean @IocBean
@Slf4j @Slf4j
@DisallowConcurrentExecution
public class SysUserAllRenewJob implements Job { public class SysUserAllRenewJob implements Job {
@Inject @Inject("refer:$ioc")
private Dao dao; private Ioc ioc;
@Inject @Inject
private SysDataUserPullService sysDataUserPullService; private SysDataUserPullService sysDataUserPullService;
@Inject
private SysDataUserUpdateService sysDataUserUpdateService;
@Override @Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date pullTime = sysDataUserPullService.pull();
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
NutMap conditionGroupMap = (NutMap)jobDataMap.get("conditionGroup");
if(conditionGroupMap == null || conditionGroupMap.isEmpty()){
log.error("SysUserAllRenewJob定时任务执行中止,原因:conditionGroup为null");
}
String conditionGroupStr = Json.toJson(conditionGroupMap).replace("@currentDate", DateUtil.today());
ConditionGroup conditionGroup = null;
try { try {
conditionGroup = Json.fromJson(ConditionGroup.class, conditionGroupStr); SysDataUserUpdateParam param = new SysDataUserUpdateParam();
param.setUpdateMode(SysDataUpdateMode.ALL.name());
Object conditionGroup = jobExecutionContext.getMergedJobDataMap().get("conditionGroup");
if (conditionGroup != null) {
String conditionGroupJson = Json.toJson(conditionGroup).replace("@currentDate", DateUtil.today());
param.setConditionGroup(Json.fromJson(ConditionGroup.class, conditionGroupJson));
}
param.setPullTime(DateUtil.formatDateTime(sysDataUserPullService.pull()));
SysDataUserUpdateService updateService = (SysDataUserUpdateService) ioc.get(SysDataUpdateMode.ALL.getClazz());
log.info("人员全量更新任务,拉取批次 {}:{}", param.getPullTime(), updateService.update(param));
} catch (Exception e) { } catch (Exception e) {
log.error("SysUserAllRenewJob定时任务执行失败,原因:{}", e.getMessage()); throw new JobExecutionException("人员拉取及全量更新任务执行失败", e);
} }
SysDataUserUpdateParam param = new SysDataUserUpdateParam();
param.setPullTime(DateUtil.formatDateTime(pullTime));
param.setUpdateMode(SysDataUpdateMode.ALL.name());
param.setConditionGroup(conditionGroup);
// sysDataUserUpdateService.update(param);
} }
} }
@@ -27,6 +27,6 @@ public class TestJob implements Job {
String taskId = context.getJobDetail().getKey().getName(); String taskId = context.getJobDetail().getKey().getName();
String hi = data.getString("hi"); String hi = data.getString("hi");
log.info("Test Job hi::" + hi); log.info("Test Job hi::" + hi);
sysTaskService.update(Chain.make("exeAt", (int) (System.currentTimeMillis() / 1000)).add("exeResult", "执行成功"), Cnd.where("id", "=", taskId)); sysTaskService.update(Chain.make("exeResult", "执行成功"), Cnd.where("id", "=", taskId));
} }
} }
@@ -17,7 +17,7 @@ import org.quartz.JobExecutionException;
* @Author JyuHsin * @Author JyuHsin
* @Date 2026/1/22 16:56 * @Date 2026/1/22 16:56
* @Version 1.0 * @Version 1.0
* @Description 每年的1月1日生成社团成员当年的缴费记录 * @Description 每年的1月1日生成协会成员当年的缴费记录
*/ */
@IocBean @IocBean
@DisallowConcurrentExecution @DisallowConcurrentExecution
@@ -0,0 +1,108 @@
package com.budwk.app.task.log;
import com.budwk.app.sys.models.Sys_task;
import com.budwk.app.sys.models.Sys_task_execution;
import com.budwk.app.sys.models.Sys_task_execution.Status;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.trans.Trans;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.listeners.JobListenerSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.UUID;
@IocBean
public class TaskExecutionListener extends JobListenerSupport {
private static final Logger log = LoggerFactory.getLogger(TaskExecutionListener.class);
@Inject
private Dao dao;
@Inject
private Scheduler scheduler;
public void register() throws SchedulerException {
if (!dao.exists(Sys_task_execution.class)) {
dao.create(Sys_task_execution.class, false);
}
dao.update(Sys_task_execution.class, Chain.make("status", Status.INTERRUPTED.name()),
Cnd.where("status", "=", Status.RUNNING.name()));
scheduler.getListenerManager().addJobListener(this);
}
@Override
public String getName() {
return "sysTaskExecutionListener";
}
@Override
public void jobToBeExecuted(JobExecutionContext context) {
JobKey key = context.getJobDetail().getKey();
context.put(getName(), null);
if (!key.getName().equals(key.getGroup())) {
return;
}
try {
if (dao.fetch(Sys_task.class, key.getName()) == null) {
return;
}
Sys_task_execution execution = new Sys_task_execution();
execution.setId(UUID.randomUUID().toString().replace("-", ""));
execution.setTaskId(key.getName());
execution.setStartedAt(System.currentTimeMillis());
execution.setStatus(Status.RUNNING.name());
Trans.exec(() -> {
dao.insert(execution);
long startedAtSeconds = execution.getStartedAt() / 1000;
dao.execute(Sqls.create("UPDATE sys_task SET exeAt = @startedAt WHERE id = @id AND (exeAt IS NULL OR exeAt < @startedAt)")
.setParam("startedAt", startedAtSeconds).setParam("id", execution.getTaskId()));
});
context.put(getName(), execution.getId());
context.setResult(null);
TaskExecutionLogAppender.begin();
log.info("开始执行任务 {}", key.getName());
} catch (RuntimeException e) {
log.error("记录任务开始时间失败,任务 {}", key, e);
}
}
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException exception) {
String id = (String) context.get(getName());
if (id == null) {
return;
}
Status status = exception != null ? Status.FAILED
: context.getResult() instanceof Status result ? result : Status.SUCCESS;
String executionLog = TaskExecutionLogAppender.end("\n任务执行结束,状态 " + status + "\n");
try {
if (exception != null) {
StringWriter stack = new StringWriter();
exception.printStackTrace(new PrintWriter(stack));
String detail = stack.toString();
if (detail.length() > 8000) {
int end = Character.isHighSurrogate(detail.charAt(7999)) ? 7999 : 8000;
detail = detail.substring(0, end) + "\n[异常堆栈已截断]";
}
executionLog += (exception.refireImmediately() ? "Quartz 将立即重试\n" : "") + detail;
}
dao.update(Sys_task_execution.class,
Chain.make("status", status.name()).add("finishedAt", System.currentTimeMillis())
.add("executionLog", executionLog),
Cnd.where("id", "=", id));
} catch (RuntimeException e) {
log.error("保存任务执行结果失败,记录 {}", id, e);
}
}
}
@@ -0,0 +1,49 @@
package com.budwk.app.task.log;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class TaskExecutionLogAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
private static final int MAX_LENGTH = 16000;
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss.SSS")
.withZone(ZoneId.of("Asia/Shanghai"));
private static final ThreadLocal<StringBuilder> CURRENT_LOG = new ThreadLocal<>();
static void begin() {
CURRENT_LOG.set(new StringBuilder());
}
static String end(String outcome) {
StringBuilder log = CURRENT_LOG.get();
CURRENT_LOG.remove();
return (log == null ? "" : log.toString()) + outcome;
}
@Override
protected void append(ILoggingEvent event) {
StringBuilder log = CURRENT_LOG.get();
if (log == null || log.length() >= MAX_LENGTH || !event.getLevel().isGreaterOrEqual(Level.INFO)) {
return;
}
log.append(TIME_FORMAT.format(Instant.ofEpochMilli(event.getTimeStamp())))
.append(" ").append(event.getLevel()).append(" ").append(event.getLoggerName())
.append(" - ").append(event.getFormattedMessage()).append('\n');
if (event.getThrowableProxy() != null) {
log.append(ThrowableProxyUtil.asString(event.getThrowableProxy()));
}
if (log.length() >= MAX_LENGTH) {
int end = MAX_LENGTH;
if (Character.isHighSurrogate(log.charAt(end - 1))) {
end--;
}
log.setLength(end);
log.append("\n[后续日志已截断]\n");
}
}
}
@@ -1,5 +1,7 @@
package com.budwk.app.task.services; package com.budwk.app.task.services;
import com.budwk.app.sys.models.Sys_task;
import java.util.List; import java.util.List;
/** /**
@@ -15,6 +17,10 @@ public interface TaskPlatformService {
*/ */
boolean isExist(String jobName, String jobGroup); boolean isExist(String jobName, String jobGroup);
void runOnce(Sys_task task) throws Exception;
void restore(Sys_task task) throws Exception;
/** /**
* 添加新任务 * 添加新任务
* *
@@ -1,28 +1,45 @@
package com.budwk.app.task.services.impl; package com.budwk.app.task.services.impl;
import com.budwk.app.sys.models.Sys_task;
import com.budwk.app.task.services.TaskPlatformService; import com.budwk.app.task.services.TaskPlatformService;
import org.nutz.integration.quartz.QuartzJob;
import org.nutz.integration.quartz.QuartzManager; import org.nutz.integration.quartz.QuartzManager;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.quartz.CronExpression; import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey; import org.quartz.JobKey;
import org.quartz.TriggerUtils; import org.quartz.Scheduler;
import org.quartz.impl.triggers.CronTriggerImpl; import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.TimeZone;
/** /**
* Created by wizzer on 2018/3/19. * Created by wizzer on 2018/3/19.
*/ */
@IocBean @IocBean
public class TaskPlatformServiceImpl implements TaskPlatformService { public class TaskPlatformServiceImpl implements TaskPlatformService {
private static final TimeZone TASK_TIME_ZONE = TimeZone.getTimeZone("Asia/Shanghai");
@Inject @Inject
private QuartzManager quartzManager; private QuartzManager quartzManager;
@Inject
private Scheduler scheduler;
/** /**
* 判断任务是否存在 * 判断任务是否存在
@@ -35,6 +52,40 @@ public class TaskPlatformServiceImpl implements TaskPlatformService {
return quartzManager.exist(new JobKey(jobName, jobGroup)); return quartzManager.exist(new JobKey(jobName, jobGroup));
} }
@Override
public void runOnce(Sys_task task) throws Exception {
JobKey key = JobKey.jobKey(task.getId(), task.getId());
if (scheduler.checkExists(key)) {
scheduler.triggerJob(key);
return;
}
JobDetail job = buildJob(key, task.getJobClass(), task.getData());
scheduler.scheduleJob(job, TriggerBuilder.newTrigger().forJob(key).startNow().build());
}
private JobDetail buildJob(JobKey key, String className, String dataMap) {
Class<? extends Job> jobClass;
try {
jobClass = Class.forName(Strings.sNull(className).trim(), false, getClass().getClassLoader())
.asSubclass(Job.class);
} catch (ClassNotFoundException | ClassCastException e) {
throw new IllegalArgumentException("执行类必须是有效的 Quartz Job", e);
}
JobDataMap data = new JobDataMap();
if (Strings.isNotBlank(dataMap)) {
try {
NutMap params = Json.fromJson(NutMap.class, dataMap);
if (params != null) {
data.putAll(params);
}
} catch (RuntimeException e) {
throw new IllegalArgumentException("执行参数必须是有效的 JSON 对象", e);
}
}
return JobBuilder.newJob(jobClass).withIdentity(key).usingJobData(data).build();
}
/** /**
* 添加新任务 * 添加新任务
* *
@@ -46,14 +97,55 @@ public class TaskPlatformServiceImpl implements TaskPlatformService {
* @param dataMap * @param dataMap
*/ */
public void add(String jobName, String jobGroup, String className, String cron, String comment, String dataMap) { public void add(String jobName, String jobGroup, String className, String cron, String comment, String dataMap) {
QuartzJob qj = new QuartzJob(); JobKey key = JobKey.jobKey(jobName, jobGroup);
qj.setJobName(jobName); JobDetail job = buildJob(key, className, dataMap);
qj.setJobGroup(jobGroup); CronExpression expression = parseCron(cron);
qj.setClassName(className); if (expression.getNextValidTimeAfter(new Date()) == null) {
qj.setCron(cron); throw new IllegalArgumentException("Cron 表达式没有未来执行时间");
qj.setComment(comment); }
qj.setDataMap(dataMap); schedule(job, expression);
quartzManager.add(qj); }
@Override
public void restore(Sys_task task) throws SchedulerException {
JobKey key = JobKey.jobKey(task.getId(), task.getId());
if (task.isDisabled()) {
scheduler.deleteJob(key);
return;
}
CronExpression expression = parseCron(task.getCron());
JobDetail job = buildJob(key, task.getJobClass(), task.getData());
JobDetail existingJob = scheduler.getJobDetail(key);
if (existingJob != null
&& existingJob.getJobClass().equals(job.getJobClass())
&& existingJob.getJobDataMap().equals(job.getJobDataMap())
&& scheduler.getTrigger(TriggerKey.triggerKey(key.getName(), key.getGroup())) instanceof CronTrigger trigger
&& trigger.getCronExpression().equals(expression.getCronExpression())
&& trigger.getTimeZone().hasSameRules(TASK_TIME_ZONE)
&& trigger.getNextFireTime() != null) {
scheduler.resetTriggerFromErrorState(trigger.getKey());
return;
}
if (expression.getNextValidTimeAfter(new Date()) != null) {
schedule(job, expression);
} else if (existingJob != null) {
scheduler.deleteJob(key);
}
}
private void schedule(JobDetail job, CronExpression expression) {
JobKey key = job.getKey();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(key.getName(), key.getGroup())
.forJob(key)
.withSchedule(CronScheduleBuilder.cronSchedule(expression))
.build();
try {
scheduler.scheduleJob(job, Set.of(trigger), true);
} catch (SchedulerException e) {
throw new IllegalStateException("Quartz 调度注册失败", e);
}
} }
/** /**
@@ -64,10 +156,7 @@ public class TaskPlatformServiceImpl implements TaskPlatformService {
* @return * @return
*/ */
public boolean delete(String jobName, String jobGroup) { public boolean delete(String jobName, String jobGroup) {
QuartzJob qj = new QuartzJob(); return quartzManager.delete(JobKey.jobKey(jobName, jobGroup));
qj.setJobName(jobName);
qj.setJobGroup(jobGroup);
return quartzManager.delete(qj);
} }
/** /**
@@ -83,36 +172,40 @@ public class TaskPlatformServiceImpl implements TaskPlatformService {
* @param cronExpression * @param cronExpression
* @return * @return
*/ */
public List<String> getCronExeTimes(String cronExpression) throws Exception { @Override
List<String> list = new ArrayList<>(); public List<String> getCronExeTimes(String cronExpression) {
CronTriggerImpl cronTriggerImpl = new CronTriggerImpl(); CronExpression expression = parseCron(cronExpression);
cronTriggerImpl.setCronExpression(cronExpression);
Calendar calendar = Calendar.getInstance();
Date now = calendar.getTime();
calendar.add(2, 1);
List<Date> dates = TriggerUtils.computeFireTimesBetween(cronTriggerImpl, null, now, calendar.getTime());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (int i = 0; i < dates.size() && i <= 4; ++i) { dateFormat.setTimeZone(TASK_TIME_ZONE);
list.add(dateFormat.format((Date) dates.get(i))); List<String> times = new ArrayList<>(5);
Date time = new Date();
for (int i = 0; i < 5; i++) {
time = expression.getNextValidTimeAfter(time);
if (time == null) {
break;
}
times.add(dateFormat.format(time));
} }
return list; return times;
} }
@Override @Override
public List<String> getCronExeTimesPlus(String cronExpression) throws Exception { public List<String> getCronExeTimesPlus(String cronExpression) {
// 创建一个列表来存储执行时间 return getCronExeTimes(cronExpression);
List<String> executionTimes = new ArrayList<>(); }
// 创建 CronTriggerImpl 对象,并设置 cron 表达式
CronTriggerImpl cronTrigger = new CronTriggerImpl(); private CronExpression parseCron(String cron) {
cronTrigger.setCronExpression(new CronExpression(cronExpression)); String normalized = Strings.sNull(cron).trim().replaceAll("\\s+", " ");
// 计算未来的执行时间 int fieldCount = normalized.split(" ").length;
List<Date> nextExecutionTimes = TriggerUtils.computeFireTimes(cronTrigger, null, 5); if (fieldCount < 6 || fieldCount > 7) {
// 格式化日期并添加到执行时间列表中 throw new IllegalArgumentException("Quartz Cron 表达式必须包含 6 或 7 个字段");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); }
for (Date date : nextExecutionTimes) { try {
executionTimes.add(dateFormat.format(date)); CronExpression expression = new CronExpression(normalized);
expression.setTimeZone(TASK_TIME_ZONE);
return expression;
} catch (ParseException e) {
throw new IllegalArgumentException("无效的 Quartz Cron 表达式:" + e.getMessage(), e);
} }
return executionTimes;
} }
} }
@@ -56,7 +56,7 @@ public class SecurityUtil {
} }
/** /**
* 获取当前用户社团ID * 获取当前用户协会ID
*/ */
public static List<String> getClubIds() { public static List<String> getClubIds() {
String clubIds = getSession().get("clubIds").toString(); String clubIds = getSession().get("clubIds").toString();
@@ -16,6 +16,8 @@ import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings; import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap; import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -25,6 +27,7 @@ import java.util.Set;
@IocBean(create = "init") @IocBean(create = "init")
public class Globals { public class Globals {
private static final Log log = Logs.get();
//项目路径 //项目路径
public static String AppRoot = ""; public static String AppRoot = "";
//项目目录 //项目目录
@@ -69,15 +72,12 @@ public class Globals {
} }
public void initTask(SysTaskService sysTaskService) { public void initTask(SysTaskService sysTaskService) {
taskPlatformService.clear();
List<Sys_task> taskList = sysTaskService.query(); List<Sys_task> taskList = sysTaskService.query();
for (Sys_task sysTask : taskList) { for (Sys_task sysTask : taskList) {
try { try {
if (!sysTask.isDisabled())//不存在则新建 taskPlatformService.restore(sysTask);
taskPlatformService.add(sysTask.getId(), sysTask.getId(), sysTask.getJobClass(), sysTask.getCron()
, sysTask.getNote(), sysTask.getData());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); log.error("恢复定时任务失败:" + sysTask.getName() + " (" + sysTask.getId() + ")", e);
} }
} }
} }
@@ -115,7 +115,7 @@ public class CommonController {
@At @At
@SaCheckLogin @SaCheckLogin
@ApiOperation("社团列表") @ApiOperation("协会列表")
public Result clubOptions(String clubId) { public Result clubOptions(String clubId) {
List<SysClub> sysClubs = sysClubService.dao().query(SysClub.class, List<SysClub> sysClubs = sysClubService.dao().query(SysClub.class,
Cnd.NEW().andEX("id", "=", clubId)); Cnd.NEW().andEX("id", "=", clubId));
@@ -355,7 +355,7 @@ public class ActivityBasicScopeController {
boolean isBranchUnionChairman = AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()); boolean isBranchUnionChairman = AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name());
boolean isClubPresident = AuthUtil.hasRole(RoleConstant.CLUB_PRESIDENT.name()); boolean isClubPresident = AuthUtil.hasRole(RoleConstant.CLUB_PRESIDENT.name());
if (isBranchUnionChairman && isClubPresident) { if (isBranchUnionChairman && isClubPresident) {
// 同时具备分工会主席和社团会长身份时,未额外筛选前默认查看两类权限范围的并集。 // 同时具备分工会主席和协会会长身份时,未额外筛选前默认查看两类权限范围的并集。
List<String> manageClubIdList = sysClubService.getMyManageClub().stream().map(SysClub::getId).toList(); List<String> manageClubIdList = sysClubService.getMyManageClub().stream().map(SysClub::getId).toList();
SqlExpressionGroup scopeGroup = new SqlExpressionGroup(); SqlExpressionGroup scopeGroup = new SqlExpressionGroup();
scopeGroup.or("u.unionid", "=", SecurityUtil.getUnionId()); scopeGroup.or("u.unionid", "=", SecurityUtil.getUnionId());
@@ -364,7 +364,7 @@ public class ActivityBasicScopeController {
} }
cnd.and(scopeGroup); cnd.and(scopeGroup);
} else if (isClubPresident) { } else if (isClubPresident) {
// 社团会长设置活动人员范围时,只允许查看自己可管理社团下的成员。 // 协会会长设置活动人员范围时,只允许查看自己可管理协会下的成员。
List<String> manageClubIdList = sysClubService.getMyManageClub().stream().map(SysClub::getId).toList(); List<String> manageClubIdList = sysClubService.getMyManageClub().stream().map(SysClub::getId).toList();
if (manageClubIdList.isEmpty()) { if (manageClubIdList.isEmpty()) {
cnd.and("clubuser.clubid", "in", List.of("__EMPTY_CLUB_ID__")); cnd.and("clubuser.clubid", "in", List.of("__EMPTY_CLUB_ID__"));
@@ -162,7 +162,7 @@ public class ActivityCultureUserStatisticsController {
if (activity_type == 40002) { if (activity_type == 40002) {
cnd.and("tissue.unionId", "=", SecurityUtil.getUnionId()); cnd.and("tissue.unionId", "=", SecurityUtil.getUnionId());
} else if (activity_type == 40003) { } else if (activity_type == 40003) {
// 协会会长进入社团报名统计时,只允许查看自己管理社团下的活动名称。 // 协会会长进入协会报名统计时,只允许查看自己管理协会下的活动名称。
List<SysClub> myManageClub = sysClubService.getMyManageClub(); List<SysClub> myManageClub = sysClubService.getMyManageClub();
List<String> clubIdList = myManageClub.stream().map(SysClub::getId).collect(Collectors.toList()); List<String> clubIdList = myManageClub.stream().map(SysClub::getId).collect(Collectors.toList());
cnd.and("tissue.clubId", "in", clubIdList); cnd.and("tissue.clubId", "in", clubIdList);
@@ -19,7 +19,7 @@ import java.util.List;
/** /**
* @author: Aaron * @author: Aaron
* @create: 2021-03-09 13:57 * @create: 2021-03-09 13:57
* @description: 社团或分工会活动校工会活动 * @description: 协会或分工会活动校工会活动
**/ **/
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@@ -147,7 +147,7 @@ public class ActivityTissue extends BaseModel implements Serializable, SysHomeCo
private String unionId; private String unionId;
@Column @Column
@Comment("创建人所属社团") @Comment("创建人所属协会")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String clubId; private String clubId;
@@ -28,7 +28,7 @@ public class ActivityTissuePerson extends BaseModel implements Serializable {
private String id; private String id;
@Column @Column
@Comment("分工会或社团活动ID") @Comment("分工会或协会活动ID")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String tissueId; private String tissueId;
@@ -30,7 +30,7 @@ import java.util.List;
*/ */
@IocBean @IocBean
@Ok("json") @Ok("json")
@ApiOperation("活动申报,社团负责人审核") @ApiOperation("活动申报,协会负责人审核")
@At("/platform/activityDeclare/clubPrincipal") @At("/platform/activityDeclare/clubPrincipal")
public class ActivityDeclareClubPrincipalController { public class ActivityDeclareClubPrincipalController {
@@ -51,7 +51,7 @@ public class ActivityDeclareClubPrincipalController {
@At @At
@ApiOperation("活动申报,社团负责人审核列表") @ApiOperation("活动申报,协会负责人审核列表")
@SaCheckPermission("activityDeclare.clubPrincipal") @SaCheckPermission("activityDeclare.clubPrincipal")
public Result pageData(CommonPageParam pageParam) { public Result pageData(CommonPageParam pageParam) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
@@ -222,8 +222,8 @@ public class ActivityDeclareMineController {
docData.put("fgh", approval); docData.put("fgh", approval);
}); });
// 社团负责人审核 // 协会负责人审核
doneTaskVos.stream().filter(task -> "社团负责人审核".equals(task.getDisplayName())) doneTaskVos.stream().filter(task -> "协会负责人审核".equals(task.getDisplayName()))
.max(Comparator.comparing(ProcessTaskVO::getCreatedAt)) .max(Comparator.comparing(ProcessTaskVO::getCreatedAt))
.ifPresent(v -> { .ifPresent(v -> {
Dict taskFormData = v.getTaskFormData(); Dict taskFormData = v.getTaskFormData();
@@ -58,7 +58,7 @@ public class ActivityDeclareInfo extends BaseModel {
private String mobile; private String mobile;
@Column @Column
@Comment("申报单位(校工会/二级工会名称/社团名称)") @Comment("申报单位(校工会/二级工会名称/协会名称)")
@ColDefine(type = ColType.VARCHAR, width = 200) @ColDefine(type = ColType.VARCHAR, width = 200)
private String declareUnitName; private String declareUnitName;
@@ -68,7 +68,7 @@ public class ActivityDeclareInfo extends BaseModel {
private String unionId; private String unionId;
@Column @Column
@Comment("社团id") @Comment("协会id")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String clubId; private String clubId;
@@ -25,9 +25,9 @@ public enum ActivityDeclareReimbursement {
*/ */
BRANCH_UNION("分工会活动"), BRANCH_UNION("分工会活动"),
/** /**
* 社团 * 协会
*/ */
CLUB("社团活动"); CLUB("协会活动");
private final String typeName; private final String typeName;
} }
@@ -110,7 +110,7 @@ public class ActivityReimbursementBoardController {
list.add(new NutMap() {{ list.add(new NutMap() {{
put("year", year); put("year", year);
put("type", "社团活动"); put("type", "协会活动");
put("num", activityReimbursementService.count(sql.setParam("type", "ACTIVITY_BUDGET_TYPE_THREE").setParam("year", year))); put("num", activityReimbursementService.count(sql.setParam("type", "ACTIVITY_BUDGET_TYPE_THREE").setParam("year", year)));
}}); }});
} }
@@ -145,7 +145,7 @@ public class ActivityReimbursementBoardController {
put("num", activityReimbursementService.count(sql.setParam("type", "ACTIVITY_BUDGET_TYPE_TWO"))); put("num", activityReimbursementService.count(sql.setParam("type", "ACTIVITY_BUDGET_TYPE_TWO")));
}}); }});
list.add(new NutMap() {{ list.add(new NutMap() {{
put("type", "社团活动"); put("type", "协会活动");
put("num", activityReimbursementService.count(sql.setParam("type", "ACTIVITY_BUDGET_TYPE_THREE"))); put("num", activityReimbursementService.count(sql.setParam("type", "ACTIVITY_BUDGET_TYPE_THREE")));
}}); }});
return Result.success(list); return Result.success(list);
@@ -191,7 +191,7 @@ public class ActivityReimbursementBoardController {
list.add(new NutMap() {{ list.add(new NutMap() {{
put("year", year); put("year", year);
put("type", "社团费用"); put("type", "协会费用");
put("num", activityReimbursementService.list(sql.setParam("type", "ACTIVITY_BUDGET_TYPE_THREE").setParam("year", year)).get(0).getDouble("money")); put("num", activityReimbursementService.list(sql.setParam("type", "ACTIVITY_BUDGET_TYPE_THREE").setParam("year", year)).get(0).getDouble("money"));
}}); }});
} }
@@ -218,7 +218,7 @@ public class ActivityReimbursementBoardController {
put("num", activityReimbursementService.count(unionSql)); put("num", activityReimbursementService.count(unionSql));
}}); }});
list.add(new NutMap() {{ list.add(new NutMap() {{
put("type", "社团费用"); put("type", "协会费用");
put("num", activityReimbursementService.count(clubSql)); put("num", activityReimbursementService.count(clubSql));
}}); }});
return Result.success().addData(list); return Result.success().addData(list);
@@ -250,7 +250,7 @@ public class ActivityReimbursementBoardController {
@At @At
@ApiOperation("所有社团的报销费用或活动数统计") @ApiOperation("所有协会的报销费用或活动数统计")
@SaCheckPermission("activityReimbursement.board") @SaCheckPermission("activityReimbursement.board")
public Result allClubActivityAndMoney(Integer year, Boolean isClubMoney) { public Result allClubActivityAndMoney(Integer year, Boolean isClubMoney) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
@@ -30,7 +30,7 @@ import java.util.List;
*/ */
@IocBean @IocBean
@Ok("json") @Ok("json")
@ApiOperation("活动报销,社团负责人审核") @ApiOperation("活动报销,协会负责人审核")
@At("/platform/activityReimbursement/clubPrincipal") @At("/platform/activityReimbursement/clubPrincipal")
public class ActivityReimbursementClubPrincipalController { public class ActivityReimbursementClubPrincipalController {
@@ -51,7 +51,7 @@ public class ActivityReimbursementClubPrincipalController {
@At @At
@ApiOperation("活动报销,社团负责人审核列表") @ApiOperation("活动报销,协会负责人审核列表")
@SaCheckPermission("activityReimbursement.clubPrincipal") @SaCheckPermission("activityReimbursement.clubPrincipal")
public Result pageData(CommonPageParam pageParam) { public Result pageData(CommonPageParam pageParam) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
@@ -323,8 +323,8 @@ public class ActivityReimbursementMineController {
docData.put("fgh", approval); docData.put("fgh", approval);
}); });
// 社团负责人审核 // 协会负责人审核
doneTaskVos.stream().filter(task -> "社团负责人审核".equals(task.getDisplayName())) doneTaskVos.stream().filter(task -> "协会负责人审核".equals(task.getDisplayName()))
.max(Comparator.comparing(ProcessTaskVO::getCreatedAt)) .max(Comparator.comparing(ProcessTaskVO::getCreatedAt))
.ifPresent(v -> { .ifPresent(v -> {
Dict taskFormData = v.getTaskFormData(); Dict taskFormData = v.getTaskFormData();
@@ -141,7 +141,7 @@ public class YearPlanController {
} }
@At @At
@ApiOperation("获取社团") @ApiOperation("获取协会")
@SaCheckPermission("planSummary.yearPlan.manage") @SaCheckPermission("planSummary.yearPlan.manage")
public Result queryClub() { public Result queryClub() {
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
@@ -51,17 +51,17 @@ public class YearPlan extends BaseModel {
private Double money; private Double money;
@Column @Column
@Comment("计划类型(40001-校活动、40002-分工会活动、40003-社团社团活动)") @Comment("计划类型(40001-校活动、40002-分工会活动、40003-协会协会活动)")
@ColDefine(type = ColType.VARCHAR, width = 7) @ColDefine(type = ColType.VARCHAR, width = 7)
private String planType; private String planType;
@Column @Column
@Comment("分工会ID或社团ID") @Comment("分工会ID或协会ID")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String tissueId; private String tissueId;
@Column @Column
@Comment("分工会或社团") @Comment("分工会或协会")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String tissueName; private String tissueName;
@@ -72,12 +72,12 @@ public class YearSummary extends BaseModel {
private String cTime; private String cTime;
@Column @Column
@Comment("工会/社团id") @Comment("工会/协会id")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String tissueId; private String tissueId;
@Column @Column
@Comment("分工会或社团") @Comment("分工会或协会")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String tissueName; private String tissueName;
@@ -87,7 +87,7 @@ public class YearSummary extends BaseModel {
private String summaryType; private String summaryType;
@Column @Column
@Comment("总结来源(1-校活动、2-分工会活动、3社团社团活动)") @Comment("总结来源(1-校活动、2-分工会活动、3协会协会活动)")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String summaryFrom; private String summaryFrom;
@@ -162,7 +162,7 @@ public class ActivitySchool extends BaseModel implements Serializable , SysHomeC
private Boolean close; private Boolean close;
@Column @Column
@Comment("活动级别(40001-校级、40002-分工会、40003-社团或社团)") @Comment("活动级别(40001-校级、40002-分工会、40003-协会或协会)")
@ColDefine(type = ColType.INT, width = 10) @ColDefine(type = ColType.INT, width = 10)
private Integer activityLevel; private Integer activityLevel;
@@ -198,7 +198,7 @@ public class ActivitySchool extends BaseModel implements Serializable , SysHomeC
private String belongUnionId; private String belongUnionId;
@Column @Column
@Comment("活动所属社团") @Comment("活动所属协会")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String belongClubId; private String belongClubId;
@@ -326,7 +326,7 @@ public class TrainSignUpManageController {
@At @At
@Ok("json:full") @Ok("json:full")
@ApiOperation("查询分工会和社团") @ApiOperation("查询分工会和协会")
@SaCheckPermission(value = {"trainSignUp.manage", "trainSignUp.applyActivity"}, mode = SaMode.OR) @SaCheckPermission(value = {"trainSignUp.manage", "trainSignUp.applyActivity"}, mode = SaMode.OR)
public Result selectUnitAndClub() { public Result selectUnitAndClub() {
List<NutMap> result = new ArrayList<>(); List<NutMap> result = new ArrayList<>();
@@ -49,9 +49,9 @@ public class ClubUserClubListController {
} }
/** /**
* 打开移动端社团列表,共用电脑端的社团查询及入退会状态。 * 打开移动端协会列表,共用电脑端的协会查询及入退会状态。
* *
* @return 无返回值,由模板渲染社团列表页面 * @return 无返回值,由模板渲染协会列表页面
*/ */
@At("/h5") @At("/h5")
@Ok("beetl:/platform/zhghh5/club/list/index.html") @Ok("beetl:/platform/zhghh5/club/list/index.html")
@@ -85,7 +85,7 @@ public class ClubUserClubListController {
@At @At
@SaCheckPermission(value = {"club.join.list", "h5.club.join.list", "h5.club.join.apply"}, mode = SaMode.OR) @SaCheckPermission(value = {"club.join.list", "h5.club.join.list", "h5.club.join.apply"}, mode = SaMode.OR)
public Result getCount() { public Result getCount() {
// 获取所有社团 // 获取所有协会
List<SysClub> list = sysClubService.query(); List<SysClub> list = sysClubService.query();
// 获取审核通过的 // 获取审核通过的
List<ProcessInstance> instanceList = sysClubService.dao().query( List<ProcessInstance> instanceList = sysClubService.dao().query(
@@ -51,7 +51,7 @@ import java.util.List;
@IocBean @IocBean
@At("/platform/club/join/apply") @At("/platform/club/join/apply")
@Api("社团申请入会、退会") @Api("协会申请入会、退会")
@Ok("json:full") @Ok("json:full")
public class ClubUserJoinApplyController { public class ClubUserJoinApplyController {
@@ -75,12 +75,12 @@ public class ClubUserJoinApplyController {
} }
/** /**
* 移动端首次入会先选择社团,已选社团或已有申请继续进入申请表单。 * 移动端首次入会先选择协会,已选协会或已有申请继续进入申请表单。
* *
* @param clubId 已选择的社团ID * @param clubId 已选择的协会ID
* @param bizId 已有入会申请ID * @param bizId 已有入会申请ID
* @param taskId 重新提交申请时的流程任务ID * @param taskId 重新提交申请时的流程任务ID
* @return 无申请上下文时跳转社团列表,否则渲染原申请页面 * @return 无申请上下文时跳转协会列表,否则渲染原申请页面
*/ */
@At("/h5") @At("/h5")
@SaCheckPermission("h5.club.join.apply") @SaCheckPermission("h5.club.join.apply")
@@ -124,7 +124,7 @@ public class ClubUserJoinApplyController {
@ApiOperation("保存申请") @ApiOperation("保存申请")
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"club.join.apply", "h5.club.join.apply"}, mode = SaMode.OR) @SaCheckPermission(value = {"club.join.apply", "h5.club.join.apply"}, mode = SaMode.OR)
@SLog(tag = "社团管理系统-申请入会", msg = "保存社团入会") @SLog(tag = "协会管理系统-申请入会", msg = "保存协会入会")
public Result save(@Param("data") ClubUserApply clubUserApply) { public Result save(@Param("data") ClubUserApply clubUserApply) {
clubUserApply.setRoleCode(RoleConstant.CLUB_MEMBER.name()); clubUserApply.setRoleCode(RoleConstant.CLUB_MEMBER.name());
clubUserApply.setMode(1); clubUserApply.setMode(1);
@@ -138,7 +138,7 @@ public class ClubUserJoinApplyController {
@ApiOperation("提交") @ApiOperation("提交")
@SaCheckPermission(value = {"club.join.apply", "h5.club.join.apply"}, mode = SaMode.OR) @SaCheckPermission(value = {"club.join.apply", "h5.club.join.apply"}, mode = SaMode.OR)
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SLog(tag = "社团管理系统-申请入/退会", msg = "申请社团入/退会") @SLog(tag = "协会管理系统-申请入/退会", msg = "申请协会入/退会")
public Result submit(@Param("data") ClubUserApply clubUserApply, public Result submit(@Param("data") ClubUserApply clubUserApply,
@Param("mode") Boolean mode) { @Param("mode") Boolean mode) {
if (Boolean.TRUE.equals(mode)) { if (Boolean.TRUE.equals(mode)) {
@@ -150,14 +150,14 @@ public class ClubUserJoinApplyController {
if(mode == false) { if(mode == false) {
clubUserApply = clubUserJoinService.buildExitApply(clubUserApply.getClubId(), SecurityUtil.getUserId()); clubUserApply = clubUserJoinService.buildExitApply(clubUserApply.getClubId(), SecurityUtil.getUserId());
if (ObjectUtil.isEmpty(clubUserApply)) { if (ObjectUtil.isEmpty(clubUserApply)) {
return Result.error("当前不是该社团成员,无法申请退出!"); return Result.error("当前不是该协会成员,无法申请退出!");
} }
} }
ClubUserApply userApply = dao.fetch(ClubUserApply.class, Cnd.where("userId", "=", clubUserApply.getUserId()).and("clubId", "=", clubUserApply.getClubId()).and("mode", "=", mode).desc(ClubUserApply::getApplyDate)); ClubUserApply userApply = dao.fetch(ClubUserApply.class, Cnd.where("userId", "=", clubUserApply.getUserId()).and("clubId", "=", clubUserApply.getClubId()).and("mode", "=", mode).desc(ClubUserApply::getApplyDate));
if (ObjectUtil.isNotEmpty(userApply) && StrUtil.isNotBlank(userApply.getId())) { if (ObjectUtil.isNotEmpty(userApply) && StrUtil.isNotBlank(userApply.getId())) {
ProcessInstance processInstance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", userApply.getId())); ProcessInstance processInstance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", userApply.getId()));
if (processInstance != null && !List.of(ProcessInstanceStateEnum.REJECT.getCode(), ProcessInstanceStateEnum.FINISHED.getCode()).contains(processInstance.getState())) { if (processInstance != null && !List.of(ProcessInstanceStateEnum.REJECT.getCode(), ProcessInstanceStateEnum.FINISHED.getCode()).contains(processInstance.getState())) {
return Result.error("您有该社团的申请记录尚未完成,请到我的申请里查看!"); return Result.error("您有该协会的申请记录尚未完成,请到我的申请里查看!");
} }
} }
@@ -209,7 +209,7 @@ public class ClubUserJoinApplyController {
} }
@At @At
@ApiOperation("校验是否申请过社团") @ApiOperation("校验是否申请过协会")
@SaCheckPermission(value = {"club.join.apply", "h5.club.join.apply"}, mode = SaMode.OR) @SaCheckPermission(value = {"club.join.apply", "h5.club.join.apply"}, mode = SaMode.OR)
public Result checkApplyClub(@Valid String clubId) { public Result checkApplyClub(@Valid String clubId) {
String qualificationMsg = clubUserJoinService.checkJoinQualification(SecurityUtil.getUserId()); String qualificationMsg = clubUserJoinService.checkJoinQualification(SecurityUtil.getUserId());
@@ -234,10 +234,10 @@ public class ClubUserJoinApplyController {
ProcessInstance processInstance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", userApply.getId())); ProcessInstance processInstance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", userApply.getId()));
// 申请记录存在但没有流程实例时,说明用户仅保存了申请草稿,需提示其先到“我的申请”继续提交。 // 申请记录存在但没有流程实例时,说明用户仅保存了申请草稿,需提示其先到“我的申请”继续提交。
if (processInstance == null) { if (processInstance == null) {
return Result.error(99, "您有该社团的申请记录尚未提交,请到我的申请里查看!"); return Result.error(99, "您有该协会的申请记录尚未提交,请到我的申请里查看!");
} }
if (!List.of(ProcessInstanceStateEnum.REJECT.getCode(), ProcessInstanceStateEnum.FINISHED.getCode()).contains(processInstance.getState())) { if (!List.of(ProcessInstanceStateEnum.REJECT.getCode(), ProcessInstanceStateEnum.FINISHED.getCode()).contains(processInstance.getState())) {
return Result.error(99, "您有该社团的申请记录尚未完成,请到我的申请里查看!"); return Result.error(99, "您有该协会的申请记录尚未完成,请到我的申请里查看!");
} }
return Result.success(); return Result.success();
} }
@@ -59,8 +59,8 @@ public class ClubUserJoinMineController {
@SaCheckPermission(value = {"club.join.mine", "h5.club.join.mine"}, mode = SaMode.OR) @SaCheckPermission(value = {"club.join.mine", "h5.club.join.mine"}, mode = SaMode.OR)
/** /**
* 我的申请分页查询。 * 我的申请分页查询。
* pageForm 为分页参数,year 为申请年度,clubName 为可选的社团名称关键字; * pageForm 为分页参数,year 为申请年度,clubName 为可选的协会名称关键字;
* 返回分页后的当前用户社团申请记录。 * 返回分页后的当前用户协会申请记录。
*/ */
public Result pageData(@Valid PageForm pageForm, public Result pageData(@Valid PageForm pageForm,
@Param("year") Integer year, @Param("year") Integer year,
@@ -135,7 +135,7 @@ public class ClubUserJoinMineController {
@SaCheckPermission(value = {"club.join.mine", "h5.club.join.mine"}, mode = SaMode.OR) @SaCheckPermission(value = {"club.join.mine", "h5.club.join.mine"}, mode = SaMode.OR)
@ApiOperation("删除") @ApiOperation("删除")
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SLog(tag = "社团管理系统-申请入会-我的申请", msg = "删除社团入会") @SLog(tag = "协会管理系统-申请入会-我的申请", msg = "删除协会入会")
public Result delete(@Valid String id) { public Result delete(@Valid String id) {
dao.delete(ClubUserApply.class, id); dao.delete(ClubUserApply.class, id);
dao.delete(ClubUser.class, id); dao.delete(ClubUser.class, id);
@@ -51,8 +51,8 @@ public class ClubUserMineClubController {
@At @At
@SaCheckPermission(value = {"club.join.mine.club", "h5.club.join.mine.club"}, mode = SaMode.OR) @SaCheckPermission(value = {"club.join.mine.club", "h5.club.join.mine.club"}, mode = SaMode.OR)
/** /**
* 我的社团分页查询。 * 我的协会分页查询。
* pageForm 传入分页参数及可选社团名称;返回当前登录人已完成入会流程的社团列表, * pageForm 传入分页参数及可选协会名称;返回当前登录人已完成入会流程的协会列表,
* 每条记录同时包含当前成员姓名、工号和最近一次入会申请 ID,供 H5 卡片及详情页展示。 * 每条记录同时包含当前成员姓名、工号和最近一次入会申请 ID,供 H5 卡片及详情页展示。
*/ */
public Result pageData(@Valid ClubUserPageForm pageForm) { public Result pageData(@Valid ClubUserPageForm pageForm) {
@@ -79,7 +79,7 @@ public class ClubUserMineClubController {
FROM FROM
sys_club club sys_club club
LEFT JOIN club_user uc ON club.id = uc.clubId LEFT JOIN club_user uc ON club.id = uc.clubId
-- 查询当前登录人的成员资料及最近一次入会申请,避免卡片标题错误显示为社团名称。 -- 查询当前登录人的成员资料及最近一次入会申请,避免卡片标题错误显示为协会名称。
LEFT JOIN club_user currentClubUser ON currentClubUser.clubId = club.id AND currentClubUser.userId = @currentUserId LEFT JOIN club_user currentClubUser ON currentClubUser.clubId = club.id AND currentClubUser.userId = @currentUserId
LEFT JOIN sys_user currentUser ON currentUser.id = currentClubUser.userId LEFT JOIN sys_user currentUser ON currentUser.id = currentClubUser.userId
LEFT JOIN ( LEFT JOIN (
@@ -97,7 +97,7 @@ public class ClubUserMineClubController {
"""); """);
sql.setParam("currentUserId", SecurityUtil.getUserId()); sql.setParam("currentUserId", SecurityUtil.getUserId());
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
// 社团名称为可选搜索参数,输入关键字时按名称模糊匹配。 // 协会名称为可选搜索参数,输入关键字时按名称模糊匹配。
if (Strings.isNotBlank(pageForm.getClubName())) { if (Strings.isNotBlank(pageForm.getClubName())) {
cnd.and("club.clubName", "like", "%" + pageForm.getClubName().trim() + "%"); cnd.and("club.clubName", "like", "%" + pageForm.getClubName().trim() + "%");
} }
@@ -52,7 +52,7 @@ public class ClubCommonController {
private SysFileService sysFileService; private SysFileService sysFileService;
/** /**
* 上传社团缩略图,限制图片大小并复用系统文件服务校验图片内容及保存文件。 * 上传协会缩略图,限制图片大小并复用系统文件服务校验图片内容及保存文件。
* *
* @param file 用户上传的 JPG、PNG 或 GIF 图片,大小不得超过 2MB * @param file 用户上传的 JPG、PNG 或 GIF 图片,大小不得超过 2MB
* @return 成功时返回图片下载地址,文件无效或超过限制时返回错误提示 * @return 成功时返回图片下载地址,文件无效或超过限制时返回错误提示
@@ -63,14 +63,14 @@ public class ClubCommonController {
@SaCheckPermission(value = {"club.register.apply", "club.infoManage.manage"}, mode = SaMode.OR) @SaCheckPermission(value = {"club.register.apply", "club.infoManage.manage"}, mode = SaMode.OR)
public Result uploadThumbnail(@Param("file") TempFile file) { public Result uploadThumbnail(@Param("file") TempFile file) {
if (file == null || StrUtil.isBlank(file.getSubmittedFileName())) { if (file == null || StrUtil.isBlank(file.getSubmittedFileName())) {
return Result.error("请选择社团缩略图"); return Result.error("请选择协会缩略图");
} }
if (file.getSize() > 2L * 1024 * 1024) { if (file.getSize() > 2L * 1024 * 1024) {
return Result.error("社团缩略图大小不能超过2MB"); return Result.error("协会缩略图大小不能超过2MB");
} }
String suffix = FileUtil.extName(file.getSubmittedFileName()).toLowerCase(Locale.ROOT); String suffix = FileUtil.extName(file.getSubmittedFileName()).toLowerCase(Locale.ROOT);
if (!Set.of("jpg", "jpeg", "png", "gif").contains(suffix)) { if (!Set.of("jpg", "jpeg", "png", "gif").contains(suffix)) {
return Result.error("社团缩略图仅支持JPG、PNG、GIF图片"); return Result.error("协会缩略图仅支持JPG、PNG、GIF图片");
} }
try { try {
return Result.success().addData(sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), file)); return Result.success().addData(sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), file));
@@ -81,7 +81,7 @@ public class ClubCommonController {
@At @At
@SaCheckLogin @SaCheckLogin
/** 无请求参数;返回入会社团选项,每项包含 id、clubName 等社团字段,同一社团不重复。 */ /** 无请求参数;返回入会协会选项,每项包含 id、clubName 等协会字段,同一协会不重复。 */
public Result listClub() { public Result listClub() {
return Result.success(sysClubService.listJoinClubOptions()); return Result.success(sysClubService.listJoinClubOptions());
} }
@@ -114,7 +114,7 @@ public class ClubCommonController {
@At @At
@SaCheckLogin @SaCheckLogin
public Result listManageClubByRole() { public Result listManageClubByRole() {
// 活动人员范围设置只允许按“可管理的社团”选择,避免把仅作为成员加入的社团也展示出来。 // 活动人员范围设置只允许按“可管理的协会”选择,避免把仅作为成员加入的协会也展示出来。
List<SysClub> clubList = sysClubService.getMyManageClub(); List<SysClub> clubList = sysClubService.getMyManageClub();
return Result.success(clubList); return Result.success(clubList);
} }
@@ -69,7 +69,7 @@ public class ClubEvaluateApplyController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.evaluate.apply") @SaCheckPermission("club.evaluate.apply")
@SLog(tag = "社团管理系统-社团评优", msg = "保存社团评优申请") @SLog(tag = "协会管理系统-协会评优", msg = "保存协会评优申请")
public Result save(@Param("data") SysClubEvaluate evaluate) { public Result save(@Param("data") SysClubEvaluate evaluate) {
evaluateService.dao().insertOrUpdate(evaluate); evaluateService.dao().insertOrUpdate(evaluate);
return Result.success(evaluate); return Result.success(evaluate);
@@ -78,7 +78,7 @@ public class ClubEvaluateApplyController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.evaluate.apply") @SaCheckPermission("club.evaluate.apply")
@SLog(tag = "社团管理系统-社团评优", msg = "提交社团评优") @SLog(tag = "协会管理系统-协会评优", msg = "提交协会评优")
public Result submit(@Valid @Param("data") SysClubEvaluate evaluate) { public Result submit(@Valid @Param("data") SysClubEvaluate evaluate) {
evaluateService.dao().insertOrUpdate(evaluate); evaluateService.dao().insertOrUpdate(evaluate);
@@ -51,7 +51,7 @@ public class ClubEvaluateMineController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.evaluate.mine") @SaCheckPermission("club.evaluate.mine")
@SLog(tag = "社团管理系统-社团评优", msg = "删除社团评优") @SLog(tag = "协会管理系统-协会评优", msg = "删除协会评优")
public Result doDelete(@Valid String id) { public Result doDelete(@Valid String id) {
sysClubEvaluateService.dao().delete(SysClubEvaluate.class, id); sysClubEvaluateService.dao().delete(SysClubEvaluate.class, id);
flowEngine.processInstanceService().deleteProcessInstanceByBusinessKey(id); flowEngine.processInstanceService().deleteProcessInstanceByBusinessKey(id);
@@ -71,12 +71,12 @@ public class ClubExamineApplyController {
@SaCheckLogin @SaCheckLogin
public Result getCount(String id, Integer year, String clubId) { public Result getCount(String id, Integer year, String clubId) {
if(StrUtil.isBlank(clubId)) { if(StrUtil.isBlank(clubId)) {
return Result.error("社团信息为空,请核查"); return Result.error("协会信息为空,请核查");
} }
if(year == null) { if(year == null) {
return Result.error("年份信息为空,请核查"); return Result.error("年份信息为空,请核查");
} }
// 同一社团的年度重复申请校验以登记年度为准,不能按申请时间判断。 // 同一协会的年度重复申请校验以登记年度为准,不能按申请时间判断。
List<SysClubExamineRegister> list = dao.query(SysClubExamineRegister.class, Cnd.where("clubId", "=", clubId).and("year", "=", year).andEX("id", "!=", id)); List<SysClubExamineRegister> list = dao.query(SysClubExamineRegister.class, Cnd.where("clubId", "=", clubId).and("year", "=", year).andEX("id", "!=", id));
List<ProcessInstance> instanceList = dao.query( List<ProcessInstance> instanceList = dao.query(
ProcessInstance.class, ProcessInstance.class,
@@ -98,7 +98,7 @@ public class ClubExamineApplyController {
// @SaCheckLogin // @SaCheckLogin
// public Result getClubUserNum(String clubId, Integer year) { // public Result getClubUserNum(String clubId, Integer year) {
// if(StrUtil.isBlank(clubId)) { // if(StrUtil.isBlank(clubId)) {
// return Result.error("社团信息为空,请核查"); // return Result.error("协会信息为空,请核查");
// } // }
// List<NutMap> result = sysClubExamineService.getClubUserNum(clubId, year); // List<NutMap> result = sysClubExamineService.getClubUserNum(clubId, year);
// return Result.success(result); // return Result.success(result);
@@ -107,13 +107,13 @@ public class ClubExamineApplyController {
// //
/** /**
* 2026年10月2日22:00点开始新开发的代码;实际整改:2026年10月3日 10:14:04;操作人:wxg。 * 2026年10月2日22:00点开始新开发的代码;实际整改:2026年10月3日 10:14:04;操作人:wxg。
* 接收 clubId(所选社团ID)、year(公历年度),基础校验后调用 service; * 接收 clubId(所选协会ID)、year(公历年度),基础校验后调用 service;
* 返回 Result,data为两类计数及统计核查信息列表,异常参数返回错误提示。 * 返回 Result,data为两类计数及统计核查信息列表,异常参数返回错误提示。
*/ */
@At @At
@SaCheckLogin @SaCheckLogin
public Result getClubUserNum(String clubId, Integer year) { public Result getClubUserNum(String clubId, Integer year) {
if (StrUtil.isBlank(clubId)) return Result.error("社团信息为空,请核查"); if (StrUtil.isBlank(clubId)) return Result.error("协会信息为空,请核查");
if (year == null || year < 1 || year > 9998) return Result.error("请选择有效统计年度"); if (year == null || year < 1 || year > 9998) return Result.error("请选择有效统计年度");
return Result.success(sysClubExamineService.getClubUserNum(clubId, year)); return Result.success(sysClubExamineService.getClubUserNum(clubId, year));
} }
@@ -122,7 +122,7 @@ public class ClubExamineApplyController {
@SaCheckLogin @SaCheckLogin
public Result getJgUser(String clubId) { public Result getJgUser(String clubId) {
if(StrUtil.isBlank(clubId)) { if(StrUtil.isBlank(clubId)) {
return Result.error("社团信息为空,请核查"); return Result.error("协会信息为空,请核查");
} }
List<NutMap> result = sysClubExamineService.getJgUser(clubId); List<NutMap> result = sysClubExamineService.getJgUser(clubId);
return Result.success(result); return Result.success(result);
@@ -132,7 +132,7 @@ public class ClubExamineApplyController {
@SaCheckLogin @SaCheckLogin
public Result getClubMemberMoney(String clubId) { public Result getClubMemberMoney(String clubId) {
if(StrUtil.isBlank(clubId)) { if(StrUtil.isBlank(clubId)) {
return Result.error("社团信息为空,请核查"); return Result.error("协会信息为空,请核查");
} }
int count = dao.count(ClubUser.class, Cnd.where("clubId", "=", clubId)); int count = dao.count(ClubUser.class, Cnd.where("clubId", "=", clubId));
SysClub club = dao.fetch(SysClub.class, clubId); SysClub club = dao.fetch(SysClub.class, clubId);
@@ -160,7 +160,7 @@ public class ClubExamineApplyController {
@SaCheckLogin @SaCheckLogin
public Result getLastYearSurplus(String clubId) { public Result getLastYearSurplus(String clubId) {
if(StrUtil.isBlank(clubId)) { if(StrUtil.isBlank(clubId)) {
return Result.error("社团信息为空,请核查"); return Result.error("协会信息为空,请核查");
} }
// 上年度结余按登记年度查询,避免申请时间跨年导致取错数据。 // 上年度结余按登记年度查询,避免申请时间跨年导致取错数据。
SysClubExamineRegister register = dao.fetch(SysClubExamineRegister.class, Cnd.where("clubId", "=", clubId) SysClubExamineRegister register = dao.fetch(SysClubExamineRegister.class, Cnd.where("clubId", "=", clubId)
@@ -186,7 +186,7 @@ public class ClubExamineApplyController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.examine.apply") @SaCheckPermission("club.examine.apply")
@SLog(tag = "社团管理系统-社团年审", msg = "保存社团年审") @SLog(tag = "协会管理系统-协会年审", msg = "保存协会年审")
public Result save(@Param("data") SysClubExamineRegister examineRegister, public Result save(@Param("data") SysClubExamineRegister examineRegister,
@Param("incomeDetailed") String incomeDetailed, @Param("incomeDetailed") String incomeDetailed,
@Param("incomeCensus") String incomeCensus) { @Param("incomeCensus") String incomeCensus) {
@@ -201,7 +201,7 @@ public class ClubExamineApplyController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.examine.apply") @SaCheckPermission("club.examine.apply")
@SLog(tag = "社团管理系统-社团年审", msg = "提交社团年审") @SLog(tag = "协会管理系统-协会年审", msg = "提交协会年审")
public Result submit(@Param("data") SysClubExamineRegister examineRegister, public Result submit(@Param("data") SysClubExamineRegister examineRegister,
@Param("incomeDetailed") String incomeDetailed, @Param("incomeDetailed") String incomeDetailed,
@Param("incomeCensus") String incomeCensus) { @Param("incomeCensus") String incomeCensus) {
@@ -48,7 +48,7 @@ public class ClubExamineMineController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.examine.mine") @SaCheckPermission("club.examine.mine")
@SLog(tag = "社团管理系统-社团年审", msg = "删除社团年审") @SLog(tag = "协会管理系统-协会年审", msg = "删除协会年审")
public Result doDelete(@Valid String id) { public Result doDelete(@Valid String id) {
sysClubExamineService.delete(id); sysClubExamineService.delete(id);
sysClubExamineService.dao().clear(SysClubExamineRegisterDetailed.class, Cnd.where("registerId", "=", id)); sysClubExamineService.dao().clear(SysClubExamineRegisterDetailed.class, Cnd.where("registerId", "=", id));
@@ -108,7 +108,7 @@ public class ClubExamineMineController {
.bind("activityQk", activityQk).build(); .bind("activityQk", activityQk).build();
try { try {
String fileName = Globals.MyConfig.getString("AppName") + "教职工社团年度考核登记表"; String fileName = Globals.MyConfig.getString("AppName") + "教职工协会年度考核登记表";
response.addHeader("Content-Type", "application/octet-stream"); response.addHeader("Content-Type", "application/octet-stream");
response.addHeader("Content-Disposition", "attachment; filename=\"" + new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + "\".docx"); response.addHeader("Content-Disposition", "attachment; filename=\"" + new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + "\".docx");
XWPFTemplate.compile(officeTemplateUtil.getPath("club_examine_register"), config).render(map).writeAndClose(response.getOutputStream()); XWPFTemplate.compile(officeTemplateUtil.getPath("club_examine_register"), config).render(map).writeAndClose(response.getOutputStream());
@@ -135,7 +135,7 @@ public class ClubChangeManagerController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.change") @SaCheckPermission("club.infoManage.change")
@SLog(tag = "社团管理系统-信息管理", msg = "提交变更理事机构") @SLog(tag = "协会管理系统-信息管理", msg = "提交变更理事机构")
public Object submit(@Param("data") SysClubManager clubManager) { public Object submit(@Param("data") SysClubManager clubManager) {
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.and(SysClubManager::getClubId, "=", clubManager.getClubId()); cnd.and(SysClubManager::getClubId, "=", clubManager.getClubId());
@@ -146,7 +146,7 @@ public class ClubChangeManagerController {
List<String> pendingProcessIds = managerList.stream().map(SysClubManager::getId).toList(); List<String> pendingProcessIds = managerList.stream().map(SysClubManager::getId).toList();
int pendingCount = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", pendingProcessIds).and(ProcessInstance::getState, "not in", List.of(ProcessInstanceStateEnum.FINISHED.getCode(), ProcessInstanceStateEnum.REJECT.getCode()))); int pendingCount = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", pendingProcessIds).and(ProcessInstance::getState, "not in", List.of(ProcessInstanceStateEnum.FINISHED.getCode(), ProcessInstanceStateEnum.REJECT.getCode())));
if (pendingCount > 0) { if (pendingCount > 0) {
return Result.error("您有该社团的申请记录尚未完成,请核对!"); return Result.error("您有该协会的申请记录尚未完成,请核对!");
} }
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
@@ -206,7 +206,7 @@ public class ClubChangeManagerController {
@ApiOperation("删除") @ApiOperation("删除")
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.change") @SaCheckPermission("club.infoManage.change")
@SLog(tag = "社团管理系统-信息管理", msg = "删除变更理事机构") @SLog(tag = "协会管理系统-信息管理", msg = "删除变更理事机构")
public Result delete(@Param("id") String id) { public Result delete(@Param("id") String id) {
dao.delete(SysClubManager.class, id); dao.delete(SysClubManager.class, id);
flowEngine.processInstanceService().deleteProcessInstanceByBusinessKey(id); flowEngine.processInstanceService().deleteProcessInstanceByBusinessKey(id);
@@ -111,7 +111,7 @@ public class ClubInfoManageController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.manage") @SaCheckPermission("club.infoManage.manage")
@SLog(tag = "社团管理系统-信息管理", msg = "修改社团信息") @SLog(tag = "协会管理系统-信息管理", msg = "修改协会信息")
public Result doSubmit(@Param("club") SysClub club) { public Result doSubmit(@Param("club") SysClub club) {
clubInfoManageService.dao().update(club); clubInfoManageService.dao().update(club);
return Result.success(); return Result.success();
@@ -120,7 +120,7 @@ public class ClubInfoManageController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.manage") @SaCheckPermission("club.infoManage.manage")
@SLog(tag = "社团管理系统-信息管理", msg = "修改缴费状态") @SLog(tag = "协会管理系统-信息管理", msg = "修改缴费状态")
public Result updatePayed(@Valid Boolean payed, @Valid String id) { public Result updatePayed(@Valid Boolean payed, @Valid String id) {
clubInfoManageService.dao().update(ClubUser.class, Chain.make("payed", payed), Cnd.where("id", "=", id)); clubInfoManageService.dao().update(ClubUser.class, Chain.make("payed", payed), Cnd.where("id", "=", id));
return Result.success(); return Result.success();
@@ -129,7 +129,7 @@ public class ClubInfoManageController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.manage") @SaCheckPermission("club.infoManage.manage")
@SLog(tag = "社团管理系统-信息管理", msg = "修改拨付状态") @SLog(tag = "协会管理系统-信息管理", msg = "修改拨付状态")
public Result updateGive(@Valid Boolean giveMoney, @Valid String id) { public Result updateGive(@Valid Boolean giveMoney, @Valid String id) {
clubInfoManageService.dao().update(ClubUser.class, Chain.make("giveMoney", giveMoney), Cnd.where("id", "=", id)); clubInfoManageService.dao().update(ClubUser.class, Chain.make("giveMoney", giveMoney), Cnd.where("id", "=", id));
return Result.success(); return Result.success();
@@ -138,7 +138,7 @@ public class ClubInfoManageController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.manage") @SaCheckPermission("club.infoManage.manage")
@SLog(tag = "社团管理系统-信息管理", msg = "退会") @SLog(tag = "协会管理系统-信息管理", msg = "退会")
public Result exitClub(@Valid String id) { public Result exitClub(@Valid String id) {
clubInfoManageService.exitClub(id); clubInfoManageService.exitClub(id);
return Result.success(); return Result.success();
@@ -147,7 +147,7 @@ public class ClubInfoManageController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.manage") @SaCheckPermission("club.infoManage.manage")
@SLog(tag = "社团管理系统-信息管理", msg = "删除社团成员") @SLog(tag = "协会管理系统-信息管理", msg = "删除协会成员")
public Result userDelete(@Valid String id) { public Result userDelete(@Valid String id) {
ClubUser clubUser = dao.fetch(ClubUser.class, id); ClubUser clubUser = dao.fetch(ClubUser.class, id);
dao.delete(ClubUser.class,id); dao.delete(ClubUser.class,id);
@@ -188,11 +188,11 @@ public class ClubInfoManageController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.manage") @SaCheckPermission("club.infoManage.manage")
@SLog(tag = "社团管理系统-信息管理", msg = "修改身份") @SLog(tag = "协会管理系统-信息管理", msg = "修改身份")
public Result updateRoleCode(@Param("id") String id, @Valid String[] roleCodes, @Valid String clubId) { public Result updateRoleCode(@Param("id") String id, @Valid String[] roleCodes, @Valid String clubId) {
List<String> roleCodeList = Arrays.asList(roleCodes); List<String> roleCodeList = Arrays.asList(roleCodes);
ClubUser clubUser = clubInfoManageService.dao().fetch(ClubUser.class, id); ClubUser clubUser = clubInfoManageService.dao().fetch(ClubUser.class, id);
//查询社团是否存在会长或者秘书长 //查询协会是否存在会长或者秘书长
if(Arrays.asList(roleCodes).contains(RoleConstant.CLUB_PRESIDENT.name())) { if(Arrays.asList(roleCodes).contains(RoleConstant.CLUB_PRESIDENT.name())) {
int count = dao.count(ClubUser.class, Cnd.where("clubId", "=", clubId) int count = dao.count(ClubUser.class, Cnd.where("clubId", "=", clubId)
.and(new Static("JSON_CONTAINS(roleCode, '\"%s\"')".formatted(RoleConstant.CLUB_PRESIDENT.name())))); .and(new Static("JSON_CONTAINS(roleCode, '\"%s\"')".formatted(RoleConstant.CLUB_PRESIDENT.name()))));
@@ -265,10 +265,10 @@ public class ClubInfoManageController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.manage") @SaCheckPermission("club.infoManage.manage")
@SLog(tag = "社团管理系统-信息管理", msg = "添加社团成员") @SLog(tag = "协会管理系统-信息管理", msg = "添加协会成员")
public Result userDoAdd(@Valid String clubId, @Valid String[] users, @Valid String roleCode, @Valid Boolean payed) { public Result userDoAdd(@Valid String clubId, @Valid String[] users, @Valid String roleCode, @Valid Boolean payed) {
//查询社团是否存在会长或者秘书长 //查询协会是否存在会长或者秘书长
if (List.of(RoleConstant.CLUB_PRESIDENT.name(), RoleConstant.CLUB_SECRETARY.name()).contains(roleCode)) { if (List.of(RoleConstant.CLUB_PRESIDENT.name(), RoleConstant.CLUB_SECRETARY.name()).contains(roleCode)) {
int count = clubInfoManageService.dao().count(ClubUser.class, Cnd.where("clubId", "=", clubId) int count = clubInfoManageService.dao().count(ClubUser.class, Cnd.where("clubId", "=", clubId)
.and(new Static("JSON_CONTAINS(roleCode, '\"%s\"')".formatted(roleCode)))); .and(new Static("JSON_CONTAINS(roleCode, '\"%s\"')".formatted(roleCode))));
@@ -354,7 +354,7 @@ public class ClubInfoManageController {
} }
/** /**
* @param clubId 左侧选中的社团ID,用于导出该社团登记表 * @param clubId 左侧选中的协会ID,用于导出该协会登记表
* @param response 成功时写入 DOCX 附件;失败由统一异常处理返回含 code/msg 的错误结果 * @param response 成功时写入 DOCX 附件;失败由统一异常处理返回含 code/msg 的错误结果
* @return void,附件直接写入响应,不再渲染 JSON 成功结果 * @return void,附件直接写入响应,不再渲染 JSON 成功结果
*/ */
@@ -372,9 +372,9 @@ public class ClubInfoManageController {
public void downloadUserImport(HttpServletResponse response) { public void downloadUserImport(HttpServletResponse response) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();){ try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();){
EasyExcel.write(byteArrayOutputStream, ClubUserImportVo.class) EasyExcel.write(byteArrayOutputStream, ClubUserImportVo.class)
.sheet("社团会员导入模版") .sheet("协会会员导入模版")
.doWrite(ArrayList::new); .doWrite(ArrayList::new);
CommonDownloadUtil.download("社团会员导入模版.xlsx", byteArrayOutputStream.toByteArray() ,response); CommonDownloadUtil.download("协会会员导入模版.xlsx", byteArrayOutputStream.toByteArray() ,response);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -405,7 +405,7 @@ public class ClubInfoManageController {
continue; continue;
} }
if (clubUsers.stream().anyMatch(s -> s.getUserId().equals(user.getId()))) { if (clubUsers.stream().anyMatch(s -> s.getUserId().equals(user.getId()))) {
v.setErrorInfo("该用户已加入该社团"); v.setErrorInfo("该用户已加入该协会");
errorInfos.add(v); errorInfos.add(v);
continue; continue;
} }
@@ -28,7 +28,7 @@ import javax.validation.Valid;
@Slf4j @Slf4j
@IocBean @IocBean
@Ok("json:full") @Ok("json:full")
@Api(tags = "社团缴费记录") @Api(tags = "协会缴费记录")
@At("/platform/club/pay") @At("/platform/club/pay")
public class ClubPayRecordController { public class ClubPayRecordController {
@@ -46,7 +46,7 @@ public class ClubPayRecordController {
@SaCheckPermission("club.infoManage.pay") @SaCheckPermission("club.infoManage.pay")
/** /**
* 2026年10月3日 14:33:18;操作人:wxg:接收查询条件,业务筛选交给 service。 * 2026年10月3日 14:33:18;操作人:wxg:接收查询条件,业务筛选交给 service。
* pageForm 为分页和姓名/工号关键字;year 为年度、clubId 为社团ID; * pageForm 为分页和姓名/工号关键字;year 为年度、clubId 为协会ID;
* payed/assign 为缴费/拨付状态(1是、0否、空值不限);memberState 为当前会员状态(1在会、0退会、空值全部)。 * payed/assign 为缴费/拨付状态(1是、0否、空值不限);memberState 为当前会员状态(1在会、0退会、空值全部)。
* 返回 Result,data 为分页对象,包含 list、totalCount 等;list.count 为当前会员关系是否存在(1在会、0退会)。 * 返回 Result,data 为分页对象,包含 list、totalCount 等;list.count 为当前会员关系是否存在(1在会、0退会)。
*/ */
@@ -62,7 +62,7 @@ public class ClubPayRecordController {
return Result.success(payRecordService.pageData(pageForm, year, clubId, payed, assign, memberState)); return Result.success(payRecordService.pageData(pageForm, year, clubId, payed, assign, memberState));
} }
/** 返回当前用户授权社团的下拉选项,字段包含 id、clubName、clubCode。 */ /** 返回当前用户授权协会的下拉选项,字段包含 id、clubName、clubCode。 */
@At @At
@SaCheckPermission("club.infoManage.pay") @SaCheckPermission("club.infoManage.pay")
public Result listClubs() { public Result listClubs() {
@@ -70,9 +70,9 @@ public class ClubPayRecordController {
} }
@At @At
@ApiOperation("修改社团成员缴费状态") @ApiOperation("修改协会成员缴费状态")
@SaCheckPermission("club.infoManage.pay") @SaCheckPermission("club.infoManage.pay")
@SLog(tag = "社团管理系统-缴费记录", msg = "修改社团成员缴费状态") @SLog(tag = "协会管理系统-缴费记录", msg = "修改协会成员缴费状态")
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public Result handle(@Param("ids") String[] ids, public Result handle(@Param("ids") String[] ids,
@Param("payed") Boolean payed) { @Param("payed") Boolean payed) {
@@ -124,7 +124,7 @@ public class ClubRefreshReportController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.refreshReport") @SaCheckPermission("club.infoManage.refreshReport")
@SLog(tag = "社团管理系统-信息管理", msg = "提交换届报告") @SLog(tag = "协会管理系统-信息管理", msg = "提交换届报告")
public Object submit(@Param("data") SysClubRefresh clubRefresh) { public Object submit(@Param("data") SysClubRefresh clubRefresh) {
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
@@ -136,7 +136,7 @@ public class ClubRefreshReportController {
List<String> idList = list.stream().map(SysClubRefresh::getId).toList(); List<String> idList = list.stream().map(SysClubRefresh::getId).toList();
int count = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", idList).and(ProcessInstance::getState, "not in", List.of(ProcessInstanceStateEnum.FINISHED.getCode(), ProcessInstanceStateEnum.REJECT.getCode()))); int count = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", idList).and(ProcessInstance::getState, "not in", List.of(ProcessInstanceStateEnum.FINISHED.getCode(), ProcessInstanceStateEnum.REJECT.getCode())));
if (count > 0) { if (count > 0) {
return Result.error("您有该社团的申请记录尚未完成,请核对!"); return Result.error("您有该协会的申请记录尚未完成,请核对!");
} }
SysClub club = clubInfoManageService.dao().fetch(SysClub.class, clubRefresh.getClubId()); SysClub club = clubInfoManageService.dao().fetch(SysClub.class, clubRefresh.getClubId());
@@ -178,9 +178,9 @@ public class ClubRefreshReportController {
@ApiOperation("删除") @ApiOperation("删除")
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.refreshReport") @SaCheckPermission("club.infoManage.refreshReport")
@SLog(tag = "社团管理系统-信息管理", msg = "删除换届报告") @SLog(tag = "协会管理系统-信息管理", msg = "删除换届报告")
public Result delete(@Param("id") String id) { public Result delete(@Param("id") String id) {
// 历史换届记录可能与社团注册共用业务编号,必须按流程定义精确定位换届流程,避免误删注册流程。 // 历史换届记录可能与协会注册共用业务编号,必须按流程定义精确定位换届流程,避免误删注册流程。
List<Long> refreshDefineIds = dao.query(ProcessDefine.class, List<Long> refreshDefineIds = dao.query(ProcessDefine.class,
Cnd.where(ProcessDefine::getName, "=", "XHHJBG")) Cnd.where(ProcessDefine::getName, "=", "XHHJBG"))
.stream() .stream()
@@ -32,9 +32,9 @@ import java.util.List;
@At("/platform/club/infoManage/registerImport") @At("/platform/club/infoManage/registerImport")
@Ok("json:full") @Ok("json:full")
public class ClubRegisterImportController { public class ClubRegisterImportController {
private static final String[] HEADERS = {"社团名称", "社团编码(选填)", "社团类型编码", "成立日期", "会长工号", "秘书长工号", private static final String[] HEADERS = {"协会名称", "协会编码(选填)", "协会类型编码", "成立日期", "会长工号", "秘书长工号",
"发起人工号", "副会长工号(选填)", "副秘书长工号(选填)", "联系人工号(选填)", "会费标准(选填)", "发起人工号", "副会长工号(选填)", "副秘书长工号(选填)", "联系人工号(选填)", "会费标准(选填)",
"联系电话(选填)", "联系邮箱(选填)", "社团介绍及宗旨(选填)", "活动形式(选填)", "缩略图地址(选填)", "联系电话(选填)", "联系邮箱(选填)", "协会介绍及宗旨(选填)", "活动形式(选填)", "缩略图地址(选填)",
"申请成立报告文件ID(选填)", "章程草案文件ID(选填)", "经费管理办法文件ID(选填)", "年度计划文件ID(选填)", "其他附件文件ID(选填)"}; "申请成立报告文件ID(选填)", "章程草案文件ID(选填)", "经费管理办法文件ID(选填)", "年度计划文件ID(选填)", "其他附件文件ID(选填)"};
@Inject @Inject
private ClubRegisterImportService clubRegisterImportService; private ClubRegisterImportService clubRegisterImportService;
@@ -47,27 +47,27 @@ public class ClubRegisterImportController {
@SaCheckPermission("club.infoManage.manage") @SaCheckPermission("club.infoManage.manage")
public void downloadTemplate(HttpServletResponse response) { public void downloadTemplate(HttpServletResponse response) {
Workbook workbook = new XSSFWorkbook(); Workbook workbook = new XSSFWorkbook();
Sheet data = sheet(workbook, "社团导入", HEADERS); Sheet data = sheet(workbook, "协会导入", HEADERS);
CellStyle style = workbook.createCellStyle(); CellStyle style = workbook.createCellStyle();
style.setDataFormat(workbook.createDataFormat().getFormat("@")); style.setDataFormat(workbook.createDataFormat().getFormat("@"));
for (int i = 0; i < HEADERS.length; i++) data.setDefaultColumnStyle(i, style); for (int i = 0; i < HEADERS.length; i++) data.setDefaultColumnStyle(i, style);
Sheet notes = sheet(workbook, "填写说明", "参数", "填写要求"); Sheet notes = sheet(workbook, "填写说明", "参数", "填写要求");
notes.setColumnWidth(1, 100 * 256); notes.setColumnWidth(1, 100 * 256);
append(notes, "导入规则", "仅读取第一个工作表,每行一个社团,最多1000行有效数据;保留表头,工号与编码按文本填写。"); append(notes, "导入规则", "仅读取第一个工作表,每行一个协会,最多1000行有效数据;保留表头,工号与编码按文本填写。");
append(notes, "必填", "社团名称、社团类型编码、成立日期、会长工号、秘书长工号、至少3名不同发起人工号。"); append(notes, "必填", "协会名称、协会类型编码、成立日期、会长工号、秘书长工号、至少3名不同发起人工号。");
append(notes, "社团编码", "选填,留空按单个注册的年度编号规则生成;已有名称或编码直接跳过,不覆盖草稿、审批中或已解散记录。"); append(notes, "协会编码", "选填,留空按单个注册的年度编号规则生成;已有名称或编码直接跳过,不覆盖草稿、审批中或已解散记录。");
append(notes, "成立日期", "按yyyy-MM-dd填写,如2026-10-23;支持Excel日期单元格。"); append(notes, "成立日期", "按yyyy-MM-dd填写,如2026-10-23;支持Excel日期单元格。");
append(notes, "人员", "使用系统内已有工号。会长和秘书长各1人;副职可选多人,用逗号分隔,理事机构人员不得重复。"); append(notes, "人员", "使用系统内已有工号。会长和秘书长各至少填1人,副会长和副秘书长选填。同一职务的多个工号用英文逗号(,)分隔,理事机构人员不得重复。");
append(notes, "发起人", "至少3名不同人员,用逗号分隔;可与理事机构人员重合。"); append(notes, "发起人", "至少3名不同人员,用逗号分隔;可与理事机构人员重合。");
append(notes, "联系人", "选填工号,电话及邮箱默认取人员资料;单独填写电话、邮箱时使用填写值。"); append(notes, "联系人", "选填工号,电话及邮箱默认取人员资料;单独填写电话、邮箱时使用填写值。");
append(notes, "社团类型", "参照社团类型工作表填写编码,不填写名称。"); append(notes, "协会类型", "参照协会类型工作表填写编码,不填写名称。");
append(notes, "其他资料", "会费标准、介绍、活动形式、缩略图地址均选填;介绍及活动形式按纯文本导入。"); append(notes, "其他资料", "会费标准、介绍、活动形式、缩略图地址均选填;介绍及活动形式按纯文本导入。");
append(notes, "附件", "选填系统已上传文件的ID,多文件用逗号分隔,每类最多5个;Excel不上传附件本身,留空可后续在维护页面补充。"); append(notes, "附件", "选填系统已上传文件的ID,多文件用逗号分隔,每类最多5个;Excel不上传附件本身,留空可后续在维护页面补充。");
append(notes, "直接通过", "保存社团、发起人、理事会员、入会时间和年度缴费记录,授予社团角色;直接通过,不生成审批任务。"); append(notes, "直接通过", "保存协会、发起人、理事会员、入会时间和年度缴费记录,授予协会角色;直接通过,不生成审批任务。");
append(notes, "导入结果", "逐行独立保存,已存在的跳过,失败行回滚并显示原因,不影响其他行。"); append(notes, "导入结果", "逐行独立保存,已存在的跳过,失败行回滚并显示原因,不影响其他行。");
Sheet types = sheet(workbook, "社团类型", "类型编码", "类型名称"); Sheet types = sheet(workbook, "协会类型", "类型编码", "类型名称");
for (Sys_dict type : sysDictService.getSubListByCode("CLUB_REGISTER_TYPE")) append(types, type.getCode(), type.getName()); for (Sys_dict type : sysDictService.getSubListByCode("CLUB_REGISTER_TYPE")) append(types, type.getCode(), type.getName());
CommonDownloadUtil.download("社团批量注册模板.xlsx", workbook, response); CommonDownloadUtil.download("协会批量注册模板.xlsx", workbook, response);
} }
@At @At
@@ -95,7 +95,7 @@ public class ClubRegisterImportController {
numbers.add(row.getRowNum() + 1); numbers.add(row.getRowNum() + 1);
} }
} catch (Exception e) { } catch (Exception e) {
log.warn("读取社团注册导入文件失败", e); log.warn("读取协会注册导入文件失败", e);
return Result.error("文件读取失败,请使用未加密的xls或xlsx模板,不要使用公式"); return Result.error("文件读取失败,请使用未加密的xls或xlsx模板,不要使用公式");
} }
if (rows.isEmpty()) return Result.error("文件中没有需要导入的数据"); if (rows.isEmpty()) return Result.error("文件中没有需要导入的数据");
@@ -110,7 +110,7 @@ public class ClubRegisterImportController {
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
result = NutMap.NEW().addv("status", "失败").addv("message", e.getMessage()); result = NutMap.NEW().addv("status", "失败").addv("message", e.getMessage());
} catch (Exception e) { } catch (Exception e) {
log.error("社团批量注册失败,行号:{}", numbers.get(i), e); log.error("协会批量注册失败,行号:{}", numbers.get(i), e);
result = NutMap.NEW().addv("status", "失败").addv("message", "保存失败,该行已回滚,请检查数据或联系管理员"); result = NutMap.NEW().addv("status", "失败").addv("message", "保存失败,该行已回滚,请检查数据或联系管理员");
} }
result.setv("rowNumber", numbers.get(i)).setv("clubName", values[0]); result.setv("rowNumber", numbers.get(i)).setv("clubName", values[0]);
@@ -123,7 +123,7 @@ public class ClubRuleUpdateController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.ruleUpdate") @SaCheckPermission("club.infoManage.ruleUpdate")
@SLog(tag = "社团管理系统-信息管理", msg = "提交章程备案") @SLog(tag = "协会管理系统-信息管理", msg = "提交章程备案")
public Object submit(@Param("data")SysClubRule clubRule) { public Object submit(@Param("data")SysClubRule clubRule) {
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
@@ -135,7 +135,7 @@ public class ClubRuleUpdateController {
List<String> idList = list.stream().map(SysClubRule::getId).toList(); List<String> idList = list.stream().map(SysClubRule::getId).toList();
int count = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", idList).and(ProcessInstance::getState, "not in", List.of(ProcessInstanceStateEnum.FINISHED.getCode(), ProcessInstanceStateEnum.REJECT.getCode()))); int count = dao.count(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", idList).and(ProcessInstance::getState, "not in", List.of(ProcessInstanceStateEnum.FINISHED.getCode(), ProcessInstanceStateEnum.REJECT.getCode())));
if (count > 0) { if (count > 0) {
return Result.error("您有该社团的申请记录尚未完成,请核对!"); return Result.error("您有该协会的申请记录尚未完成,请核对!");
} }
SysClub club = clubInfoManageService.dao().fetch(SysClub.class, clubRule.getClubId()); SysClub club = clubInfoManageService.dao().fetch(SysClub.class, clubRule.getClubId());
@@ -177,7 +177,7 @@ public class ClubRuleUpdateController {
@ApiOperation("删除") @ApiOperation("删除")
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.infoManage.ruleUpdate") @SaCheckPermission("club.infoManage.ruleUpdate")
@SLog(tag = "社团管理系统-信息管理", msg = "删除章程备案") @SLog(tag = "协会管理系统-信息管理", msg = "删除章程备案")
public Result delete(@Param("id") String id) { public Result delete(@Param("id") String id) {
clubInfoManageService.dao().delete(SysClubRule.class, id); clubInfoManageService.dao().delete(SysClubRule.class, id);
flowEngine.processInstanceService().deleteProcessInstanceByBusinessKey(id); flowEngine.processInstanceService().deleteProcessInstanceByBusinessKey(id);
@@ -201,7 +201,7 @@ public class ClubSchoolAuditReportController {
} }
/** /**
* 使用社团名称建立目录,方便按换届报告来源查阅。 * 使用协会名称建立目录,方便按换届报告来源查阅。
* *
* @param report 换届报告记录 * @param report 换届报告记录
* @return Zip 内目录名称 * @return Zip 内目录名称
@@ -211,7 +211,7 @@ public class ClubSchoolAuditReportController {
} }
/** /**
* 保留附件原始文件名;同一社团目录存在同名文件时追加序号,避免覆盖。 * 保留附件原始文件名;同一协会目录存在同名文件时追加序号,避免覆盖。
* *
* @param entryName 原始 Zip 条目名称 * @param entryName 原始 Zip 条目名称
* @param entryNames 已写入的 Zip 条目名称 * @param entryNames 已写入的 Zip 条目名称
@@ -22,7 +22,7 @@ import javax.validation.Valid;
* @Author: JyuHsin * @Author: JyuHsin
* @Date: 2024/8/6 10:02 * @Date: 2024/8/6 10:02
* @Version: v1.0.0 * @Version: v1.0.0
* @Description: 社团确认 * @Description: 协会确认
**/ **/
@IocBean @IocBean
@Ok("json") @Ok("json")
@@ -31,7 +31,7 @@ import javax.validation.Valid;
**/ **/
@IocBean @IocBean
@Ok("json") @Ok("json")
@Api("社团-我的注册") @Api("协会-我的注册")
@At("/platform/club/register/clubMyApply") @At("/platform/club/register/clubMyApply")
public class ClubMyApplyController { public class ClubMyApplyController {
@@ -54,10 +54,10 @@ public class ClubMyApplyController {
} }
@At @At
@ApiOperation("删除社团") @ApiOperation("删除协会")
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.register.myApply") @SaCheckPermission("club.register.myApply")
@SLog(tag = "社团管理系统-社团注册", msg = "删除社团") @SLog(tag = "协会管理系统-协会注册", msg = "删除协会")
public Result doDelete(@Valid String id) { public Result doDelete(@Valid String id) {
sysClubService.delete(id); sysClubService.delete(id);
sysClubService.dao().clear(SysClubSponsor.class, Cnd.where("clubId", "=", id)); sysClubService.dao().clear(SysClubSponsor.class, Cnd.where("clubId", "=", id));
@@ -42,7 +42,7 @@ import java.util.List;
* @Author: JyuHsin * @Author: JyuHsin
* @Date: 2024/8/6 9:56 * @Date: 2024/8/6 9:56
* @Version: v1.0.0 * @Version: v1.0.0
* @Description: 注册社团 * @Description: 注册协会
**/ **/
@IocBean @IocBean
@Ok("json") @Ok("json")
@@ -67,7 +67,7 @@ public class ClubRegistApplyController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.register.apply") @SaCheckPermission("club.register.apply")
@SLog(tag = "社团管理系统-社团注册", msg = "保存注册社团") @SLog(tag = "协会管理系统-协会注册", msg = "保存注册协会")
public Result save(@Param("club") SysClub club, public Result save(@Param("club") SysClub club,
@Param("::deleteIds") List<String> deleteIds, @Param("::deleteIds") List<String> deleteIds,
@Param("::managePerson") List<NutMap> managePerson) { @Param("::managePerson") List<NutMap> managePerson) {
@@ -83,7 +83,7 @@ public class ClubRegistApplyController {
@At @At
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("club.register.apply") @SaCheckPermission("club.register.apply")
@SLog(tag = "社团管理系统-社团注册", msg = "提交注册社团") @SLog(tag = "协会管理系统-协会注册", msg = "提交注册协会")
public Result submit(@Param("club") SysClub club, public Result submit(@Param("club") SysClub club,
@Param("::deleteIds") List<String> deleteIds, @Param("::deleteIds") List<String> deleteIds,
@Param("::managePerson") List<NutMap> managePerson) { @Param("::managePerson") List<NutMap> managePerson) {
@@ -232,7 +232,7 @@ public class ClubStatisticsController {
nutMap.put("roleName", roleName); nutMap.put("roleName", roleName);
nutMap.put("roleCode", list); nutMap.put("roleCode", list);
} }
//获取所有社团 //获取所有协会
Cnd stCnd = Cnd.NEW(); Cnd stCnd = Cnd.NEW();
stCnd.andEX("id", "=", clubId); stCnd.andEX("id", "=", clubId);
List<SysClub> clubList = sysClubService.query(stCnd); List<SysClub> clubList = sysClubService.query(stCnd);
@@ -328,7 +328,7 @@ public class ClubStatisticsController {
} }
List<ExcelExportEntity> exportEntities = new ArrayList<>(); List<ExcelExportEntity> exportEntities = new ArrayList<>();
exportEntities.add(new ExcelExportEntity("社团名称", "clubName", 20)); exportEntities.add(new ExcelExportEntity("协会名称", "clubName", 20));
exportEntities.add(new ExcelExportEntity("姓名", "userName", 20)); exportEntities.add(new ExcelExportEntity("姓名", "userName", 20));
exportEntities.add(new ExcelExportEntity("工号", "loginName", 20)); exportEntities.add(new ExcelExportEntity("工号", "loginName", 20));
exportEntities.add(new ExcelExportEntity("性别", "sex", 20)); exportEntities.add(new ExcelExportEntity("性别", "sex", 20));
@@ -357,9 +357,9 @@ public class ClubStatisticsController {
List<NutMap> list = sysClubService.listMap(sql); List<NutMap> list = sysClubService.listMap(sql);
List<ExcelExportEntity> exportEntities = new ArrayList<>(); List<ExcelExportEntity> exportEntities = new ArrayList<>();
exportEntities.add(new ExcelExportEntity("社团名称", "clubName", 20)); exportEntities.add(new ExcelExportEntity("协会名称", "clubName", 20));
exportEntities.add(new ExcelExportEntity("编码", "clubCode", 20)); exportEntities.add(new ExcelExportEntity("编码", "clubCode", 20));
exportEntities.add(new ExcelExportEntity("社团总人数", "total", 20)); exportEntities.add(new ExcelExportEntity("协会总人数", "total", 20));
exportEntities.add(new ExcelExportEntity("在职人数", "work", 20)); exportEntities.add(new ExcelExportEntity("在职人数", "work", 20));
exportEntities.add(new ExcelExportEntity("退休人数", "retire", 20)); exportEntities.add(new ExcelExportEntity("退休人数", "retire", 20));
exportEntities.add(new ExcelExportEntity("男", "man", 20)); exportEntities.add(new ExcelExportEntity("男", "man", 20));
@@ -368,7 +368,7 @@ public class ClubStatisticsController {
ExportParams exportParams = new ExportParams(); ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF); exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list); Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list);
CommonDownloadUtil.download("社团统计表.xlsx", workbook, response); CommonDownloadUtil.download("协会统计表.xlsx", workbook, response);
} }
@At @At
@@ -380,7 +380,7 @@ public class ClubStatisticsController {
response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode("上传资料数据.zip")); response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode("上传资料数据.zip"));
LinkedHashMap<String, List<JSONObject>> fileList = new LinkedHashMap<>(); LinkedHashMap<String, List<JSONObject>> fileList = new LinkedHashMap<>();
//查询社团 //查询协会
List<SysClub> clubList = sysClubService.query(Cnd.NEW().andEX("id", "=", clubId)); List<SysClub> clubList = sysClubService.query(Cnd.NEW().andEX("id", "=", clubId));
clubList.forEach(item -> { clubList.forEach(item -> {
List<JSONObject> file = new ArrayList<>(); List<JSONObject> file = new ArrayList<>();
@@ -394,7 +394,7 @@ public class ClubStatisticsController {
fileList.put(item.getId(), file); fileList.put(item.getId(), file);
}); });
//社团年审附件 //协会年审附件
List<SysClubExamineRegister> examineList = sysClubService.dao().query(SysClubExamineRegister.class, List<SysClubExamineRegister> examineList = sysClubService.dao().query(SysClubExamineRegister.class,
Cnd.NEW().andEX("clubId", "=", clubId) Cnd.NEW().andEX("clubId", "=", clubId)
.and("year(registerDate)", "=", DateUtil.thisYear())); .and("year(registerDate)", "=", DateUtil.thisYear()));
@@ -406,7 +406,7 @@ public class ClubStatisticsController {
} }
}); });
//社团评优附件 //协会评优附件
List<SysClubEvaluate> evaluateList = sysClubService.dao().query(SysClubEvaluate.class, Cnd.NEW().andEX("clubId", "=", clubId) List<SysClubEvaluate> evaluateList = sysClubService.dao().query(SysClubEvaluate.class, Cnd.NEW().andEX("clubId", "=", clubId)
.and("year(applyTime)", "=", DateUtil.thisYear())); .and("year(applyTime)", "=", DateUtil.thisYear()));
evaluateList.forEach(item -> { evaluateList.forEach(item -> {
@@ -439,7 +439,7 @@ public class ClubStatisticsController {
} }
private Sql generateSql(String clubId, Boolean auditState, String userState, String sex, Boolean giveMoney) { private Sql generateSql(String clubId, Boolean auditState, String userState, String sex, Boolean giveMoney) {
// 注册流程按社团去重后再关联,避免换届等流程复用社团ID时将会员人数重复统计。 // 注册流程按协会去重后再关联,避免换届等流程复用协会ID时将会员人数重复统计。
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
club.id, club.id,
@@ -25,7 +25,7 @@ public class ClubRefreshUpdateInterceptor implements FlowInterceptor {
SysClubRefresh clubRefresh = Json.fromJson(SysClubRefresh.class, formDataStr); SysClubRefresh clubRefresh = Json.fromJson(SysClubRefresh.class, formDataStr);
Dao dao = ServiceContext.find(Dao.class); Dao dao = ServiceContext.find(Dao.class);
// 审核通过,将新的附件更新到社团表 // 审核通过,将新的附件更新到协会表
SysClub club = dao.fetch(SysClub.class, clubRefresh.getClubId()); SysClub club = dao.fetch(SysClub.class, clubRefresh.getClubId());
club.setReplaceReport(clubRefresh.getFiles()); club.setReplaceReport(clubRefresh.getFiles());
dao.update(club); dao.update(club);
@@ -24,7 +24,7 @@ public class ClubRuleUpdateInterceptor implements FlowInterceptor {
SysClubRule clubRule = Json.fromJson(SysClubRule.class, formDataStr); SysClubRule clubRule = Json.fromJson(SysClubRule.class, formDataStr);
Dao dao = ServiceContext.find(Dao.class); Dao dao = ServiceContext.find(Dao.class);
// 审核通过,将新的附件更新到社团表 // 审核通过,将新的附件更新到协会表
SysClub club = dao.fetch(SysClub.class, clubRule.getClubId()); SysClub club = dao.fetch(SysClub.class, clubRule.getClubId());
club.setRulesFile(clubRule.getFiles()); club.setRulesFile(clubRule.getFiles());
dao.update(club); dao.update(club);
@@ -44,7 +44,7 @@ public class ClubUserExitInterceptor implements FlowInterceptor {
// 删人 // 删人
dao.clear(ClubUser.class, Cnd.where(ClubUser::getUserId, "=", clubUserApply.getUserId()).and(ClubUser::getClubId, "=", clubUserApply.getClubId())); dao.clear(ClubUser.class, Cnd.where(ClubUser::getUserId, "=", clubUserApply.getUserId()).and(ClubUser::getClubId, "=", clubUserApply.getClubId()));
// 2026年10月3日 09:51:00;操作人:wxg:WF 退会完成后,同步将该社团会员的拨付字段置为 false。 // 2026年10月3日 09:51:00;操作人:wxg:WF 退会完成后,同步将该协会会员的拨付字段置为 false。
ServiceContext.find(ClubPayRecordService.class).disableExitMemberPayment(clubUserApply.getClubId(), clubUserApply.getUserId()); ServiceContext.find(ClubPayRecordService.class).disableExitMemberPayment(clubUserApply.getClubId(), clubUserApply.getUserId());
// 删角色 // 删角色
dao.clear(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", clubUserApply.getUserId()).and(Sys_user_role::getClubId, "=", clubUserApply.getClubId())); dao.clear(Sys_user_role.class, Cnd.where(Sys_user_role::getUserId, "=", clubUserApply.getUserId()).and(Sys_user_role::getClubId, "=", clubUserApply.getClubId()));
@@ -47,7 +47,7 @@ public class ClubUserJoinInterceptor implements FlowInterceptor {
clubUserApply.setJoinTime(ServiceContext.find(ClubUserJoinService.class).completeJoinApproval( clubUserApply.setJoinTime(ServiceContext.find(ClubUserJoinService.class).completeJoinApproval(
execution.getProcessInstance().getBusinessNo(), execution.getProcessTaskId())); execution.getProcessInstance().getBusinessNo(), execution.getProcessTaskId()));
// 将申请信息复制一份,插入社团成员表 // 将申请信息复制一份,插入协会成员表
ClubUser clubUser = BeanUtil.copyProperties(clubUserApply, ClubUser.class); ClubUser clubUser = BeanUtil.copyProperties(clubUserApply, ClubUser.class);
// 读取本次已写入的入会申请时间,不能取成立时间或用插入时刻替代审核完成时间。 // 读取本次已写入的入会申请时间,不能取成立时间或用插入时刻替代审核完成时间。
ClubUserApply approvedApply = dao.fetch(ClubUserApply.class, execution.getProcessInstance().getBusinessNo()); ClubUserApply approvedApply = dao.fetch(ClubUserApply.class, execution.getProcessInstance().getBusinessNo());
@@ -59,8 +59,8 @@ public class ClubUserJoinInterceptor implements FlowInterceptor {
ServiceContext.find(SysClubUserService.class).prepareMemberSnapshots(List.of(clubUser)); ServiceContext.find(SysClubUserService.class).prepareMemberSnapshots(List.of(clubUser));
dao.insert(clubUser); dao.insert(clubUser);
// 还要加到社团成员缴费表中 // 还要加到协会成员缴费表中
// 对了,如果同一个人,在同一年,多次加入同一社团,则只插入一条 // 对了,如果同一个人,在同一年,多次加入同一协会,则只插入一条
ClubPayRecord payRecord = dao.fetch( ClubPayRecord payRecord = dao.fetch(
ClubPayRecord.class, ClubPayRecord.class,
Cnd.where(ClubPayRecord::getClubId, "=", clubUser.getClubId()) Cnd.where(ClubPayRecord::getClubId, "=", clubUser.getClubId())
@@ -21,7 +21,7 @@ import java.util.Date;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("club_pay_record") @Table("club_pay_record")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团会员缴费记录表") @Comment("协会会员缴费记录表")
public class ClubPayRecord extends BaseModel { public class ClubPayRecord extends BaseModel {
@Name @Name
@@ -36,7 +36,7 @@ public class ClubPayRecord extends BaseModel {
private Integer year; private Integer year;
@Column @Column
@Comment("社团ID") @Comment("协会ID")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String clubId; private String clubId;
@@ -14,7 +14,7 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("club_user") @Table("club_user")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团会员表") @Comment("协会会员表")
public class ClubUser extends BaseModel { public class ClubUser extends BaseModel {
@Name @Name
@@ -24,7 +24,7 @@ public class ClubUser extends BaseModel {
private String id; private String id;
@Column @Column
@Comment("社团ID") @Comment("协会ID")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String clubId; private String clubId;
@@ -39,7 +39,7 @@ public class ClubUser extends BaseModel {
private List<String> roleCode = new ArrayList<>(); private List<String> roleCode = new ArrayList<>();
@Column @Column
@Comment("社团职务") @Comment("协会职务")
@ColDefine(type = ColType.VARCHAR, width = 50) @ColDefine(type = ColType.VARCHAR, width = 50)
private String clubPosition; private String clubPosition;
@@ -54,7 +54,7 @@ public class ClubUser extends BaseModel {
private String avatar; private String avatar;
@Column @Column
@Comment("同时参加其他社团情况") @Comment("同时参加其他协会情况")
@ColDefine(type = ColType.VARCHAR, width = 500) @ColDefine(type = ColType.VARCHAR, width = 500)
private String sameTimeJoinOtherClubSituation; private String sameTimeJoinOtherClubSituation;
@@ -12,7 +12,7 @@ import java.util.Date;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("club_user_apply") @Table("club_user_apply")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团申请退出申请表") @Comment("协会申请退出申请表")
public class ClubUserApply extends BaseModel { public class ClubUserApply extends BaseModel {
@Name @Name
@@ -22,7 +22,7 @@ public class ClubUserApply extends BaseModel {
private String id; private String id;
@Column @Column
@Comment("社团ID") @Comment("协会ID")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String clubId; private String clubId;
@@ -42,7 +42,7 @@ public class ClubUserApply extends BaseModel {
private Integer mode; private Integer mode;
@Column @Column
@Comment("社团职务") @Comment("协会职务")
@ColDefine(type = ColType.VARCHAR, width = 50) @ColDefine(type = ColType.VARCHAR, width = 50)
private String clubPosition; private String clubPosition;
@@ -67,7 +67,7 @@ public class ClubUserApply extends BaseModel {
private String avatar; private String avatar;
@Column @Column
@Comment("同时参加其他社团情况") @Comment("同时参加其他协会情况")
@ColDefine(type = ColType.VARCHAR, width = 500) @ColDefine(type = ColType.VARCHAR, width = 500)
private String sameTimeJoinOtherClubSituation; private String sameTimeJoinOtherClubSituation;
@@ -21,7 +21,7 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("sys_club") @Table("sys_club")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团") @Comment("协会")
public class SysClub extends BaseModel { public class SysClub extends BaseModel {
@Column @Column
@@ -32,26 +32,26 @@ public class SysClub extends BaseModel {
private String id; private String id;
@Column @Column
@Comment("社团名称") @Comment("协会名称")
@ColDefine(type = ColType.VARCHAR, width = 100) @ColDefine(type = ColType.VARCHAR, width = 100)
@NotEmpty(message = "社团名称不能为空") @NotEmpty(message = "协会名称不能为空")
private String clubName; private String clubName;
@Column @Column
@Comment("社团缩略图") @Comment("协会缩略图")
@ColDefine(type = ColType.VARCHAR, width = 1000) @ColDefine(type = ColType.VARCHAR, width = 1000)
private String thumbnail; private String thumbnail;
@Column @Column
@Comment("社团编码") @Comment("协会编码")
@ColDefine(type = ColType.VARCHAR, width = 100) @ColDefine(type = ColType.VARCHAR, width = 100)
@NotEmpty(message = "社团编码不能为空") @NotEmpty(message = "协会编码不能为空")
private String clubCode; private String clubCode;
@Column @Column
@Comment("社团类型") @Comment("协会类型")
@ColDefine(type = ColType.VARCHAR, width = 30) @ColDefine(type = ColType.VARCHAR, width = 30)
@NotEmpty(message = "社团类型不能为空") @NotEmpty(message = "协会类型不能为空")
private String clubType; private String clubType;
@Column @Column
@@ -70,12 +70,12 @@ public class SysClub extends BaseModel {
private String concatPersonEmail; private String concatPersonEmail;
@Column @Column
@Comment("社团介绍") @Comment("协会介绍")
@ColDefine(type = ColType.TEXT) @ColDefine(type = ColType.TEXT)
private String introduce; private String introduce;
@Column @Column
@Comment("社团宗旨") @Comment("协会宗旨")
@ColDefine(type = ColType.TEXT) @ColDefine(type = ColType.TEXT)
private String purpose; private String purpose;
@@ -21,7 +21,7 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("sys_club_evaluate") @Table("sys_club_evaluate")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团评优") @Comment("协会评优")
public class SysClubEvaluate extends BaseModel { public class SysClubEvaluate extends BaseModel {
@Column @Column
@@ -32,9 +32,9 @@ public class SysClubEvaluate extends BaseModel {
private String id; private String id;
@Column @Column
@Comment("社团id") @Comment("协会id")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
@NotEmpty(message = "社团id不能为空") @NotEmpty(message = "协会id不能为空")
private String clubId; private String clubId;
@Column @Column
@@ -20,7 +20,7 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("sys_club_examine_register") @Table("sys_club_examine_register")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团年审") @Comment("协会年审")
public class SysClubExamineRegister extends BaseModel { public class SysClubExamineRegister extends BaseModel {
@Column @Column
@@ -41,12 +41,12 @@ public class SysClubExamineRegister extends BaseModel {
private Integer year; private Integer year;
@Column @Column
@Comment("社团id") @Comment("协会id")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String clubId; private String clubId;
@Column @Column
@Comment("社团名称") @Comment("协会名称")
@ColDefine(type = ColType.VARCHAR) @ColDefine(type = ColType.VARCHAR)
private String clubName; private String clubName;
@@ -109,7 +109,7 @@ public class SysClubExamineRegister extends BaseModel {
private List<JSONObject> incomeCensus; private List<JSONObject> incomeCensus;
@Column @Column
@Comment("社团成员变化情况json") @Comment("协会成员变化情况json")
@ColDefine(type = ColType.MYSQL_JSON) @ColDefine(type = ColType.MYSQL_JSON)
private List<JSONObject> changeUserNum; private List<JSONObject> changeUserNum;
@@ -11,7 +11,7 @@ import org.nutz.dao.interceptor.annotation.PrevInsert;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("sys_club_examine_register_detailed") @Table("sys_club_examine_register_detailed")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团年审其他信息") @Comment("协会年审其他信息")
public class SysClubExamineRegisterDetailed extends BaseModel { public class SysClubExamineRegisterDetailed extends BaseModel {
@Column @Column
@@ -19,7 +19,7 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("sys_club_manager") @Table("sys_club_manager")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团理事机构") @Comment("协会理事机构")
public class SysClubManager extends BaseModel { public class SysClubManager extends BaseModel {
@Column @Column
@@ -50,7 +50,7 @@ public class SysClubManager extends BaseModel {
private String changeUserName; private String changeUserName;
@Column @Column
@Comment("社团id") @Comment("协会id")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String clubId; private String clubId;
@@ -20,7 +20,7 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("sys_club_refresh") @Table("sys_club_refresh")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团换届报告") @Comment("协会换届报告")
public class SysClubRefresh extends BaseModel { public class SysClubRefresh extends BaseModel {
@Column @Column
@@ -41,7 +41,7 @@ public class SysClubRefresh extends BaseModel {
private String userName; private String userName;
@Column @Column
@Comment("社团id") @Comment("协会id")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String clubId; private String clubId;
@@ -20,7 +20,7 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("sys_club_rule") @Table("sys_club_rule")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团章程修订") @Comment("协会章程修订")
public class SysClubRule extends BaseModel { public class SysClubRule extends BaseModel {
@Column @Column
@@ -41,7 +41,7 @@ public class SysClubRule extends BaseModel {
private String userName; private String userName;
@Column @Column
@Comment("社团id") @Comment("协会id")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
private String clubId; private String clubId;
@@ -18,7 +18,7 @@ import javax.validation.constraints.NotEmpty;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Table("sys_club_sponsor") @Table("sys_club_sponsor")
@TableMeta("{'mysql-charset':'utf8mb4'}") @TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("社团发起人") @Comment("协会发起人")
public class SysClubSponsor extends BaseModel { public class SysClubSponsor extends BaseModel {
@Column @Column
@@ -29,9 +29,9 @@ public class SysClubSponsor extends BaseModel {
private String id; private String id;
@Column @Column
@Comment("社团id") @Comment("协会id")
@ColDefine(type = ColType.VARCHAR, width = 32) @ColDefine(type = ColType.VARCHAR, width = 32)
@NotEmpty(message = "社团不能为空") @NotEmpty(message = "协会不能为空")
private String clubId; private String clubId;
@Column @Column
@@ -34,9 +34,9 @@ public class ClubUserPageForm extends PageForm {
private Integer auditType; private Integer auditType;
private Integer source; private Integer source;
private Integer year; private Integer year;
/** 社团评优申报开始年度筛选条件。 */ /** 协会评优申报开始年度筛选条件。 */
private String applyStartYear; private String applyStartYear;
/** 社团评优申报结束年度筛选条件。 */ /** 协会评优申报结束年度筛选条件。 */
private String applyEndYear; private String applyEndYear;
private Boolean giveMoney; private Boolean giveMoney;
private Integer radioType; private Integer radioType;
@@ -30,14 +30,14 @@ public interface ClubPayRecordService extends BaseService<ClubPayRecord> {
void prepareSnapshots(List<ClubPayRecord> records); void prepareSnapshots(List<ClubPayRecord> records);
/** 2026年10月3日 09:51:00;操作人:wxg:设置缴费状态并保存拨付结果及日志。 /** 2026年10月3日 09:51:00;操作人:wxg:设置缴费状态并保存拨付结果及日志。
* @param ids 非空缴费记录ID数组;只允许操作当前用户授权社团的记录 * @param ids 非空缴费记录ID数组;只允许操作当前用户授权协会的记录
* @param payed true已缴费、false未缴费,不能为空 * @param payed true已缴费、false未缴费,不能为空
* @return void;参数错误或记录不在授权范围时抛出异常,事务回滚 * @return void;参数错误或记录不在授权范围时抛出异常,事务回滚
*/ */
void updatePayStatus(String[] ids, Boolean payed); void updatePayStatus(String[] ids, Boolean payed);
/** 2026年10月3日 09:51:00;操作人:wxg:退会后将该用户在指定社团的各年度缴费记录置为不拨付。 /** 2026年10月3日 09:51:00;操作人:wxg:退会后将该用户在指定协会的各年度缴费记录置为不拨付。
* @param clubId 退会社团ID,不能为空 * @param clubId 退会协会ID,不能为空
* @param userId 退会用户ID,不能为空 * @param userId 退会用户ID,不能为空
* @return void;保留缴费状态和人员快照,仅更新 isGiveMoney * @return void;保留缴费状态和人员快照,仅更新 isGiveMoney
*/ */
@@ -50,24 +50,24 @@ public interface ClubPayRecordService extends BaseService<ClubPayRecord> {
*/ */
int generateYearPayRecords(Integer year); int generateYearPayRecords(Integer year);
/** 返回当前用户可查询的社团选项,每项包含 id、clubName、clubCode;无授权社团时返回空列表。 */ /** 返回当前用户可查询的协会选项,每项包含 id、clubName、clubCode;无授权协会时返回空列表。 */
List<NutMap> getPayManageClubList(); List<NutMap> getPayManageClubList();
/** /**
* 按权限范围分页查询缴费记录,并按社团编号及缴费记录保存的加入时间排序。 * 按权限范围分页查询缴费记录,并按协会编号及缴费记录保存的加入时间排序。
* @param pageForm 页码、每页条数及姓名/工号关键字 * @param pageForm 页码、每页条数及姓名/工号关键字
* @param year 缴费年度,空值表示不限制 * @param year 缴费年度,空值表示不限制
* @param clubId 社团ID,空值表示查询全部授权社团 * @param clubId 协会ID,空值表示查询全部授权协会
* @param payed 缴费状态:1已缴费、0未缴费、空值不限 * @param payed 缴费状态:1已缴费、0未缴费、空值不限
* @param assign 保存的拨付状态:1拨付、0不拨付、空值不限 * @param assign 保存的拨付状态:1拨付、0不拨付、空值不限
* 2026年10月3日 14:33:18;操作人:wxg:在分页前按实时会员关系筛选,历史年度也使用当前会员状态。 * 2026年10月3日 14:33:18;操作人:wxg:在分页前按实时会员关系筛选,历史年度也使用当前会员状态。
* @param memberState 1在会、0退会、null不限;在会以同一 clubId/userId 存在 club_user 记录为准 * @param memberState 1在会、0退会、null不限;在会以同一 clubId/userId 存在 club_user 记录为准
* @return 分页记录,list 包含缴费字段、社团及人员信息、applyTime(保存的入会时间)与 assign(isGiveMoney 保存值转换为布尔值) * @return 分页记录,list 包含缴费字段、协会及人员信息、applyTime(保存的入会时间)与 assign(isGiveMoney 保存值转换为布尔值)
*/ */
Pagination pageData(PageForm pageForm, Integer year, String clubId, Integer payed, Integer assign, Integer memberState); Pagination pageData(PageForm pageForm, Integer year, String clubId, Integer payed, Integer assign, Integer memberState);
/** /**
* 获取社团成员是否拨付 * 获取协会成员是否拨付
* @param userId * @param userId
* @param clubId * @param clubId
*/ */
@@ -48,17 +48,17 @@ public class ClubRegisterImportService {
String name = values[0]; String name = values[0];
String code = values[1]; String code = values[1];
if (StrUtil.isBlank(name)) { if (StrUtil.isBlank(name)) {
throw new IllegalArgumentException("社团名称不能为空"); throw new IllegalArgumentException("协会名称不能为空");
} }
if (dao.count(SysClub.class, Cnd.where("clubName", "=", name)) > 0 if (dao.count(SysClub.class, Cnd.where("clubName", "=", name)) > 0
|| (StrUtil.isNotBlank(code) && dao.count(SysClub.class, Cnd.where("clubCode", "=", code)) > 0)) { || (StrUtil.isNotBlank(code) && dao.count(SysClub.class, Cnd.where("clubCode", "=", code)) > 0)) {
return NutMap.NEW().addv("status", "跳过").addv("message", "社团名称或编码已存在,保留原记录(含草稿、审批中及已解散社团)"); return NutMap.NEW().addv("status", "跳过").addv("message", "协会名称或编码已存在,保留原记录(含草稿、审批中及已解散协会)");
} }
if (name.length() > 100 || code.length() > 100) { if (name.length() > 100 || code.length() > 100) {
throw new IllegalArgumentException("社团名称和编码不能超过100个字符"); throw new IllegalArgumentException("协会名称和编码不能超过100个字符");
} }
if (sysDictService.getSubListByCode("CLUB_REGISTER_TYPE").stream().noneMatch(item -> values[2].equals(item.getCode()))) { if (sysDictService.getSubListByCode("CLUB_REGISTER_TYPE").stream().noneMatch(item -> values[2].equals(item.getCode()))) {
throw new IllegalArgumentException("请选择类型对照表中的社团类型编码"); throw new IllegalArgumentException("请选择类型对照表中的协会类型编码");
} }
try { try {
LocalDate.parse(values[3]); LocalDate.parse(values[3]);
@@ -82,7 +82,7 @@ public class ClubRegisterImportService {
} }
ProcessDefine define = dao.fetch(ProcessDefine.class, Cnd.where("name", "=", "XHZC").and("state", "=", 1).desc("version")); ProcessDefine define = dao.fetch(ProcessDefine.class, Cnd.where("name", "=", "XHZC").and("state", "=", 1).desc("version"));
if (define == null) { if (define == null) {
throw new IllegalArgumentException("未配置可用的社团注册定义,无法记录直接通过状态"); throw new IllegalArgumentException("未配置可用的协会注册定义,无法记录直接通过状态");
} }
if (StrUtil.isBlank(code)) { if (StrUtil.isBlank(code)) {
int sequence = dao.count(SysClub.class, Cnd.where("year(createTime)", "=", DateUtil.thisYear())) + 1; int sequence = dao.count(SysClub.class, Cnd.where("year(createTime)", "=", DateUtil.thisYear())) + 1;
@@ -116,19 +116,19 @@ public class ClubRegisterImportService {
club.setManageFile(files(values[18])); club.setManageFile(files(values[18]));
club.setYearPlanFile(files(values[19])); club.setYearPlanFile(files(values[19]));
club.setFiles(files(values[20])); club.setFiles(files(values[20]));
// 复用单个注册:社团、发起人、理事会员资料及入会时间、年度缴费记录和快照。 // 复用单个注册:协会、发起人、理事会员资料及入会时间、年度缴费记录和快照。
sysClubService.doAdd(club, managers); sysClubService.doAdd(club, managers);
// 与注册通过后的角色授权一致,但不启动流程或创建审批任务。 // 与注册通过后的角色授权一致,但不启动流程或创建审批任务。
for (NutMap manager : managers) { for (NutMap manager : managers) {
Sys_role role = sysRoleService.getByCode(manager.getString("roleCode")); Sys_role role = sysRoleService.getByCode(manager.getString("roleCode"));
if (role == null) throw new IllegalArgumentException("社团角色不存在:" + manager.getString("roleCode")); if (role == null) throw new IllegalArgumentException("协会角色不存在:" + manager.getString("roleCode"));
Sys_user_role relation = new Sys_user_role(); Sys_user_role relation = new Sys_user_role();
relation.setUserId(manager.getString("userId")); relation.setUserId(manager.getString("userId"));
relation.setRoleId(role.getId()); relation.setRoleId(role.getId());
relation.setClubId(club.getId()); relation.setClubId(club.getId());
dao.insert(relation); dao.insert(relation);
} }
// 现有社团查询通过已完成实例识别有效社团;保留可追溯的管理员直接通过记录。 // 现有协会查询通过已完成实例识别有效协会;保留可追溯的管理员直接通过记录。
ProcessInstance instance = new ProcessInstance(); ProcessInstance instance = new ProcessInstance();
instance.setProcessDefineId(define.getId()); instance.setProcessDefineId(define.getId());
instance.setBusinessNo(club.getId()); instance.setBusinessNo(club.getId());
@@ -146,9 +146,9 @@ public class ClubRegisterImportService {
private void addManagers(List<NutMap> managers, Set<String> ids, String text, String roleCode, boolean required) { private void addManagers(List<NutMap> managers, Set<String> ids, String text, String roleCode, boolean required) {
List<String> names = split(text); List<String> names = split(text);
if (required && names.size() != 1) throw new IllegalArgumentException("会长、秘书长各需填写1名人员工号"); if (required && names.isEmpty()) throw new IllegalArgumentException("会长、秘书长各需至少填写1名人员工号");
if (!names.isEmpty() && sysRoleService.getByCode(roleCode) == null) { if (!names.isEmpty() && sysRoleService.getByCode(roleCode) == null) {
throw new IllegalArgumentException("系统未配置社团角色:" + roleCode); throw new IllegalArgumentException("系统未配置协会角色:" + roleCode);
} }
for (String name : names) { for (String name : names) {
Sys_user user = findUser(name); Sys_user user = findUser(name);
@@ -21,27 +21,27 @@ public interface ClubUserJoinService extends BaseService<ClubUserApply> {
/** /**
* 构造退会申请数据。 * 构造退会申请数据。
* 当用户没有历史入会申请记录时,使用当前社团成员关系和人员基础信息兜底生成一份退会申请, * 当用户没有历史入会申请记录时,使用当前协会成员关系和人员基础信息兜底生成一份退会申请,
* 以保证会长等通过社团注册直接入会的人员也能正常发起退会流程。 * 以保证会长等通过协会注册直接入会的人员也能正常发起退会流程。
* *
* @param clubId 社团ID * @param clubId 协会ID
* @param userId 用户ID * @param userId 用户ID
* @return 退会申请对象,不存在当前成员关系时返回null * @return 退会申请对象,不存在当前成员关系时返回null
*/ */
ClubUserApply buildExitApply(String clubId, String userId); ClubUserApply buildExitApply(String clubId, String userId);
/** /**
* 获取社团成员的入会时间,优先取当前 club_user.joinTime。 * 获取协会成员的入会时间,优先取当前 club_user.joinTime。
* 旧会员关系尚无入会时间时,保留历史申请及社团创办时间的原有回退规则。 * 旧会员关系尚无入会时间时,保留历史申请及协会创办时间的原有回退规则。
* *
* @param clubId 社团ID * @param clubId 协会ID
* @param userId 用户ID * @param userId 用户ID
* @return 入会时间 * @return 入会时间
*/ */
java.util.Date resolveJoinTime(String clubId, String userId); java.util.Date resolveJoinTime(String clubId, String userId);
/** /**
* 校验当前用户是否满足申请加入社团的资格。 * 校验当前用户是否满足申请加入协会的资格。
* *
* @param userId 用户ID * @param userId 用户ID
* @return 校验不通过时返回提示语,通过时返回null * @return 校验不通过时返回提示语,通过时返回null
@@ -17,7 +17,7 @@ public interface SysClubExamineService extends BaseService<SysClubExamineRegiste
// /** // /**
// * 按会员入会、退休、退会时间统计指定年度的在职和退休人数变化。 // * 按会员入会、退休、退会时间统计指定年度的在职和退休人数变化。
// * // *
// * @param clubId 社团ID // * @param clubId 协会ID
// * @param year 统计年度 // * @param year 统计年度
// * @return 在职和退休人员的年初、增加、减少、年末人数 // * @return 在职和退休人员的年初、增加、减少、年末人数
// */ // */
@@ -26,7 +26,7 @@ public interface SysClubExamineService extends BaseService<SysClubExamineRegiste
// //
/** /**
* 2026年10月2日22:00点开始新开发的代码;实际整改:2026年10月3日 10:14:04;操作人:wxg。 * 2026年10月2日22:00点开始新开发的代码;实际整改:2026年10月3日 10:14:04;操作人:wxg。
* @param clubId 所选社团ID,不能为空 * @param clubId 所选协会ID,不能为空
* @param year 所选公历年度,1至9998;按该年1月1日至下一年1月1日左闭右开统计 * @param year 所选公历年度,1至9998;按该年1月1日至下一年1月1日左闭右开统计
* @return 两类人员计数列表:yearFirstNum年初、yearAddNum增加、yearReduceNum减少、thisYearNum公式年末; * @return 两类人员计数列表:yearFirstNum年初、yearAddNum增加、yearReduceNum减少、thisYearNum公式年末;
* personType为类别,userState仅兼容已有查看页标签;statisticsWarnings为核查提示, * personType为类别,userState仅兼容已有查看页标签;statisticsWarnings为核查提示,
@@ -25,7 +25,7 @@ public interface SysClubInfoManageService extends BaseService<ClubCommonPageVo>
/** /**
* 修改时间:2026年10月2日 23:06:55;操作人:wxg。 * 修改时间:2026年10月2日 23:06:55;操作人:wxg。
* 当前会员查询前,用已完成 WF 入会流程的最近一次会长同意审核时间补齐空入会时间。 * 当前会员查询前,用已完成 WF 入会流程的最近一次会长同意审核时间补齐空入会时间。
* @param pageForm clubId 为选中社团ID;radioType=4 查历史成员且不补齐,其他值查当前会员; * @param pageForm clubId 为选中协会ID;radioType=4 查历史成员且不补齐,其他值查当前会员;
* 姓名/工号、人员类型、在职状态、入会日期范围及分页排序参数用于列表筛选 * 姓名/工号、人员类型、在职状态、入会日期范围及分页排序参数用于列表筛选
* @return 会员分页对象,list 中 joinTime 为会员关系保存的入会时间;历史成员仍取申请表时间 * @return 会员分页对象,list 中 joinTime 为会员关系保存的入会时间;历史成员仍取申请表时间
*/ */
@@ -38,7 +38,7 @@ public interface SysClubInfoManageService extends BaseService<ClubCommonPageVo>
ClubUserJoinVo exitManageInfo(String id); ClubUserJoinVo exitManageInfo(String id);
/** /**
* 管理端直接退会,同时新增系统处理记录并清理当前社团会员关系及角色关联。 * 管理端直接退会,同时新增系统处理记录并清理当前协会会员关系及角色关联。
* @param membershipId club_user 的会员关系ID,不是用户ID或申请ID * @param membershipId club_user 的会员关系ID,不是用户ID或申请ID
* @return void;会员关系不存在或已退会时抛出异常,事务回滚 * @return void;会员关系不存在或已退会时抛出异常,事务回滚
*/ */
@@ -24,8 +24,8 @@ import java.util.List;
public interface SysClubService extends BaseService<SysClub> { public interface SysClubService extends BaseService<SysClub> {
/** /**
* 获取入会申请的社团下拉选项,供 PC 与 H5 共用。 * 获取入会申请的协会下拉选项,供 PC 与 H5 共用。
* @return 存在已完成 WF 流程的社团列表,每项为 SysClub,包含 id、clubName 等社团字段;同一社团只返回一次 * @return 存在已完成 WF 流程的协会列表,每项为 SysClub,包含 id、clubName 等协会字段;同一协会只返回一次
*/ */
List<SysClub> listJoinClubOptions(); List<SysClub> listJoinClubOptions();
@@ -19,7 +19,7 @@ public interface SysClubUserService extends BaseService<ClubUser> {
/** /**
* 为直接新增的会员关系设置本批次的后端确认时间,不覆盖已有入会时间。 * 为直接新增的会员关系设置本批次的后端确认时间,不覆盖已有入会时间。
* @param members 手工录入、导入或社团创建时直接新增的会员关系;不得传入审核入会记录 * @param members 手工录入、导入或协会创建时直接新增的会员关系;不得传入审核入会记录
* @return void;仅填写列表内 joinTime 为空的对象,由调用方在同一事务中保存 * @return void;仅填写列表内 joinTime 为空的对象,由调用方在同一事务中保存
*/ */
void prepareDirectJoinTimes(List<ClubUser> members); void prepareDirectJoinTimes(List<ClubUser> members);
@@ -114,11 +114,11 @@ public class ClubPayRecordServiceImpl extends BaseServiceImpl<ClubPayRecord> imp
if (records == null || records.isEmpty()) { if (records == null || records.isEmpty()) {
return; return;
} }
// 新增记录须明确所属年度、社团和人员,避免缺失参数形成不受限制的批量查询。 // 新增记录须明确所属年度、协会和人员,避免缺失参数形成不受限制的批量查询。
for (ClubPayRecord record : records) { for (ClubPayRecord record : records) {
if (record == null || record.getYear() == null || record.getYear() < 1 || record.getYear() > 9998 if (record == null || record.getYear() == null || record.getYear() < 1 || record.getYear() > 9998
|| StrUtil.isBlank(record.getClubId()) || StrUtil.isBlank(record.getUserId())) { || StrUtil.isBlank(record.getClubId()) || StrUtil.isBlank(record.getUserId())) {
throw new IllegalArgumentException("缴费记录必须提供有效年度、社团和用户"); throw new IllegalArgumentException("缴费记录必须提供有效年度、协会和用户");
} }
} }
List<String> userIds = records.stream().map(ClubPayRecord::getUserId).distinct().toList(); List<String> userIds = records.stream().map(ClubPayRecord::getUserId).distinct().toList();
@@ -193,15 +193,15 @@ public class ClubPayRecordServiceImpl extends BaseServiceImpl<ClubPayRecord> imp
return records.size(); return records.size();
} }
/** 校工会社团管理员与系统管理员可查看全部社团缴费记录。 */ /** 校工会协会管理员与系统管理员可查看全部协会缴费记录。 */
private boolean canManageAllPayClubs() { private boolean canManageAllPayClubs() {
return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name()); return AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name());
} }
/** 普通菜单授权用户仅能查看其有效角色关联的社团;无关联时显式返回空范围。 */ /** 普通菜单授权用户仅能查看其有效角色关联的协会;无关联时显式返回空范围。 */
private void addPayClubScope(Cnd cnd, String clubField) { private void addPayClubScope(Cnd cnd, String clubField) {
if (!canManageAllPayClubs()) { if (!canManageAllPayClubs()) {
// 用户ID使用 SQL 参数绑定,社团集合交给 Cnd 处理,避免空集合被可选条件忽略。 // 用户ID使用 SQL 参数绑定,协会集合交给 Cnd 处理,避免空集合被可选条件忽略。
Sql scopeSql = Sqls.create(""" Sql scopeSql = Sqls.create("""
SELECT DISTINCT ur.clubId FROM sys_user_role ur SELECT DISTINCT ur.clubId FROM sys_user_role ur
INNER JOIN sys_role r ON r.id = ur.roleId AND r.disabled = false INNER JOIN sys_role r ON r.id = ur.roleId AND r.disabled = false
@@ -246,7 +246,7 @@ public class ClubPayRecordServiceImpl extends BaseServiceImpl<ClubPayRecord> imp
addPayClubScope(cnd, "cpr.clubId"); addPayClubScope(cnd, "cpr.clubId");
cnd.andEX("cpr.payed", "=", payed); cnd.andEX("cpr.payed", "=", payed);
// 在分页前筛选,保证总条数及页数准确;EXISTS 不会因重复会员关系增加缴费记录条数。 // 在分页前筛选,保证总条数及页数准确;EXISTS 不会因重复会员关系增加缴费记录条数。
// 与列表 count 标签一致,只看该人员在该社团的当前关系,不依赖退会申请或缴费记录年份。 // 与列表 count 标签一致,只看该人员在该协会的当前关系,不依赖退会申请或缴费记录年份。
if (memberState != null) { if (memberState != null) {
String membershipExists = "EXISTS (SELECT 1 FROM club_user membership " String membershipExists = "EXISTS (SELECT 1 FROM club_user membership "
+ "WHERE membership.clubId = cpr.clubId AND membership.userId = cpr.userId)"; + "WHERE membership.clubId = cpr.clubId AND membership.userId = cpr.userId)";
@@ -263,7 +263,7 @@ public class ClubPayRecordServiceImpl extends BaseServiceImpl<ClubPayRecord> imp
cnd.and(new Static("(" + ASSIGNED_SQL + ") = " + (assign == 0 ? 0 : 1))); cnd.and(new Static("(" + ASSIGNED_SQL + ") = " + (assign == 0 ? 0 : 1)));
} }
// 全社团管理角色先按社团编号分组;普通角色仅在授权社团内按加入时间倒序。 // 全协会管理角色先按协会编号分组;普通角色仅在授权协会内按加入时间倒序。
if (canManageAllPayClubs()) { if (canManageAllPayClubs()) {
cnd.asc("c.clubCode"); cnd.asc("c.clubCode");
cnd.asc("c.id"); cnd.asc("c.id");
@@ -336,7 +336,7 @@ public class ClubPayRecordServiceImpl extends BaseServiceImpl<ClubPayRecord> imp
public void disableExitMemberPayment(String clubId, String userId) { public void disableExitMemberPayment(String clubId, String userId) {
// 2026年10月3日 09:51:00;操作人:wxg:退会影响实时会员关系对应的各年度名单,保留原缴费状态。 // 2026年10月3日 09:51:00;操作人:wxg:退会影响实时会员关系对应的各年度名单,保留原缴费状态。
if (StrUtil.isBlank(clubId) || StrUtil.isBlank(userId)) { if (StrUtil.isBlank(clubId) || StrUtil.isBlank(userId)) {
throw new IllegalArgumentException("退会社团和用户不能为空"); throw new IllegalArgumentException("退会协会和用户不能为空");
} }
Sql sql = Sqls.create("UPDATE club_pay_record SET isGiveMoney = false WHERE clubId = @clubId AND userId = @userId"); Sql sql = Sqls.create("UPDATE club_pay_record SET isGiveMoney = false WHERE clubId = @clubId AND userId = @userId");
sql.setParam("clubId", clubId); sql.setParam("clubId", clubId);
@@ -108,7 +108,7 @@ public class ClubUserJoinServiceImpl extends BaseServiceImpl<ClubUserApply> impl
return latestApply; return latestApply;
} }
// 对于会长等注册社团时直接入会的人员,没有申请记录时从当前成员信息兜底生成退会申请。 // 对于会长等注册协会时直接入会的人员,没有申请记录时从当前成员信息兜底生成退会申请。
ClubUser clubUser = currentMember; ClubUser clubUser = currentMember;
if (clubUser == null) { if (clubUser == null) {
return null; return null;
@@ -140,7 +140,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
// .setParam("clubId", clubId); // .setParam("clubId", clubId);
// List<NutMap> applications = listMap(applySql); // List<NutMap> applications = listMap(applySql);
// if (!applications.isEmpty()) { // if (!applications.isEmpty()) {
// // 流程状态改为按当前社团申请批量查询,避免相关子查询对流程实例表重复扫描。 // // 流程状态改为按当前协会申请批量查询,避免相关子查询对流程实例表重复扫描。
// List<String> applicationIds = applications.stream() // List<String> applicationIds = applications.stream()
// .map(application -> application.getString("id")) // .map(application -> application.getString("id"))
// .filter(StrUtil::isNotBlank) // .filter(StrUtil::isNotBlank)
@@ -313,14 +313,14 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
@Override @Override
public List<NutMap> getClubUserNum(String clubId, Integer year) { public List<NutMap> getClubUserNum(String clubId, Integer year) {
if (StrUtil.isBlank(clubId) || year == null || year < 1 || year > 9998) { if (StrUtil.isBlank(clubId) || year == null || year < 1 || year > 9998) {
throw new IllegalArgumentException("请选择有效社团和统计年度"); throw new IllegalArgumentException("请选择有效协会和统计年度");
} }
MemberChangeStatistics stats = new MemberChangeStatistics(year); MemberChangeStatistics stats = new MemberChangeStatistics(year);
// 年初基数来自当年名单,不受缴费、拨付或当前退会状态影响;同一会员按 userId 去重。 // 年初基数来自当年名单,不受缴费、拨付或当前退会状态影响;同一会员按 userId 去重。
List<NutMap> roster = listMap(Sqls.create("SELECT userId, joinTime, personType, retireDate, operateLogs FROM club_pay_record WHERE clubId = @clubId AND year = @year") List<NutMap> roster = listMap(Sqls.create("SELECT userId, joinTime, personType, retireDate, operateLogs FROM club_pay_record WHERE clubId = @clubId AND year = @year")
.setParam("clubId", clubId).setParam("year", year)); .setParam("clubId", clubId).setParam("year", year));
if (roster.isEmpty()) { if (roster.isEmpty()) {
stats.invalidate(year + "年度该社团没有缴费名单,请检查年度名单生成计划任务及名单数据,不能将缺失名单视为年初零人。"); stats.invalidate(year + "年度该协会没有缴费名单,请检查年度名单生成计划任务及名单数据,不能将缺失名单视为年初零人。");
return stats.result(); return stats.result();
} }
boolean generated = roster.stream().anyMatch(row -> { boolean generated = roster.stream().anyMatch(row -> {
@@ -328,7 +328,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
return logs != null && logs.contains("缴费管理-定时任务自动设置"); return logs != null && logs.contains("缴费管理-定时任务自动设置");
}); });
if (!generated) { if (!generated) {
stats.warnings.add("未发现该社团年度名单的定时生成记录,请检查计划任务及名单完整性;手工入会记录不能证明年度名单已完整生成。"); stats.warnings.add("未发现该协会年度名单的定时生成记录,请检查计划任务及名单完整性;手工入会记录不能证明年度名单已完整生成。");
} }
Map<String, NutMap> snapshots = new java.util.LinkedHashMap<>(); Map<String, NutMap> snapshots = new java.util.LinkedHashMap<>();
Map<String, AnnualMemberPeriod> periods = new java.util.LinkedHashMap<>(); Map<String, AnnualMemberPeriod> periods = new java.util.LinkedHashMap<>();
@@ -378,7 +378,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
if (member.getTime("joinTime") == null) { if (member.getTime("joinTime") == null) {
if (!snapshots.containsKey(member.getString("userId"))) stats.invalidate("当前会员存在加入时间缺失,无法确定年度增加,请核查。"); if (!snapshots.containsKey(member.getString("userId"))) stats.invalidate("当前会员存在加入时间缺失,无法确定年度增加,请核查。");
} else { } else {
// 手工录入、导入和成立社团时加入的成员没有 mode=1,也需要计入可靠的年度增加。 // 手工录入、导入和成立协会时加入的成员没有 mode=1,也需要计入可靠的年度增加。
addAnnualPeriod(periods, member.getString("userId"), member.getTime("joinTime"), stats); addAnnualPeriod(periods, member.getString("userId"), member.getTime("joinTime"), stats);
} }
} }
@@ -549,7 +549,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
@Override @Override
public List<NutMap> getJgUser(String clubId) { public List<NutMap> getJgUser(String clubId) {
// 构建社团成员查询SQL,理事机构成员优先展示,普通成员随后展示。 // 构建协会成员查询SQL,理事机构成员优先展示,普通成员随后展示。
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
cl.*, cl.*,
@@ -638,13 +638,13 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
@Override @Override
public Pagination<ClubExaminePageVo> minePageData(ClubUserPageForm pageForm) { public Pagination<ClubExaminePageVo> minePageData(ClubUserPageForm pageForm) {
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
// 非系统管理员,仅查询自己管理的社团(空集合判断避免in查询异常) // 非系统管理员,仅查询自己管理的协会(空集合判断避免in查询异常)
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
List<SysClub> myManageClub = sysClubService.getMyManageClub(); List<SysClub> myManageClub = sysClubService.getMyManageClub();
if (!Lang.isEmpty(myManageClub)) { if (!Lang.isEmpty(myManageClub)) {
cnd.and("info.clubId", "in", myManageClub.stream().map(SysClub::getId).collect(Collectors.toList())); cnd.and("info.clubId", "in", myManageClub.stream().map(SysClub::getId).collect(Collectors.toList()));
} else { } else {
// 无管理社团,返回空分页结果 // 无管理协会,返回空分页结果
return new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), 0, new ArrayList<>()); return new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), 0, new ArrayList<>());
} }
} }
@@ -719,7 +719,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
@Override @Override
public ClubExamineVo findOne(String id) { public ClubExamineVo findOne(String id) {
// 构建单条考核登记查询SQL(关联社团、联系人信息) // 构建单条考核登记查询SQL(关联协会、联系人信息)
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
scer.*, scer.*,
@@ -750,7 +750,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
a.auditOpinion, a.auditOpinion,
a.auditSign, a.auditSign,
CASE CASE
WHEN a.id = scer.clubAuditId THEN '社团审核' WHEN a.id = scer.clubAuditId THEN '协会审核'
WHEN a.id = scer.clubLeaderAuditId THEN '会长审核' WHEN a.id = scer.clubLeaderAuditId THEN '会长审核'
WHEN a.id = scer.schoolAuditId THEN '校工会审核' WHEN a.id = scer.schoolAuditId THEN '校工会审核'
END AS auditName END AS auditName
@@ -982,7 +982,7 @@ public class SysClubExamineServiceImpl extends BaseServiceImpl<SysClubExamineReg
} }
/** /**
* 构建分页查询基础条件(年度、社团ID) * 构建分页查询基础条件(年度、协会ID)
* @param cnd 条件对象 * @param cnd 条件对象
* @param pageForm 分页参数 * @param pageForm 分页参数
* @param tableAlias 表别名 * @param tableAlias 表别名
@@ -207,7 +207,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
/** /**
* 2026年10月3日 12:01:28;操作人:wxg:当前会员以 club_user 分页,查询期间不补齐或修改数据库。 * 2026年10月3日 12:01:28;操作人:wxg:当前会员以 club_user 分页,查询期间不补齐或修改数据库。
* @param pageForm clubId 为选中社团ID;radioType=4 查历史成员,其他值查当前会员; * @param pageForm clubId 为选中协会ID;radioType=4 查历史成员,其他值查当前会员;
* searchKeyword 匹配会员表姓名/工号,personType/userState 筛选当前用户资料; * searchKeyword 匹配会员表姓名/工号,personType/userState 筛选当前用户资料;
* focusMembers=true 同时筛选当前人员类型为自聘人员或其他的会员,false/空值不限制; * focusMembers=true 同时筛选当前人员类型为自聘人员或其他的会员,false/空值不限制;
* joinDateStart/joinDateEnd 为 yyyy-MM-dd,pageNumber/pageSize 为分页参数,排序只允许列表字段 * joinDateStart/joinDateEnd 为 yyyy-MM-dd,pageNumber/pageSize 为分页参数,排序只允许列表字段
@@ -219,7 +219,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
return historyMemberPageData(pageForm); return historyMemberPageData(pageForm);
} }
if (StrUtil.isBlank(pageForm.getClubId())) { if (StrUtil.isBlank(pageForm.getClubId())) {
throw new IllegalArgumentException("请选择社团后查询会员信息"); throw new IllegalArgumentException("请选择协会后查询会员信息");
} }
// 姓名、工号、电话及入会时间使用持久化字段;列排序需要其他资料时才关联用户表。 // 姓名、工号、电话及入会时间使用持久化字段;列排序需要其他资料时才关联用户表。
Map<String, String> sortColumns = Map.of( Map<String, String> sortColumns = Map.of(
@@ -242,7 +242,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
sql.setVar("userJoin", needsUnitSort ? "LEFT JOIN vw_user u ON u.id = scu.userId" sql.setVar("userJoin", needsUnitSort ? "LEFT JOIN vw_user u ON u.id = scu.userId"
: needsUser ? "LEFT JOIN sys_user u ON u.id = scu.userId" : ""); : needsUser ? "LEFT JOIN sys_user u ON u.id = scu.userId" : "");
Cnd cnd = Cnd.where("scu.clubId", "=", pageForm.getClubId()); Cnd cnd = Cnd.where("scu.clubId", "=", pageForm.getClubId());
// 在分页和总数统计前同时筛选两类人员,保持当前社团及其他查询条件的限制。 // 在分页和总数统计前同时筛选两类人员,保持当前协会及其他查询条件的限制。
if (Boolean.TRUE.equals(pageForm.getFocusMembers())) { if (Boolean.TRUE.equals(pageForm.getFocusMembers())) {
cnd.and("u.personType", "in", List.of("自聘人员", "其他")); cnd.and("u.personType", "in", List.of("自聘人员", "其他"));
} }
@@ -290,7 +290,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
/** /**
* 2026年10月3日 12:01:28;操作人:wxg:分页后批量获取本页其他资料,避免把完整用户视图带入默认统计和分页。 * 2026年10月3日 12:01:28;操作人:wxg:分页后批量获取本页其他资料,避免把完整用户视图带入默认统计和分页。
* @param members 当前页会员关系;空页不查询,姓名/工号/电话/入会时间始终保留会员表值 * @param members 当前页会员关系;空页不查询,姓名/工号/电话/入会时间始终保留会员表值
* @param clubId 当前选中社团ID,用于补充社团名称 * @param clubId 当前选中协会ID,用于补充协会名称
*/ */
private void fillMemberPageDetails(List<ClubUserCommonPageVo> members, String clubId) { private void fillMemberPageDetails(List<ClubUserCommonPageVo> members, String clubId) {
if (members.isEmpty()) return; if (members.isEmpty()) return;
@@ -322,8 +322,8 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
} }
/** /**
* 当前社团历史退会记录分页;每次退会保留一行,不按人员去重,也不要求关联 WF 已完成流程。 * 当前协会历史退会记录分页;每次退会保留一行,不按人员去重,也不要求关联 WF 已完成流程。
* @param pageForm clubId 为当前社团ID;姓名/工号、人员类型、在职状态及 yyyy-MM-dd 入会日期范围为可选筛选; * @param pageForm clubId 为当前协会ID;姓名/工号、人员类型、在职状态及 yyyy-MM-dd 入会日期范围为可选筛选;
* focusMembers=true 筛选当前人员类型为自聘人员或其他的历史成员,false/空值不限制; * focusMembers=true 筛选当前人员类型为自聘人员或其他的历史成员,false/空值不限制;
* pageNumber/pageSize 为分页参数,pageOrderName/pageOrderBy 为列排序参数 * pageNumber/pageSize 为分页参数,pageOrderName/pageOrderBy 为列排序参数
* @return 会员展示分页对象;id 为申请ID,joinTime/exitTime 来自申请,人员资料取用户视图当前值 * @return 会员展示分页对象;id 为申请ID,joinTime/exitTime 来自申请,人员资料取用户视图当前值
@@ -493,7 +493,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
* 退会申请审批完成后会删除club_user成员关系,因此退会管理以退会申请记录为主数据源。 * 退会申请审批完成后会删除club_user成员关系,因此退会管理以退会申请记录为主数据源。
* 同时保留旧sys_club_user表中已退会的数据,避免历史直接移出记录无法查询。 * 同时保留旧sys_club_user表中已退会的数据,避免历史直接移出记录无法查询。
* 2026年10月3日 19:42:45;操作人:wxg:mode=2 为系统处理,无需 WF;mode=0 通过 EXISTS 判断 WF 完成,避免一对多重复。 * 2026年10月3日 19:42:45;操作人:wxg:mode=2 为系统处理,无需 WF;mode=0 通过 EXISTS 判断 WF 完成,避免一对多重复。
* 同一用户、社团存在以上申请时不再重复展示旧历史表记录;每条申请仍独立保留,列表与详情共用。 * 同一用户、协会存在以上申请时不再重复展示旧历史表记录;每条申请仍独立保留,列表与详情共用。
* *
* @return 退会管理列表和详情共用的数据来源SQL * @return 退会管理列表和详情共用的数据来源SQL
*/ */
@@ -597,7 +597,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
NutMap nutMap = NutMap.NEW(); NutMap nutMap = NutMap.NEW();
nutMap.put("id", "0"); nutMap.put("id", "0");
nutMap.put("clubName", Globals.AppName); nutMap.put("clubName", Globals.AppName);
// 社团管理树只按社团编号展示,避免影响公共社团列表的原有排序。 // 协会管理树只按协会编号展示,避免影响公共协会列表的原有排序。
List<SysClub> clubList = sysClubService.getMyManageClub().stream() List<SysClub> clubList = sysClubService.getMyManageClub().stream()
.sorted(Comparator.comparing(SysClub::getClubCode, Comparator.nullsLast(String::compareTo))) .sorted(Comparator.comparing(SysClub::getClubCode, Comparator.nullsLast(String::compareTo)))
.toList(); .toList();
@@ -608,7 +608,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
/** /**
* 按现有成员排序渲染 clubRegis Word 模板,模板读取为空时提前阻止解析及下载。 * 按现有成员排序渲染 clubRegis Word 模板,模板读取为空时提前阻止解析及下载。
* @param clubId 当前选中社团ID,登记表包含该社团资料和成员信息 * @param clubId 当前选中协会ID,登记表包含该协会资料和成员信息
* @param response 写入生成的 DOCX 附件;模板或渲染失败时抛出中文业务异常 * @param response 写入生成的 DOCX 附件;模板或渲染失败时抛出中文业务异常
* @return void,文件内容通过响应输出,错误结果由统一异常处理返回 * @return void,文件内容通过响应输出,错误结果由统一异常处理返回
*/ */
@@ -687,17 +687,17 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl<ClubCommonPage
// 文件存储读取失败会返回空内容,先校验再交给 POI,避免显示英文 EmptyFileException。 // 文件存储读取失败会返回空内容,先校验再交给 POI,避免显示英文 EmptyFileException。
byte[] templateBytes = templateStream == null ? new byte[0] : templateStream.readAllBytes(); byte[] templateBytes = templateStream == null ? new byte[0] : templateStream.readAllBytes();
if (templateBytes.length == 0) { if (templateBytes.length == 0) {
throw new BaseException("社团登记表模板读取失败,请检查模板文件及文件存储服务。"); throw new BaseException("协会登记表模板读取失败,请检查模板文件及文件存储服务。");
} }
XWPFTemplate.compile(new ByteArrayInputStream(templateBytes), config).render(docData).writeAndClose(byteArrayOutputStream); XWPFTemplate.compile(new ByteArrayInputStream(templateBytes), config).render(docData).writeAndClose(byteArrayOutputStream);
CommonDownloadUtil.download("教职工文体社团登记表(" + clubInfo.getString("clubName") + ").docx", byteArrayOutputStream.toByteArray(), response); CommonDownloadUtil.download("教职工文体协会登记表(" + clubInfo.getString("clubName") + ").docx", byteArrayOutputStream.toByteArray(), response);
} catch (BaseException e) { } catch (BaseException e) {
// 保留模板配置及空文件校验的具体提示,同时记录完整异常以便排查。 // 保留模板配置及空文件校验的具体提示,同时记录完整异常以便排查。
log.error("社团登记表导出失败,id:{}", clubId, e); log.error("协会登记表导出失败,id:{}", clubId, e);
throw e; throw e;
} catch (IOException | RuntimeException e) { } catch (IOException | RuntimeException e) {
log.error("社团登记表导出失败,id:{}", clubId, e); log.error("协会登记表导出失败,id:{}", clubId, e);
throw new BaseException("社团登记表生成失败,请检查模板是否为有效的 Word 文件或联系管理员。"); throw new BaseException("协会登记表生成失败,请检查模板是否为有效的 Word 文件或联系管理员。");
} }
} }
} }
@@ -72,7 +72,7 @@ public class SysClubServiceImpl extends BaseServiceImpl<SysClub> implements SysC
@Override @Override
public List<SysClub> listJoinClubOptions() { public List<SysClub> listJoinClubOptions() {
// 注册、换届等流程可能使用同一社团ID作为业务号;仅判断流程存在,避免联表将社团重复返回。 // 注册、换届等流程可能使用同一协会ID作为业务号;仅判断流程存在,避免联表将协会重复返回。
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT c.* SELECT c.*
FROM sys_club c FROM sys_club c
@@ -100,7 +100,7 @@ public class SysClubServiceImpl extends BaseServiceImpl<SysClub> implements SysC
LEFT JOIN sys_user u on club.concatPerson = u.id LEFT JOIN sys_user u on club.concatPerson = u.id
$condition $condition
""").setParam("userId", SecurityUtil.getUserId()); """).setParam("userId", SecurityUtil.getUserId());
// 同一社团可能关联注册、换届等多个已完成 WF 流程;仅检查流程存在,避免列表和分页总数重复。 // 同一协会可能关联注册、换届等多个已完成 WF 流程;仅检查流程存在,避免列表和分页总数重复。
cnd.and(new Static("EXISTS (SELECT 1 FROM wf_process_instance ins WHERE ins.businessNo = club.id AND ins.state = %s)" cnd.and(new Static("EXISTS (SELECT 1 FROM wf_process_instance ins WHERE ins.businessNo = club.id AND ins.state = %s)"
.formatted(ProcessInstanceStateEnum.FINISHED.getCode()))); .formatted(ProcessInstanceStateEnum.FINISHED.getCode())));
sql.setCondition(cnd); sql.setCondition(cnd);
@@ -112,7 +112,7 @@ public class SysClubServiceImpl extends BaseServiceImpl<SysClub> implements SysC
if (StrUtil.isBlank(clubId)) { if (StrUtil.isBlank(clubId)) {
return new ClubRegisterVo(); return new ClubRegisterVo();
} }
// 与社团列表保持一致,按社团成员角色汇总会长、秘书长姓名,避免多名任职人员导致详情记录重复。 // 与协会列表保持一致,按协会成员角色汇总会长、秘书长姓名,避免多名任职人员导致详情记录重复。
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
club.*, club.*,
@@ -225,12 +225,12 @@ public class SysClubServiceImpl extends BaseServiceImpl<SysClub> implements SysC
log.set("operatorUserId", SecurityUtil.getUserId()); log.set("operatorUserId", SecurityUtil.getUserId());
log.set("operatorUserName", SecurityUtil.getUserUsername()); log.set("operatorUserName", SecurityUtil.getUserUsername());
log.set("operatorTime", DateUtil.now()); log.set("operatorTime", DateUtil.now());
log.set("operatorType", "社团创建"); log.set("operatorType", "协会创建");
log.set("oldValue", null); log.set("oldValue", null);
log.set("newValue", false); log.set("newValue", false);
record.setOperateLogs(List.of(log)); record.setOperateLogs(List.of(log));
// 2026年10月3日 09:05:37;操作人:wxg:社团创建直接入会的成员同样保存缴费快照。 // 2026年10月3日 09:05:37;操作人:wxg:协会创建直接入会的成员同样保存缴费快照。
payRecordService.prepareSnapshots(List.of(record)); payRecordService.prepareSnapshots(List.of(record));
dao().insert(record); dao().insert(record);
} }
@@ -242,7 +242,7 @@ public class SysClubServiceImpl extends BaseServiceImpl<SysClub> implements SysC
@Override @Override
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
public SysClub doEdit(SysClub club, List<String> deleteIds, List<NutMap> managePerson) { public SysClub doEdit(SysClub club, List<String> deleteIds, List<NutMap> managePerson) {
// 修改社团表 // 修改协会表
dao().update(club); dao().update(club);
// 修改发起人表 // 修改发起人表
dao().clear(SysClubSponsor.class, Cnd.where("clubId", "=", club.getId())); dao().clear(SysClubSponsor.class, Cnd.where("clubId", "=", club.getId()));
@@ -264,7 +264,7 @@ public class SysClubServiceImpl extends BaseServiceImpl<SysClub> implements SysC
.filter(member -> java.util.Objects.equals(member.getUserId(), clubUser.getUserId())) .filter(member -> java.util.Objects.equals(member.getUserId(), clubUser.getUserId()))
.findFirst() .findFirst()
.ifPresent(member -> { .ifPresent(member -> {
// 2026年10月3日 12:01:28;操作人:wxg:编辑社团重建既有会员关系时保留原入会时间及人员资料。 // 2026年10月3日 12:01:28;操作人:wxg:编辑协会重建既有会员关系时保留原入会时间及人员资料。
clubUser.setJoinTime(member.getJoinTime()); clubUser.setJoinTime(member.getJoinTime());
clubUser.setUserName(member.getUserName()); clubUser.setUserName(member.getUserName());
clubUser.setLoginName(member.getLoginName()); clubUser.setLoginName(member.getLoginName());
@@ -323,7 +323,7 @@ public class SysClubServiceImpl extends BaseServiceImpl<SysClub> implements SysC
} }
cnd.andEX("year(info.createTime)", "=", pageForm.getYear()); cnd.andEX("year(info.createTime)", "=", pageForm.getYear());
cnd.and("info.userId", "=", SecurityUtil.getUserId()); cnd.and("info.userId", "=", SecurityUtil.getUserId());
// 我的注册列表默认按社团编码升序展示,点击表头时按用户选择的方向排序。 // 我的注册列表默认按协会编码升序展示,点击表头时按用户选择的方向排序。
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) { if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.asc("info.clubCode"); cnd.asc("info.clubCode");
} else if ("clubCode".equals(pageForm.getPageOrderName())) { } else if ("clubCode".equals(pageForm.getPageOrderName())) {
@@ -229,7 +229,7 @@ public class SysClubUserServiceImpl extends BaseServiceImpl<ClubUser> implements
@Override @Override
public void clubUser2Scope(String clubId, List<String> users) { public void clubUser2Scope(String clubId, List<String> users) {
// 添加社团成员,需要增加到品牌活动的组别去,wcnm // 添加协会成员,需要增加到品牌活动的组别去,wcnm
List<TrainSignUpActivity> list = dao().query(TrainSignUpActivity.class, Cnd.NEW()); List<TrainSignUpActivity> list = dao().query(TrainSignUpActivity.class, Cnd.NEW());
List<TrainSignUpActivity> activityList = new ArrayList<>(); List<TrainSignUpActivity> activityList = new ArrayList<>();
for (TrainSignUpActivity activity : list) { for (TrainSignUpActivity activity : list) {
@@ -17,9 +17,9 @@ public class ClubEvaluatePageVo {
private String foundTime; private String foundTime;
private String typeName; private String typeName;
private Date applyTime; private Date applyTime;
/** 社团评优申报开始年度。 */ /** 协会评优申报开始年度。 */
private String applyStartYear; private String applyStartYear;
/** 社团评优申报结束年度。 */ /** 协会评优申报结束年度。 */
private String applyEndYear; private String applyEndYear;
private String instanceId; private String instanceId;

Some files were not shown because too many files have changed in this diff Show More