From 3c7e80c4285e3d1b990caab85fed6c4ca15d8630 Mon Sep 17 00:00:00 2001 From: hongqiwei <1143662660@qq.com> Date: Wed, 4 Nov 2026 14:16:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=80=E6=9C=89=E5=8D=8F=E4=BC=9A=E3=80=81?= =?UTF-8?q?=E7=A4=BE=E5=9B=A2=E6=94=B9=E6=88=90=E4=BF=B1=E4=B9=90=E9=83=A8?= =?UTF-8?q?=EF=BC=9B=E6=AF=8F=E5=A4=A9=E5=AE=9A=E6=97=B6=E6=8B=89=E5=8F=96?= =?UTF-8?q?=E5=85=A8=E9=87=8F=E6=9B=B4=E6=96=B0=E4=BA=BA=E5=91=98=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=EF=BC=88=E5=9C=A8=E8=81=8C=E5=9C=A8=E5=B2=97=EF=BC=8C?= =?UTF-8?q?=E9=9D=9E=E4=B8=B4=E6=97=B6=E8=81=98=E7=94=A8=E4=BA=BA=E5=91=98?= =?UTF-8?q?=E8=AE=BE=E4=B8=BA=E4=BC=9A=E5=91=98=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/budwk/app/MainLauncher.java | 1 + .../app/base/config/UserSourceDatabase.java | 44 ++++ .../budwk/app/base/constant/RoleConstant.java | 22 +- .../base/event/role/RoleEventPublisher.java | 16 +- ...wClubPresidentByArgsAssignmentHandler.java | 2 +- .../impl/SysDataUnitPullServiceImpl.java | 15 +- .../impl/SysDataUserAllUpdateServiceImpl.java | 205 ++++++++---------- .../SysDataUserIncrUpdateServiceImpl.java | 57 +++-- .../impl/SysDataUserPullServiceImpl.java | 61 ++++-- .../impl/SysUserDailySyncService.java | 109 ++++++++++ .../app/task/job/SysUserIncrRenewJob.java | 38 ++++ .../ActivityDeclareMineController.java | 3 +- .../enums/ActivityDeclareReimbursement.java | 2 +- .../ActivityReimbursementBoardController.java | 8 +- .../ActivityReimbursementMineController.java | 3 +- .../apply/ClubUserJoinApplyController.java | 4 +- .../examine/ClubExamineApplyController.java | 10 +- .../examine/ClubExamineMineController.java | 2 +- .../infoManage/ClubInfoManageController.java | 28 ++- .../ClubRefreshReportController.java | 2 +- .../infoManage/ClubRuleUpdateController.java | 2 +- .../statistics/ClubStatisticsController.java | 6 +- .../budwk/app/zhgh/club/model/SysClub.java | 6 +- .../app/zhgh/club/model/SysClubEvaluate.java | 2 +- .../app/zhgh/club/model/SysClubSponsor.java | 2 +- .../impl/SysClubInfoManageServiceImpl.java | 2 +- .../controller/ArticleAnalysisController.java | 2 +- .../ArticleFirstExamineController.java | 2 +- .../caredata/UnionCareDataCon.java | 2 +- ...tivityBudgetQueryStatisticsController.java | 2 +- .../template/ActivityBudgetTemp.java | 2 +- .../service/impl/SiteCugApplyServiceImpl.java | 6 +- .../UnionReimburseApplyController.java | 2 +- .../resources/views/layouts/v4/home/user.js | 11 +- .../activity/culture/common/applyActivity.js | 6 +- .../activity/culture/common/infoActivity.js | 2 +- .../activity/culture/common/singleSignUp.js | 2 +- .../declare/apply/form.html | 6 +- .../declare/apply/index.html | 12 +- .../declare/apply/view.html | 2 +- .../declare/branchunion/index.html | 2 +- .../declare/clubprincipal/index.html | 6 +- .../declare/common/info.js | 2 +- .../declare/mine/index.html | 6 +- .../declare/schoolprincipal/index.html | 6 +- .../declare/schoolunion/index.html | 6 +- .../reimbursement/apply/index.html | 6 +- .../reimbursement/apply/view.html | 2 +- .../reimbursement/board/index.html | 12 +- .../reimbursement/branchunion/index.html | 2 +- .../reimbursement/clubprincipal/index.html | 6 +- .../reimbursement/common/info.js | 2 +- .../deputySchoolUnion/index.html | 6 +- .../reimbursement/mine/index.html | 6 +- .../reimbursement/schoolFinance/index.html | 6 +- .../reimbursement/schoolprincipal/index.html | 6 +- .../reimbursement/schoolunion/index.html | 6 +- .../planSummary/yearPlan/basicForm.js | 6 +- .../activity/planSummary/yearPlan/index.html | 6 +- .../activity/planSummary/yearPlan/info.js | 2 +- .../planSummary/yearSummary/basicForm.js | 6 +- .../planSummary/yearSummary/index.html | 6 +- .../activity/planSummary/yearSummary/info.js | 2 +- .../activity/sports/infoManage/addActivity.js | 8 +- .../zhgh/club/common/clubInfoComponent.js | 10 +- .../zhgh/club/common/clubRoleConstant.js | 4 +- .../platform/zhgh/club/common/clubUserJoin.js | 2 +- .../zhgh/club/evaluate/apply/index.html | 12 +- .../zhgh/club/evaluate/clubAudit/index.html | 12 +- .../zhgh/club/evaluate/common/evaluateInfo.js | 6 +- .../zhgh/club/evaluate/mine/index.html | 12 +- .../zhgh/club/evaluate/schoolAudit/index.html | 12 +- .../evaluate/schoolLeaderAudit/index.html | 12 +- .../zhgh/club/examine/apply/index.html | 6 +- .../zhgh/club/examine/clubAudit/index.html | 6 +- .../zhgh/club/examine/common/examineInfo.js | 8 +- .../zhgh/club/examine/mine/index.html | 6 +- .../zhgh/club/examine/schoolAudit/index.html | 6 +- .../club/examine/schoolLeaderAudit/index.html | 6 +- .../club/infoManage/auditManager/index.html | 6 +- .../zhgh/club/infoManage/change/index.html | 26 +-- .../zhgh/club/infoManage/change/info.js | 2 +- .../club/infoManage/manage/clubInfoManage.js | 12 +- .../infoManage/manage/schoolClubManage.js | 12 +- .../club/infoManage/refreshReport/index.html | 10 +- .../club/infoManage/refreshReport/info.js | 2 +- .../club/infoManage/ruleUpdate/index.html | 12 +- .../zhgh/club/infoManage/ruleUpdate/info.js | 6 +- .../infoManage/schoolAuditReport/index.html | 6 +- .../infoManage/schoolAuditRule/index.html | 6 +- .../platform/zhgh/club/join/apply/index.html | 16 +- .../zhgh/club/join/clubApproval/index.html | 2 +- .../platform/zhgh/club/join/list/index.html | 28 +-- .../platform/zhgh/club/join/mine/index.html | 2 +- .../zhgh/club/join/mineclub/index.html | 28 +-- .../club/join/schoolUnionApproval/index.html | 2 +- .../club/register/apply/clubRegisterForm.js | 24 +- .../register/apply/clubRegisterManager.js | 2 +- .../zhgh/club/register/apply/index.html | 10 +- .../zhgh/club/register/clubConfirm/index.html | 8 +- .../zhgh/club/register/mine/index.html | 14 +- .../register/schoolLeaderAudit/index.html | 8 +- .../zhgh/club/register/schoolReply/index.html | 8 +- .../platform/zhgh/club/statistics/index.html | 12 +- .../article/firstExamine/index.html | 4 +- .../dayofficework/article/mine/index.html | 2 +- .../dayofficework/article/query/index.html | 6 +- .../dayofficework/article/write/index.html | 2 +- .../dayofficework/careData/staff/index.html | 2 +- .../careData/unionCare/index.html | 12 +- .../activityBudget/allocationEdit/index.html | 6 +- .../activityBudget/applyStatistics/index.html | 4 +- .../activityBudget/queryStatistics/index.html | 2 +- .../activityBudget/schoolAudit/index.html | 2 +- .../activityBudget/schoolPrincipal/index.html | 2 +- .../outlayManage/club/manage/index.html | 2 +- .../outlayManage/club/useDetail/index.html | 8 +- .../outlay/outlayReimburse/apply/index.html | 6 +- .../zhgh/dayofficework/siteCug/apply/apply.js | 18 +- .../dayofficework/siteCug/common/applyInfo.js | 2 +- .../dayofficework/siteCug/record/index.html | 2 +- .../unionReimburse/apply/index.html | 24 +- .../zhgh/dayofficework/unionReimburse/info.js | 4 +- .../zhghh5/activity/culture/applyUser.html | 2 +- .../platform/zhghh5/club/apply/index.html | 18 +- .../zhghh5/club/clubApproval/index.html | 2 +- .../zhghh5/club/common/clubUserJoin.js | 4 +- .../outlayReimburse/apply/index.html | 4 +- .../outlayReimburse/clubAudit/index.html | 2 +- .../outlayReimburse/clubZxAudit/index.html | 2 +- .../dayofficework/siteCug/apply/form/apply.js | 10 +- .../siteCug/apply/form/index.html | 6 +- .../dayofficework/siteCug/common/applyInfo.js | 2 +- .../dayofficework/siteCug/mine/index.html | 2 +- .../siteCug/schoolUnionAudit/index.html | 2 +- .../unionReimburse/apply/index.html | 33 +-- .../dayofficework/unionReimburse/info.js | 4 +- 137 files changed, 843 insertions(+), 579 deletions(-) create mode 100644 src/main/java/com/budwk/app/base/config/UserSourceDatabase.java create mode 100644 src/main/java/com/budwk/app/sys/services/impl/SysUserDailySyncService.java create mode 100644 src/main/java/com/budwk/app/task/job/SysUserIncrRenewJob.java diff --git a/src/main/java/com/budwk/app/MainLauncher.java b/src/main/java/com/budwk/app/MainLauncher.java index 07e198c..9ca39e1 100644 --- a/src/main/java/com/budwk/app/MainLauncher.java +++ b/src/main/java/com/budwk/app/MainLauncher.java @@ -86,6 +86,7 @@ public class MainLauncher { ioc.get(GlobalMessageSendService.class); AppIocUtil.setIoc(ioc); + ioc.get(com.budwk.app.sys.services.impl.SysUserDailySyncService.class).register(); } public void init_auth() { diff --git a/src/main/java/com/budwk/app/base/config/UserSourceDatabase.java b/src/main/java/com/budwk/app/base/config/UserSourceDatabase.java new file mode 100644 index 0000000..b22b6de --- /dev/null +++ b/src/main/java/com/budwk/app/base/config/UserSourceDatabase.java @@ -0,0 +1,44 @@ +package com.budwk.app.base.config; + +import com.alibaba.druid.pool.DruidDataSource; +import org.nutz.dao.Dao; +import org.nutz.dao.impl.NutDao; +import org.nutz.ioc.impl.PropertiesProxy; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** 推送库独立连接池,仅供人员、单位中间表查询,不能代替系统默认 DAO。 */ +@IocBean(create = "init", depose = "close") +public class UserSourceDatabase { + @Inject + private PropertiesProxy conf; + private DruidDataSource dataSource; + private Dao dao; + + /** 根据 user-source.jdbc 配置初始化来源连接;连接按需建立,使用只读模式。 */ + public void init() { + dataSource = new DruidDataSource(); + dataSource.setUrl(conf.get("user-source.jdbc.url")); + dataSource.setUsername(conf.get("user-source.jdbc.username")); + dataSource.setPassword(conf.get("user-source.jdbc.password")); + dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); + dataSource.setDefaultReadOnly(true); + dataSource.setMaxActive(3); + dataSource.setMaxWait(10000); + dataSource.setValidationQuery("select 1"); + dataSource.setTestWhileIdle(true); + dao = new NutDao(dataSource); + } + + /** 返回来源库 DAO;调用方只能查询中间表,业务写入继续使用默认 DAO。 */ + public Dao dao() { + return dao; + } + + /** 应用退出时释放来源库连接。 */ + public void close() { + if (dataSource != null) { + dataSource.close(); + } + } +} diff --git a/src/main/java/com/budwk/app/base/constant/RoleConstant.java b/src/main/java/com/budwk/app/base/constant/RoleConstant.java index c5f8ef0..f624f3d 100644 --- a/src/main/java/com/budwk/app/base/constant/RoleConstant.java +++ b/src/main/java/com/budwk/app/base/constant/RoleConstant.java @@ -26,9 +26,9 @@ RoleConstant { SCHOOL_UNION_DC_ADMIN("校工会民主管理员"), SCHOOL_UNION_PROPOSAL_ADMIN("校工会提案管理员"), APPEAL_COMMITTEE_MEMBER("申诉处理委员会成员"), - SCHOOL_UNION_CLUB_ADMIN("校工会协会管理员"), + SCHOOL_UNION_CLUB_ADMIN("校工会俱乐部管理员"), SCHOOL_UNION_ACTIVITY_ADMIN("校工会活动管理员"), - SCHOOL_UNION_ARTICLE_CLUB_ADMIN("校工会协会新闻审批管理员"), + SCHOOL_UNION_ARTICLE_CLUB_ADMIN("校工会俱乐部新闻审批管理员"), SCHOOL_UNION_ARTICLE_FGH_ADMIN("校工会分工会新闻审批管理员"), SCHOOL_UNION_ARTICLE_ADMIN("校工会新闻审批管理员"), SCHOOL_OUTLAY_ADMIN("校工会经费管理员"), @@ -80,15 +80,15 @@ RoleConstant { SCHOOL_HOSPITAL_LEADER("校医院负责人"), /*协会模块*/ - CLUB_MEMBER("协会会员"), - CLUB_MANAGER("协会负责人"), - CLUB_PRESIDENT("协会会长"), - CLUB_VICE_PRESIDENT("协会副会长"), - CLUB_SECRETARY("协会秘书长"), - CLUB_VICE_SECRETARY("协会副秘书长"), + CLUB_MEMBER("俱乐部会员"), + CLUB_MANAGER("俱乐部负责人"), + CLUB_PRESIDENT("俱乐部会长"), + CLUB_VICE_PRESIDENT("俱乐部副会长"), + CLUB_SECRETARY("俱乐部秘书长"), + CLUB_VICE_SECRETARY("俱乐部副秘书长"), // SCHOOL_UNION_CLUB_ADMIN("校工会协会管理员"), - CLUB_AUDIT_LEADER("协会分管领导"), - CLUB_OPERATOR("协会操作员"), + CLUB_AUDIT_LEADER("俱乐部分管领导"), + CLUB_OPERATOR("俱乐部操作员"), /*大病基金会员*/ AID_FUND_MEMBER("大病基金会员"), @@ -102,7 +102,7 @@ RoleConstant { LEGAL_DOCTOR("法律顾问"), UNION_REIMBURSEMENT_MANAGER("分工会报销负责人"), - CLUB_REIMBURSEMENT_MANAGER("协会报销负责人"), + CLUB_REIMBURSEMENT_MANAGER("俱乐部报销负责人"), SCHOOL_REIMBURSEMENT_MANAGER("校工会报销负责人"), ; diff --git a/src/main/java/com/budwk/app/base/event/role/RoleEventPublisher.java b/src/main/java/com/budwk/app/base/event/role/RoleEventPublisher.java index 59ae31a..abb03f0 100644 --- a/src/main/java/com/budwk/app/base/event/role/RoleEventPublisher.java +++ b/src/main/java/com/budwk/app/base/event/role/RoleEventPublisher.java @@ -1,6 +1,7 @@ package com.budwk.app.base.event.role; -import org.nutz.mvc.Mvcs; +import com.budwk.app.base.utils.AppIocUtil; +import org.nutz.ioc.Ioc; /** * @version 1.0 @@ -11,10 +12,19 @@ import org.nutz.mvc.Mvcs; */ public class RoleEventPublisher { + /** + * 同步发布角色变更,兼容 HTTP 请求与 Quartz 后台线程。 + * 无返回值;监听器异常继续向调用方抛出,使外层业务事务可以回滚。 + * + * @param event 角色事件:roleCode 为角色编码,userIds 或 unitId 指定人员范围, + * operationType 为 ADD_ROLE、REMOVE_ROLE 或 RENEW_ROLE + */ public static void broadcast(RoleEventMsg event){ - String[] names = Mvcs.getIoc().getNamesByType(RoleEventListener.class); + // 使用应用启动时保存的全局容器,避免后台线程依赖 HTTP 请求上下文。 + Ioc ioc = AppIocUtil.get(); + String[] names = ioc.getNamesByType(RoleEventListener.class); for (String name : names) { - RoleEventListener listener = Mvcs.getIoc().get(RoleEventListener.class, name); + RoleEventListener listener = ioc.get(RoleEventListener.class, name); listener.receive(event); } } diff --git a/src/main/java/com/budwk/app/flow/handler/FlowClubPresidentByArgsAssignmentHandler.java b/src/main/java/com/budwk/app/flow/handler/FlowClubPresidentByArgsAssignmentHandler.java index 052bf80..e987b93 100644 --- a/src/main/java/com/budwk/app/flow/handler/FlowClubPresidentByArgsAssignmentHandler.java +++ b/src/main/java/com/budwk/app/flow/handler/FlowClubPresidentByArgsAssignmentHandler.java @@ -45,7 +45,7 @@ public class FlowClubPresidentByArgsAssignmentHandler implements AssignmentHandl @Override public String getMessage() { - return "获取协会负责人(根据args中的clubId)"; + return "获取俱乐部负责人(根据args中的clubId)"; } @Override diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysDataUnitPullServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysDataUnitPullServiceImpl.java index 48ae3b7..ce7b786 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysDataUnitPullServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysDataUnitPullServiceImpl.java @@ -41,6 +41,9 @@ import java.util.stream.Collectors; @IocBean(args = {"refer:dao"}) public class SysDataUnitPullServiceImpl extends BaseServiceImpl implements SysDataUnitPullService { + @Inject + private com.budwk.app.base.config.UserSourceDatabase userSourceDatabase; + @Inject private DataCenterProperties dcPro; @@ -66,7 +69,7 @@ public class SysDataUnitPullServiceImpl extends BaseServiceImpl implem @Aop(TransAop.READ_COMMITTED) public void updateUnits(List unitIds) { // 中间表数据 - List unitMiddleList = dao().query("unit_middle", Cnd.NEW()); + List unitMiddleList = userSourceDatabase.dao().query("unit_middle", Cnd.NEW()); List sysUnits = dao().query(Sys_unit.class, Cnd.NEW()); Map unitMapById = sysUnits.stream() .filter(unit -> StrUtil.isNotBlank(unit.getId())) @@ -84,7 +87,14 @@ public class SysDataUnitPullServiceImpl extends BaseServiceImpl implem Map api2db = Arrays.stream(Api2UnitFiledMap.values()) .collect(Collectors.toMap(e -> e.apiField, e -> e.dbColumn)); for (Record row : unitMiddleList) { - JSONObject jsonRow = JSONUtil.parseObj(JSONUtil.toJsonStr(row)); + JSONObject jsonRow = new JSONObject(); + // 用映射中的原始键构建 JSON,避免 Record 转 JSON 后列名变小写导致取值失败。 + for (String key : api2db.keySet()) { + jsonRow.set(key, row.get(key)); + } + jsonRow.set("dwdm", row.get("dwdm")); + jsonRow.set("dwdj", row.get("dwdj")); + jsonRow.set("sjdwmcdm", row.get("sjdwmcdm")); String unitCode = getDataCenterField(jsonRow, "dwdm"); String unitLevel = getDataCenterField(jsonRow, "dwdj"); if (StrUtil.isBlank(unitCode)) { @@ -135,6 +145,7 @@ public class SysDataUnitPullServiceImpl extends BaseServiceImpl implem * * @param updateList 需要更新的单位数据 */ + @Aop(TransAop.READ_COMMITTED) private void updateDataCenterUnitFields(List updateList) { for (Sys_unit unit : updateList) { Chain chain = Chain.make("unitcode", unit.getUnitcode()); diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysDataUserAllUpdateServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysDataUserAllUpdateServiceImpl.java index efbda11..71c530b 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysDataUserAllUpdateServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysDataUserAllUpdateServiceImpl.java @@ -2,7 +2,6 @@ package com.budwk.app.sys.services.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.date.DateField; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -13,6 +12,8 @@ import com.budwk.app.base.event.role.RoleEventPublisher; import com.budwk.app.base.event.user.UserChangeMsg; import com.budwk.app.base.event.user.UserChangePublisher; import com.budwk.app.base.utils.PwdUtil; +import com.budwk.app.base.exception.BaseException; +import com.budwk.app.base.utils.ConditionGroupUtil; import com.budwk.app.sys.annotation.DataCenterColumn; import com.budwk.app.sys.models.*; import com.budwk.app.sys.param.SysDataUserUpdateParam; @@ -128,7 +129,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService if (dataCenterUpdateFields == null) { dataCenterUpdateFields = getFieldMappings().stream() .map(mapping -> mapping.field.getName()) - .collect(Collectors.joining("|", "^(id|", ")$")); + .collect(Collectors.joining("|", "^(id|member|", ")$")); } } } @@ -136,31 +137,16 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService } /** - * 检查用户是否符合会员条件 - * - * @param userState 用户状态 - * @param preparedBy 编制类别 - * @param postDoctoralJoinDate 博士后进站时间 - * @return 是否符合会员条件 + * 判断本次来源记录是否可自动设为会员。 + * @param userState 在职状态,接受“在职”或“在岗”,忽略首尾空格 + * @param personType 人员类型,必须非空且不是“临时聘用人员” + * @return true 表示授予会员资格;false 表示取消会员资格(包括状态或人员类型为空) */ - private boolean checkMembershipEligibility(String userState, String preparedBy, Date postDoctoralJoinDate) { - // 博士后单独判断:只要进站时间在两年内就是会员 - if ("博士后".equals(preparedBy)) { - if (postDoctoralJoinDate != null) { - Date twoYearsAgo = DateUtil.offset(DateUtil.date(), DateField.YEAR, -2).toJdkDate(); - return postDoctoralJoinDate.after(twoYearsAgo); - } - return false; - } - - // 其他人员需要判断在岗状态和聘用方式 - if (!"在岗".equals(userState)) { - return false; - } - - // 判断聘用方式 - Set memberPreparedByTypes = new HashSet<>(Arrays.asList("新人事代理", "校聘合同制", "事业编制")); - return memberPreparedByTypes.contains(preparedBy); + private boolean checkMembershipEligibility(String userState, String personType) { + String state = StrUtil.trimToNull(userState); + String type = StrUtil.trimToNull(personType); + return ("在职".equals(state) || "在岗".equals(state)) + && type != null && !"临时聘用人员".equals(type); } /** @@ -172,6 +158,9 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService @Override @Aop(TransAop.READ_COMMITTED) public String update(SysDataUserUpdateParam updateParam) { + if (updateParam == null || StrUtil.isBlank(updateParam.getPullTime())) { + throw new BaseException("全量更新必须指定拉取批次"); + } long startTime = System.currentTimeMillis(); log.info("开始全量更新用户数据"); @@ -179,6 +168,10 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService Sys_role publicRole = sysRoleService.getByCode(RoleConstant.PUBLIC); Sys_role memberRole = sysRoleService.getByCode(RoleConstant.MEMBER); + if (publicRole == null || memberRole == null) { + throw new BaseException("公共角色或会员角色不存在,已停止全量更新"); + } + // 获取单位信息,后续判断单位变更 List unitList = dao.query(Sys_unit.class, Cnd.where("unitTypeCode", "=", "1")); unitIdNameMap = unitList.stream().collect(Collectors.toMap(Sys_unit::getId, Sys_unit::getName)); @@ -188,30 +181,59 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService // 处理复杂条件 if (updateParam.getConditionGroup() != null) { -// cnd = ConditionGroupUtil.applyConditionGroup(cnd, updateParam.getConditionGroup()); + cnd = ConditionGroupUtil.applyConditionGroup(cnd, updateParam.getConditionGroup()); } - List sources = dao.query(Sys_user_source.class, cnd.groupBy("loginname")); + List sources = dao.query(Sys_user_source.class, cnd); + if (sources.isEmpty() || sources.stream().anyMatch(v -> StrUtil.isBlank(v.getLoginname()) || StrUtil.isBlank(v.getUsername()))) { + throw new BaseException("当前批次为空或包含空工号、空姓名,不能执行全量更新"); + } + // 不使用 SELECT * GROUP BY,兼容 MySQL 严格模式;重复工号中止,避免随意覆盖。 + if (sources.stream().map(Sys_user::getLoginname).distinct().count() != sources.size()) { + throw new BaseException("当前批次存在重复工号,已停止全量更新"); + } log.info("符合条件的数据源记录数: {}", sources.size()); // 查询系统用户 - List sysUsers = dao.query(Sys_user.class, Cnd.NEW().groupBy("loginname")); - Map userMap = sysUsers.stream().collect(Collectors.toMap(Sys_user::getLoginname, sysUser -> sysUser)); + List sysUsers = dao.query(Sys_user.class, Cnd.NEW()); + Map userMap = new HashMap<>(); + for (Sys_user user : sysUsers) { + if (StrUtil.isNotBlank(user.getLoginname()) && userMap.putIfAbsent(user.getLoginname(), user) != null) { + throw new BaseException("系统存在重复工号,已停止全量更新"); + } + } // 准备数据集合 - List needDoUpdateList = new CopyOnWriteArrayList<>(); - List needInitUserList = new CopyOnWriteArrayList<>(); - List histories = new CopyOnWriteArrayList<>(); + List needDoUpdateList = new ArrayList<>(); + List needInitUserList = new ArrayList<>(); + List histories = new ArrayList<>(); + int newMemberCount = 0; + int withdrawnMemberCount = 0; + List nonMemberUserIds = new ArrayList<>(); // 处理每条数据 for (Sys_user_source source : sources) { Sys_user user = userMap.get(source.getLoginname()); - // 创建只包含数据中心字段的用户对象,避免覆盖本地维护的会员、登录、积分等业务字段。 + // 仅更新数据中心字段和明确授权同步的会员标记,保留登录、积分等本地字段。 Sys_user u = buildDataCenterUser(source); + boolean eligible = checkMembershipEligibility(source.getUserState(), source.getPersonType()); + // 本批次人员严格按来源条件设置会员;不符合条件时同步撤销会员角色。 + u.setMember(eligible); + if (eligible && (user == null || !Boolean.TRUE.equals(user.getMember()))) { + newMemberCount++; + } + if (!eligible && user != null) { + nonMemberUserIds.add(user.getId()); + if (Boolean.TRUE.equals(user.getMember())) { + withdrawnMemberCount++; + } + } + source.setMember(u.getMember()); if (user == null) { // 新增用户,初始化数据 + u.setId(R.UU32()); String salt = R.UU32(); u.setSalt(salt); u.setPassword(PwdUtil.getPassword(PwdUtil.generate(12), salt)); @@ -235,58 +257,18 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService } } - // 使用CompletableFuture处理并行任务 - List> updateTasks = new ArrayList<>(); - - // 1. 新增用户 - 使用批量处理 - if (Lang.isNotEmpty(needInitUserList)) { - CompletableFuture insertTask = CompletableFuture.runAsync(() -> { - log.info("新增用户: {} 个", needInitUserList.size()); - // 分批处理,每批200条 - List> batches = ListUtil.split(needInitUserList, 500); - batches.forEach(batch -> { - try { - dao.fastInsert(batch); - } catch (Exception e) { - log.error("批量新增用户异常", e); - } - }); - }, executorService); - updateTasks.add(insertTask); + // 人员、角色、历史在当前事务线程同步写入,任一失败抛出异常并回滚。 + log.info("全量批次 {}:新增 {} 人,更新 {} 人,新设会员 {} 人,取消会员 {} 人", updateParam.getPullTime(), + needInitUserList.size(), needDoUpdateList.size(), newMemberCount, withdrawnMemberCount); + for (List batch : ListUtil.split(needInitUserList, 500)) { + dao.fastInsert(batch); + } + Dao extDao = Daos.ext(dao, FieldFilter.create(Sys_user.class, getDataCenterUpdateFields())); + for (List batch : ListUtil.split(needDoUpdateList, 500)) { + extDao.updateIgnoreNull(batch); } - // 2. 更新现有用户 - 使用批量处理 - if (Lang.isNotEmpty(needDoUpdateList)) { - CompletableFuture updateTask = CompletableFuture.runAsync(() -> { - log.info("更新用户: {} 个", needDoUpdateList.size()); - // 分批处理,每批200条 - List> batches = ListUtil.split(needDoUpdateList, 500); - batches.forEach(batch -> { - try { - Dao extDao = Daos.ext(dao, FieldFilter.create(Sys_user.class, getDataCenterUpdateFields())); - extDao.updateIgnoreNull(batch); - } catch (Exception e) { - log.error("批量更新用户异常", e); - } - }); - }, executorService); - updateTasks.add(updateTask); - } - - // 等待用户数据更新完成 - try { - // 设置超时时间,避免无限等待 - CompletableFuture.allOf(updateTasks.toArray(new CompletableFuture[0])) - .get(5, TimeUnit.MINUTES); - } catch (TimeoutException e) { - log.warn("更新用户数据超时"); - return "更新超时,请检查数据处理情况"; - } catch (Exception e) { - log.error("更新用户数据异常", e); - return "更新失败: " + e.getMessage(); - } - - // 3. 全量同步不变更会员状态,只按当前用户会员标记补齐基础角色。 + // 根据更新后的会员标记补齐基础角色,不移除其他已有权限。 List roleUsers = dao.query(Sys_user.class, Cnd.NEW()); List publicRoleUserIds = roleUsers.stream().map(Sys_user::getId).toList(); List memberRoleUserIds = roleUsers.stream() @@ -295,36 +277,37 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService .toList(); int addPublicRoleCount = addMissingUserRole(publicRole.getId(), publicRoleUserIds, "公共角色"); int addMemberRoleCount = addMissingUserRole(memberRole.getId(), memberRoleUserIds, "会员角色"); + // 仅删除本次来源中不符合条件人员的 MEMBER 关系,包括历史残留的会员角色。 + // 明确限制 roleId,公共角色和其他业务角色不受本次退会操作影响。 + int removedMemberRoleCount = 0; + for (List batch : ListUtil.split(nonMemberUserIds, 500)) { + removedMemberRoleCount += dao.clear(Sys_user_role.class, + Cnd.where(Sys_user_role::getRoleId, "=", memberRole.getId()) + .and(Sys_user_role::getUserId, "in", batch)); + } + log.info("取消会员 {} 人,移除会员角色关系 {} 条", withdrawnMemberCount, removedMemberRoleCount); sysRoleService.clearCache(); - // 4. 异步添加历史记录 - 不等待完成 - if (Lang.isNotEmpty(histories)) { - executorService.execute(() -> { - log.info("添加历史记录: {} 条", histories.size()); - // 分批处理历史记录 - List> batches = ListUtil.split(histories, 500); - batches.forEach(batch -> { - try { - dao.fastInsert(batch); - } catch (Exception e) { - log.error("批量添加历史记录异常", e); - } - }); - }); + // 历史记录必须随业务事务完成,不能在任务报告成功后异步写入。 + for (List batch : ListUtil.split(histories, 500)) { + dao.fastInsert(batch); } // 5. 处理其他任务 // 5.1 更新提案的校领导角色,发送订阅 ProposalConfig config = dao.fetch(ProposalConfig.class, Cnd.where("delFlag", "=", false).desc("updatedAt")); - for (String unitId : config.getSchoolLeaderUnitIds()) { - RoleEventPublisher.broadcast(new RoleEventMsg(unitId, RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name(), RoleEventMsg.RENEW_ROLE)); + if (config != null && config.getSchoolLeaderUnitIds() != null) { + for (String unitId : config.getSchoolLeaderUnitIds()) { + RoleEventPublisher.broadcast(new RoleEventMsg(unitId, RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name(), RoleEventMsg.RENEW_ROLE)); + } } long endTime = System.currentTimeMillis(); log.info("全量更新用户数据完成,耗时: {} 毫秒", (endTime - startTime)); return "更新完成: 新增用户 " + needInitUserList.size() + " 个, 更新用户 " + needDoUpdateList.size() - + " 个, 补齐公共角色 " + addPublicRoleCount + " 个, 补齐会员角色 " + addMemberRoleCount + " 个"; + + " 个, 新设会员 " + newMemberCount + " 个, 取消会员 " + withdrawnMemberCount + + " 个, 移除会员角色 " + removedMemberRoleCount + " 条, 补齐公共角色 " + addPublicRoleCount + " 个, 补齐会员角色 " + addMemberRoleCount + " 个"; } /** @@ -335,6 +318,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService * @param roleName 日志中的角色名称 * @return 本次新增的角色关系数量 */ + @Aop(TransAop.READ_COMMITTED) private int addMissingUserRole(String roleId, List userIds, String roleName) { List distinctUserIds = userIds.stream() .filter(Objects::nonNull) @@ -443,6 +427,7 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService * @param user 系统用户 * @return 历史记录 */ + @Aop(TransAop.READ_COMMITTED) private Sys_user_history createHistory(Sys_user_source source, Sys_user user) { List changeTypes = new ArrayList<>(); List changeList = new ArrayList<>(); @@ -494,15 +479,15 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService changeTypes.add(MemberChangeType.BASIC_CHANGE.name()); } - // 特殊字段变更处理 - // 1. 会员状态变更 -// if (!ObjectUtil.equals(user.getMember(), source.getMember())) { -// if (source.getMember()) { -// changeTypes.add(MemberChangeType.RESTORE.name()); -// } else { -// changeTypes.add(MemberChangeType.WITHDRAWAL.name()); -// } -// } + // 会员标记实际发生变化时记录入会或退会,重复同步不重复生成会员变更历史。 + boolean oldMember = Boolean.TRUE.equals(user.getMember()); + boolean newMember = Boolean.TRUE.equals(source.getMember()); + if (oldMember != newMember) { + changeTypes.add(newMember ? MemberChangeType.RESTORE.name() : MemberChangeType.WITHDRAWAL.name()); + changeList.add(NutMap.NEW().addv("fieldName", "会员状态").addv("field", "member") + .addv("sourceValue", oldMember ? "会员" : "非会员") + .addv("newValue", newMember ? "会员" : "非会员")); + } // 2. 单位变更 String oldUnitId = normalizeSecondLevelUnitId(user.getUnitId()); diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysDataUserIncrUpdateServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysDataUserIncrUpdateServiceImpl.java index 78b3469..fe94c25 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysDataUserIncrUpdateServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysDataUserIncrUpdateServiceImpl.java @@ -2,8 +2,6 @@ package com.budwk.app.sys.services.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.thread.AsyncUtil; -import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.event.role.RoleEventMsg; @@ -15,10 +13,12 @@ import com.budwk.app.sys.param.SysDataUserUpdateParam; import com.budwk.app.sys.services.SysDataUserUpdateService; import com.budwk.app.sys.services.SysRoleService; import com.budwk.app.zhgh.democratic.proposal.models.ProposalConfig; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.ioc.aop.Aop; +import com.budwk.app.base.exception.BaseException; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.dao.Sqls; -import org.nutz.dao.sql.Sql; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.random.R; @@ -27,7 +27,6 @@ import java.util.List; import java.util.Arrays; import java.util.Date; import java.util.Calendar; -import java.util.concurrent.CompletableFuture; /** * 增量更新系统用户数据 @@ -40,21 +39,38 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic @Inject private SysRoleService sysRoleService; + /** + * 按 pullTime(yyyy-MM-dd HH:mm:ss)批次新增工号,conditionGroup 为可选筛选条件。 + * 返回新增人员、单位、会员数量的说明字符串;所有业务写入在同一事务线程执行。 + */ @Override + @Aop(TransAop.READ_COMMITTED) public String update(SysDataUserUpdateParam updateParam) { - // 基本SQL查询 + if (updateParam == null || StrUtil.isBlank(updateParam.getPullTime())) { + throw new BaseException("拉取批次不能为空"); + } + // 空工号不参与新增,系统中的空值也不能干扰 NOT IN 判断。 Cnd cnd = Cnd.where("pullTime", "=", updateParam.getPullTime()) - .and("loginname", "NOT IN", Sqls.create("SELECT loginname FROM sys_user")); + .and("loginname", "NOT IN", Sqls.create("SELECT loginname FROM sys_user WHERE loginname IS NOT NULL")); // 处理复杂条件 if (updateParam.getConditionGroup() != null) { cnd = ConditionGroupUtil.applyConditionGroup(cnd, updateParam.getConditionGroup()); } - cnd.groupBy("loginname"); + cnd.and("loginname", "IS NOT", null).and("loginname", "!=", ""); // 执行查询 - List userSources = dao.query(Sys_user_source.class, cnd); + List userSources = new java.util.ArrayList<>(dao.query(Sys_user_source.class, cnd).stream() + .collect(java.util.stream.Collectors.toMap(Sys_user_source::getLoginname, + v -> v, (first, duplicate) -> first, java.util.LinkedHashMap::new)).values()); + if (userSources.isEmpty()) { + return "增量更新成功,本次无新增人员。"; + } + Sys_role publicRole = sysRoleService.getByCode(RoleConstant.PUBLIC); + if (publicRole == null) { + throw new BaseException("公共角色不存在,已停止新增人员"); + } //加入到系统用户表 List sysUsers = BeanUtil.copyToList(userSources, Sys_user.class); @@ -98,11 +114,10 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic } List> splitSysUsers = ListUtil.split(sysUsers, 500); - List>> splitSysUsersFutures = splitSysUsers.stream().map(v -> CompletableFuture.supplyAsync(() -> dao.fastInsert(v))).toList(); - //等待任务全部完成 - AsyncUtil.waitAll(CompletableFuture.allOf(splitSysUsersFutures.toArray(new CompletableFuture[0]))); - - Sys_role publicRole = sysRoleService.getByCode(RoleConstant.PUBLIC); + // 分批同步写入,避免异步线程脱离当前事务造成部分成功。 + for (List batch : splitSysUsers) { + dao.fastInsert(batch); + } //添加公共角色 List sysUserRoles = sysUsers.stream().map(user -> { @@ -111,13 +126,11 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic sysUserRole.setRoleId(publicRole.getId()); return sysUserRole; }).toList(); - ThreadUtil.execAsync(() -> { - dao.insert(sysUserRoles); - }); + dao.insert(sysUserRoles); sysRoleService.clearCache(); //比较单位数据 - List sysUnits = dao.query(Sys_unit.class, Cnd.where(Sys_unit::getUnitLevel, "=", 2)); + List sysUnits = dao.query(Sys_unit.class, Cnd.NEW()); List sysUnitIds = sysUnits.stream().map(Sys_unit::getId).toList(); //信息中心的数据 转为单位代码 ->单位名称 map @@ -139,17 +152,17 @@ public class SysDataUserIncrUpdateServiceImpl implements SysDataUserUpdateServic sysUnit.setUnitLevel(2); return sysUnit; }).toList(); - ThreadUtil.execAsync(() -> { - dao.insert(insertUnits); - }); + dao.insert(insertUnits); // 计算设置为会员的人数 long memberCount = sysUsers.stream().filter(Sys_user::getMember).count(); // 6.1 更新提案的校领导角色,发送订阅 ProposalConfig config = dao.fetch(ProposalConfig.class, Cnd.where("delFlag", "=", false).desc("updatedAt")); - for (String unitId : config.getSchoolLeaderUnitIds()) { - RoleEventPublisher.broadcast(new RoleEventMsg(unitId, RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name(), RoleEventMsg.RENEW_ROLE)); + if (config != null && config.getSchoolLeaderUnitIds() != null) { + for (String unitId : config.getSchoolLeaderUnitIds()) { + RoleEventPublisher.broadcast(new RoleEventMsg(unitId, RoleConstant.PROPOSAL_BRANCH_SCHOOL_LEADER.name(), RoleEventMsg.RENEW_ROLE)); + } } return StrUtil.format("增量更新成功,本次更新新入职老师{}人,新增单位{}个,设置为会员{}人。", sysUsers.size(), insertUnits.size(), memberCount); diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysDataUserPullServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysDataUserPullServiceImpl.java index 7a583ca..18bd787 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysDataUserPullServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysDataUserPullServiceImpl.java @@ -49,6 +49,9 @@ import java.util.stream.Collectors; @Slf4j public class SysDataUserPullServiceImpl extends BaseServiceImpl implements SysDataUserPullService { + @Inject + private com.budwk.app.base.config.UserSourceDatabase userSourceDatabase; + @Inject private SysDictService sysDictService; @Inject @@ -110,42 +113,43 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl public Date pull() { try { // 中间表数据 - List userMiddleList = sysDataUnitPullService.listMap(Sqls.create("select * from user_middle")); + List userMiddleList = userSourceDatabase.dao().query("user_middle", Cnd.NEW()); - List rawDataList = new ArrayList<>(); - if (!userMiddleList.isEmpty()) { - // 先收集所有原始数据 - rawDataList = userMiddleList.stream().map(v -> JSONUtil.parseObj(JSONUtil.toJsonStr(v))).toList(); - log.info("数据拉取进度: {}/{}", rawDataList.size(), userMiddleList.size()); - } else { - log.warn("当前未获取到数据"); + if (userMiddleList.isEmpty()) { + throw new BaseException("来源人员中间表为空,本次同步已停止"); } + log.info("来源人员记录数:{}", userMiddleList.size()); - Date nowDate = new Date(); + // 与 DATETIME 批次精度保持一致,后续增量更新只消费本次批次。 + Date nowDate = new Date(System.currentTimeMillis() / 1000 * 1000); // 赋值 - List latestSourceList = rawDataList.stream().map(raw -> { + List latestSourceList = userMiddleList.stream().map(raw -> { Sys_user_source sysUser = new Sys_user_source(); - // 使用缓存的字段映射 + // Record.get 忽略列名大小写;禁止转成普通 JSON 后用大写键读取小写列。 for (FieldMapping mapping : getFieldMappings()) { try { // 根据字段类型设置值 if (mapping.field.getType() == String.class) { - mapping.field.set(sysUser, raw.getStr(mapping.key)); + mapping.field.set(sysUser, StrUtil.trimToNull(raw.getString(mapping.key))); } else if (mapping.field.getType() == Date.class) { - mapping.field.set(sysUser, raw.getDate(mapping.key)); + mapping.field.set(sysUser, cn.hutool.core.convert.Convert.toDate(raw.get(mapping.key))); } } catch (IllegalAccessException e) { - log.error("设置字段值失败: {}", mapping.field.getName(), e); + throw new BaseException("来源字段映射失败:" + mapping.field.getName()); } } + // 在写入批次前拦截映射异常,不能把空工号批次当成“无新增”成功返回。 + if (StrUtil.isBlank(sysUser.getLoginname()) || StrUtil.isBlank(sysUser.getUsername())) { + throw new BaseException("来源人员工号或姓名为空,请检查 ZGH/XM 字段映射;本批次未写入"); + } sysUser.setPullTime(nowDate); return sysUser; }).toList(); - log.info("用户数据初始化完成,等待插入,当前数据条数{}", latestSourceList.size()); + log.info("人员映射校验通过,批次:{},有效人数:{}", DateUtil.formatDateTime(nowDate), latestSourceList.size()); // 插入到数据库 dao().insert(latestSourceList); // 更新字典数据 @@ -162,13 +166,38 @@ public class SysDataUserPullServiceImpl extends BaseServiceImpl log.info("单位需要更新,正在同步更新"); sysDataUnitPullService.updateUnits(unitIds); } + // 新批次及关联数据写入成功后,在当前拉取事务内清理过期来源批次。 + retainLatestPullBatches(); return nowDate; } catch (Exception e) { throw new BaseException("数据拉取失败,{}", e.getMessage()); } } - @Async + /** + * 保留最近十个非空 pullTime 对应的完整批次,空时间记录无法归属批次,一并清理。 + * 无参数、无返回值;由 pull() 在现有事务内调用,清理失败会使本次拉取回滚。 + * 第十批时间作为严格小于的删除边界,避免误删该批次内的其他人员记录。 + */ + @Aop(TransAop.READ_COMMITTED) + private void retainLatestPullBatches() { + Sql sql = Sqls.create("SELECT DISTINCT pullTime FROM sys_user_source " + + "WHERE pullTime IS NOT NULL ORDER BY pullTime DESC LIMIT 1 OFFSET 9"); + sql.setCallback((conn, rs, sqlObject) -> rs.next() ? rs.getTimestamp("pullTime") : null); + dao().execute(sql); + Date oldestRetainedTime = sql.getObject(Date.class); + + // 不足十批时没有时间边界,只删除空时间记录;严禁退化为无条件清表。 + Cnd expired = Cnd.where(Sys_user_source::getPullTime, "IS", null); + if (oldestRetainedTime != null) { + expired.or(Sys_user_source::getPullTime, "<", oldestRetainedTime); + } + int deletedRows = dao().clear(Sys_user_source.class, expired); + log.info("来源批次清理:保留最近10批,最早保留时间:{},删除记录:{}条(随当前事务提交)", + oldestRetainedTime == null ? "不足10批" : DateUtil.formatDateTime(oldestRetainedTime), deletedRows); + } + + @Aop(TransAop.READ_COMMITTED) private void updateDict(List userSources) { //判断是否要更新在职状态字典 List sourceUserStates = userSources.stream().map(Sys_user::getUserState).filter(StrUtil::isNotBlank).distinct().toList(); diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysUserDailySyncService.java b/src/main/java/com/budwk/app/sys/services/impl/SysUserDailySyncService.java new file mode 100644 index 0000000..aa3dda4 --- /dev/null +++ b/src/main/java/com/budwk/app/sys/services/impl/SysUserDailySyncService.java @@ -0,0 +1,109 @@ +package com.budwk.app.sys.services.impl; + +import cn.hutool.core.date.DateUtil; +import com.budwk.app.sys.enums.SysDataUpdateMode; +import com.budwk.app.sys.models.Sys_task; +import com.budwk.app.sys.param.SysDataUserUpdateParam; +import com.budwk.app.sys.services.SysDataUserPullService; +import com.budwk.app.sys.services.SysTaskService; +import com.budwk.app.task.job.SysUserIncrRenewJob; +import com.budwk.app.task.services.TaskPlatformService; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.ioc.aop.Aop; +import org.nutz.ioc.impl.PropertiesProxy; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import java.util.Date; +import java.util.List; +import lombok.extern.slf4j.Slf4j; + +/** 每日人员同步的编排及任务登记,复用手动拉取与全量更新服务。 */ +@IocBean +@Slf4j +public class SysUserDailySyncService { + @Inject private SysDataUserPullService sysDataUserPullService; + @Inject("refer:sysDataUserAllUpdateServiceImpl") + private SysDataUserAllUpdateServiceImpl sysDataUserAllUpdateService; + @Inject private SysTaskService sysTaskService; + @Inject private TaskPlatformService taskPlatformService; + @Inject private PropertiesProxy conf; + + /** 无参数;返回本批次新增统计。拉取失败时抛出异常,不消费历史批次。 */ + @Aop(TransAop.READ_COMMITTED) + public String sync() { + Date pullTime = sysDataUserPullService.pull(); + log.info("人员拉取完成,开始全量更新,批次:{}", DateUtil.formatDateTime(pullTime)); + SysDataUserUpdateParam param = new SysDataUserUpdateParam(); + param.setPullTime(DateUtil.formatDateTime(pullTime)); + param.setUpdateMode(SysDataUpdateMode.ALL.name()); + return sysDataUserAllUpdateService.update(param); + } + + /** + * 启动时复用同执行类的真实任务 ID;旧增量任务只更新名称说明,保留页面设置的执行时间。 + * 重复的历史任务保留记录但禁用;后续重启保留页面设置的时间和禁用状态。 + */ + @Aop(TransAop.READ_COMMITTED) + public void register() { + if (!conf.getBoolean("user-source.sync.enabled", false)) { + return; + } + String jobClass = SysUserIncrRenewJob.class.getName(); + List tasks = sysTaskService.query(Cnd.where("jobClass", "=", jobClass) + .asc("disabled").asc("createdAt").asc("id")); + String name = "每日人员拉取及全量更新"; + String cron = conf.get("user-source.sync.cron", "0 0 17 * * ?"); + String note = "从推送库全量同步;按在职状态及人员类型同步会员身份和会员角色"; + Sys_task task; + if (tasks.isEmpty()) { + task = new Sys_task(); + task.setName(name); + task.setJobClass(jobClass); + task.setCron(cron); + task.setNote(note); + task.setData("{}"); + task.setDisabled(false); + // Sys_task 插入时自动生成 UUID,必须使用返回的真实 ID 注册和记录执行结果。 + task = sysTaskService.insert(task); + } else { + task = tasks.get(0); + if ("每日人员拉取及增量新增".equals(task.getName()) || name.equals(task.getName())) { + task.setName(name); + task.setNote(note); + sysTaskService.update(Chain.make("name", name).add("note", note), + Cnd.where("id", "=", task.getId())); + } + // 兼容旧版本重启生成的重复任务,不删除用户可追溯的任务记录。 + for (int i = 1; i < tasks.size(); i++) { + Sys_task duplicate = tasks.get(i); + sysTaskService.update(Chain.make("disabled", true), Cnd.where("id", "=", duplicate.getId())); + if (taskPlatformService.isExist(duplicate.getId(), duplicate.getId())) { + taskPlatformService.delete(duplicate.getId(), duplicate.getId()); + } + } + } + String legacyId = "sys-user-daily-incr-sync"; + if (taskPlatformService.isExist(legacyId, legacyId)) { + taskPlatformService.delete(legacyId, legacyId); + } + String id = task.getId(); + // 使用数据库保存的 Cron 重建调度;配置默认值只用于首次创建,不覆盖页面设置。 + if (taskPlatformService.isExist(id, id)) { + taskPlatformService.delete(id, id); + } + if (!task.isDisabled()) { + taskPlatformService.add(id, id, task.getJobClass(), task.getCron(), task.getNote(), task.getData()); + } + log.info("人员全量同步任务就绪,任务ID:{},Cron:{},禁用:{},JVM时区:{}", + id, task.getCron(), task.isDisabled(), java.util.TimeZone.getDefault().getID()); + } + + /** taskId 为调度任务主键,result 为统计或失败说明;结果显示在系统任务列表。 */ + @Aop(TransAop.READ_COMMITTED) + public void recordResult(String taskId, String result) { + sysTaskService.update(Chain.make("exeAt", System.currentTimeMillis() / 1000) + .add("exeResult", result), Cnd.where("id", "=", taskId)); + } +} diff --git a/src/main/java/com/budwk/app/task/job/SysUserIncrRenewJob.java b/src/main/java/com/budwk/app/task/job/SysUserIncrRenewJob.java new file mode 100644 index 0000000..b017298 --- /dev/null +++ b/src/main/java/com/budwk/app/task/job/SysUserIncrRenewJob.java @@ -0,0 +1,38 @@ +package com.budwk.app.task.job; + +import com.budwk.app.sys.services.impl.SysUserDailySyncService; +import lombok.extern.slf4j.Slf4j; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.quartz.*; + +/** 每天拉取推送库并全量更新;保留原类名以兼容线上已有任务记录。 */ +@IocBean +@Slf4j +@DisallowConcurrentExecution +public class SysUserIncrRenewJob implements Job { + @Inject + private SysUserDailySyncService sysUserDailySyncService; + + /** Quartz 上下文提供任务 ID;失败交由调度器记录,不立即循环重试。 */ + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + String taskId = context.getJobDetail().getKey().getName(); + long startTime = System.currentTimeMillis(); + log.info("每日人员全量同步开始,任务ID:{}", taskId); + try { + String result = sysUserDailySyncService.sync(); + sysUserDailySyncService.recordResult(taskId, result); + log.info("每日人员同步完成,任务ID:{},耗时:{}ms,{}", taskId, System.currentTimeMillis() - startTime, result); + } catch (Exception e) { + log.error("每日人员同步失败,任务ID:" + taskId, e); + // 记录结果失败不能掩盖最初的同步异常。 + try { + sysUserDailySyncService.recordResult(taskId, "人员同步失败,请检查来源连接、数据及应用日志。"); + } catch (Exception recordError) { + log.error("同步失败结果写入失败,任务ID:" + taskId, recordError); + } + throw new JobExecutionException("每日人员同步失败", e, false); + } + } +} diff --git a/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/declare/controller/ActivityDeclareMineController.java b/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/declare/controller/ActivityDeclareMineController.java index 54d0158..a820d3c 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/declare/controller/ActivityDeclareMineController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/declare/controller/ActivityDeclareMineController.java @@ -223,7 +223,8 @@ public class ActivityDeclareMineController { }); // 协会负责人审核 - doneTaskVos.stream().filter(task -> "协会负责人审核".equals(task.getDisplayName())) + // 同时识别历史与新版节点名称,确保导出保留两种流程版本的负责人审批意见。 + doneTaskVos.stream().filter(task -> ("协会负责人审核".equals(task.getDisplayName()) || "俱乐部负责人审核".equals(task.getDisplayName()))) .max(Comparator.comparing(ProcessTaskVO::getCreatedAt)) .ifPresent(v -> { Dict taskFormData = v.getTaskFormData(); diff --git a/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/enums/ActivityDeclareReimbursement.java b/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/enums/ActivityDeclareReimbursement.java index 6768c0c..7148c50 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/enums/ActivityDeclareReimbursement.java +++ b/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/enums/ActivityDeclareReimbursement.java @@ -27,7 +27,7 @@ public enum ActivityDeclareReimbursement { /** * 协会 */ - CLUB("协会活动"); + CLUB("俱乐部活动"); private final String typeName; } diff --git a/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/reimbursement/controller/ActivityReimbursementBoardController.java b/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/reimbursement/controller/ActivityReimbursementBoardController.java index 678edb7..5d97053 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/reimbursement/controller/ActivityReimbursementBoardController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/reimbursement/controller/ActivityReimbursementBoardController.java @@ -110,7 +110,7 @@ public class ActivityReimbursementBoardController { list.add(new NutMap() {{ put("year", year); - put("type", "社团活动"); + put("type", "俱乐部活动"); 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"))); }}); list.add(new NutMap() {{ - put("type", "社团活动"); + put("type", "俱乐部活动"); put("num", activityReimbursementService.count(sql.setParam("type", "ACTIVITY_BUDGET_TYPE_THREE"))); }}); return Result.success(list); @@ -191,7 +191,7 @@ public class ActivityReimbursementBoardController { list.add(new NutMap() {{ 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")); }}); } @@ -218,7 +218,7 @@ public class ActivityReimbursementBoardController { put("num", activityReimbursementService.count(unionSql)); }}); list.add(new NutMap() {{ - put("type", "社团费用"); + put("type", "俱乐部费用"); put("num", activityReimbursementService.count(clubSql)); }}); return Result.success().addData(list); diff --git a/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/reimbursement/controller/ActivityReimbursementMineController.java b/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/reimbursement/controller/ActivityReimbursementMineController.java index 2cee04b..c940574 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/reimbursement/controller/ActivityReimbursementMineController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/declarereimbursement/reimbursement/controller/ActivityReimbursementMineController.java @@ -325,7 +325,8 @@ public class ActivityReimbursementMineController { }); // 协会负责人审核 - doneTaskVos.stream().filter(task -> "协会负责人审核".equals(task.getDisplayName())) + // 同时识别历史与新版节点名称,确保导出保留两种流程版本的负责人审批意见。 + doneTaskVos.stream().filter(task -> ("协会负责人审核".equals(task.getDisplayName()) || "俱乐部负责人审核".equals(task.getDisplayName()))) .max(Comparator.comparing(ProcessTaskVO::getCreatedAt)) .ifPresent(v -> { Dict taskFormData = v.getTaskFormData(); diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java b/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java index a197f99..314abe6 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/apply/ClubUserJoinApplyController.java @@ -123,7 +123,7 @@ public class ClubUserJoinApplyController { if (ObjectUtil.isNotEmpty(userApply) && StrUtil.isBlank(userApply.getId())) { ProcessInstance processInstance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", userApply.getId())); if (!List.of(ProcessInstanceStateEnum.REJECT.getCode(),ProcessInstanceStateEnum.FINISHED.getCode()).contains(processInstance.getState())) { - return Result.error("您有该协会的申请记录尚未完成,请到我的申请里查看!"); + return Result.error("您有该俱乐部的申请记录尚未完成,请到我的申请里查看!"); } } @@ -178,7 +178,7 @@ public class ClubUserJoinApplyController { ProcessInstance processInstance = dao.fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", userApply.getId())); if (!List.of(ProcessInstanceStateEnum.REJECT.getCode(),ProcessInstanceStateEnum.FINISHED.getCode()).contains(processInstance.getState())) { - return Result.error(99, "您有该协会的申请记录尚未完成,请到我的申请里查看!"); + return Result.error(99, "您有该俱乐部的申请记录尚未完成,请到我的申请里查看!"); } return Result.success(); } diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineApplyController.java b/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineApplyController.java index ded88f7..b6a426a 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineApplyController.java @@ -69,7 +69,7 @@ public class ClubExamineApplyController { @SaCheckLogin public Result getCount(String id, Integer year) { if(StrUtil.isBlank(id)) { - return Result.error("协会信息为空,请核查"); + return Result.error("俱乐部信息为空,请核查"); } if(year == null) { return Result.error("年份信息为空,请核查"); @@ -94,7 +94,7 @@ public class ClubExamineApplyController { @SaCheckLogin public Result getClubUserNum(String clubId) { if(StrUtil.isBlank(clubId)) { - return Result.error("协会信息为空,请核查"); + return Result.error("俱乐部信息为空,请核查"); } List result = sysClubExamineService.getClubUserNum(clubId); return Result.success(result); @@ -104,7 +104,7 @@ public class ClubExamineApplyController { @SaCheckLogin public Result getJgUser(String clubId) { if(StrUtil.isBlank(clubId)) { - return Result.error("协会信息为空,请核查"); + return Result.error("俱乐部信息为空,请核查"); } List result = sysClubExamineService.getJgUser(clubId); return Result.success(result); @@ -114,7 +114,7 @@ public class ClubExamineApplyController { @SaCheckLogin public Result getClubMemberMoney(String clubId) { if(StrUtil.isBlank(clubId)) { - return Result.error("协会信息为空,请核查"); + return Result.error("俱乐部信息为空,请核查"); } int count = dao.count(ClubUser.class, Cnd.where("clubId", "=", clubId)); SysClub club = dao.fetch(SysClub.class, clubId); @@ -134,7 +134,7 @@ public class ClubExamineApplyController { @SaCheckLogin public Result getLasYearSurplus(String clubId) { if(StrUtil.isBlank(clubId)) { - return Result.error("协会信息为空,请核查"); + return Result.error("俱乐部信息为空,请核查"); } SysClubExamineRegister register = dao.fetch(SysClubExamineRegister.class, Cnd.where("clubId", "=", clubId) .and("YEAR(registerDate)", "=", DateUtil.thisYear() - 1)); diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineMineController.java b/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineMineController.java index 8674678..0fc9aea 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineMineController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/examine/ClubExamineMineController.java @@ -107,7 +107,7 @@ public class ClubExamineMineController { .bind("activityQk", activityQk).build(); try { - String fileName = Globals.MyConfig.getString("AppName") + "教职工社团年度考核登记表"; + String fileName = Globals.MyConfig.getString("AppName") + "教职工俱乐部年度考核登记表"; 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"); XWPFTemplate.compile(officeTemplateUtil.getPath("club_examine_register"), config).render(map).writeAndClose(response.getOutputStream()); diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubInfoManageController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubInfoManageController.java index bac633c..9fd824d 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubInfoManageController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubInfoManageController.java @@ -3,7 +3,11 @@ package com.budwk.app.zhgh.club.controller.infoManage; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONUtil; -import com.alibaba.excel.EasyExcel; +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import org.apache.poi.ss.usermodel.Workbook; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; @@ -52,7 +56,6 @@ import org.nutz.mvc.upload.UploadAdaptor; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; -import java.io.ByteArrayOutputStream; import java.util.*; import java.util.stream.Collectors; @@ -277,7 +280,7 @@ public class ClubInfoManageController { int count = clubInfoManageService.dao().count(ClubUser.class, Cnd.where("clubId", "=", clubId) .and(new Static("JSON_CONTAINS(roleCode, '\"%s\"')".formatted(roleCode)))); if (count > 0) { - return Result.error((Objects.equals(roleCode, RoleConstant.CLUB_MANAGER.name()) ? "协会负责人" : "秘书长") + "只能有一位"); + return Result.error((Objects.equals(roleCode, RoleConstant.CLUB_MANAGER.name()) ? "俱乐部负责人" : "秘书长") + "只能有一位"); } } Sys_role sysRole = sysRoleService.getByCode(roleCode); @@ -315,6 +318,7 @@ public class ClubInfoManageController { } @At + @Ok("void") @SaCheckPermission("club.infoManage.manage") @ApiOperation("导出登记表") public void exportRegistrationDoc(@Valid String clubId, HttpServletResponse response){ @@ -325,11 +329,17 @@ public class ClubInfoManageController { @Ok("void") @SaCheckPermission("club.infoManage.manage") public void downloadUserImport(HttpServletResponse response) { - try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();){ - EasyExcel.write(byteArrayOutputStream, ClubUserImportVo.class) - .sheet("协会会员导入模版") - .doWrite(ArrayList::new); - CommonDownloadUtil.download("协会会员导入模版.xlsx", byteArrayOutputStream.toByteArray() ,response); + // 模板保留工号、姓名两列及一行表头,兼容原有从第2行开始的导入逻辑。 + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + exportParams.setSheetName("俱乐部会员导入模版"); + exportParams.setAddIndex(false); + List columns = Arrays.asList( + new ExcelExportEntity("工号", "loginname", 25), + new ExcelExportEntity("姓名", "username", 25)); + try (Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns, Collections.emptyList())) { + workbook.getSheetAt(0).getRow(0).setHeightInPoints(20); + CommonDownloadUtil.download("俱乐部会员导入模版.xlsx", workbook, response); } catch (Exception e) { e.printStackTrace(); } @@ -360,7 +370,7 @@ public class ClubInfoManageController { continue; } if (clubUsers.stream().anyMatch(s -> s.getUserId().equals(user.getId()))) { - v.setErrorInfo("该用户已加入该协会"); + v.setErrorInfo("该用户已加入该俱乐部"); errorInfos.add(v); continue; } diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java index 1e84fa7..4a76397 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRefreshReportController.java @@ -129,7 +129,7 @@ public class ClubRefreshReportController { List 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()))); if (count > 0) { - return Result.error("您有该协会的申请记录尚未完成,请核对!"); + return Result.error("您有该俱乐部的申请记录尚未完成,请核对!"); } SysClub club = clubInfoManageService.dao().fetch(SysClub.class, clubRefresh.getClubId()); diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java index bd6dfa7..b2360d3 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/infoManage/ClubRuleUpdateController.java @@ -128,7 +128,7 @@ public class ClubRuleUpdateController { List 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()))); if (count > 0) { - return Result.error("您有该协会的申请记录尚未完成,请核对!"); + return Result.error("您有该俱乐部的申请记录尚未完成,请核对!"); } SysClub club = clubInfoManageService.dao().fetch(SysClub.class, clubRule.getClubId()); diff --git a/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubStatisticsController.java b/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubStatisticsController.java index 7ce6d6a..c46c5c8 100644 --- a/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubStatisticsController.java +++ b/src/main/java/com/budwk/app/zhgh/club/controller/statistics/ClubStatisticsController.java @@ -268,9 +268,9 @@ public class ClubStatisticsController { List list = sysClubService.listMap(sql); List 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("社团总人数", "total", 20)); + exportEntities.add(new ExcelExportEntity("俱乐部总人数", "total", 20)); exportEntities.add(new ExcelExportEntity("在职人数", "work", 20)); exportEntities.add(new ExcelExportEntity("退休人数", "retire", 20)); exportEntities.add(new ExcelExportEntity("男", "man", 20)); @@ -279,7 +279,7 @@ public class ClubStatisticsController { ExportParams exportParams = new ExportParams(); exportParams.setType(ExcelType.XSSF); Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list); - CommonDownloadUtil.download("协会统计表.xlsx", workbook, response); + CommonDownloadUtil.download("俱乐部统计表.xlsx", workbook, response); } @At diff --git a/src/main/java/com/budwk/app/zhgh/club/model/SysClub.java b/src/main/java/com/budwk/app/zhgh/club/model/SysClub.java index 43db3b2..aedd051 100644 --- a/src/main/java/com/budwk/app/zhgh/club/model/SysClub.java +++ b/src/main/java/com/budwk/app/zhgh/club/model/SysClub.java @@ -34,19 +34,19 @@ public class SysClub extends BaseModel { @Column @Comment("协会名称") @ColDefine(type = ColType.VARCHAR, width = 100) - @NotEmpty(message = "协会名称不能为空") + @NotEmpty(message = "俱乐部名称不能为空") private String clubName; @Column @Comment("协会编码") @ColDefine(type = ColType.VARCHAR, width = 100) - @NotEmpty(message = "协会编码不能为空") + @NotEmpty(message = "俱乐部编码不能为空") private String clubCode; @Column @Comment("协会类型") @ColDefine(type = ColType.VARCHAR, width = 30) - @NotEmpty(message = "协会类型不能为空") + @NotEmpty(message = "俱乐部类型不能为空") private String clubType; @Column diff --git a/src/main/java/com/budwk/app/zhgh/club/model/SysClubEvaluate.java b/src/main/java/com/budwk/app/zhgh/club/model/SysClubEvaluate.java index 5e51656..af96f04 100644 --- a/src/main/java/com/budwk/app/zhgh/club/model/SysClubEvaluate.java +++ b/src/main/java/com/budwk/app/zhgh/club/model/SysClubEvaluate.java @@ -34,7 +34,7 @@ public class SysClubEvaluate extends BaseModel { @Column @Comment("协会id") @ColDefine(type = ColType.VARCHAR, width = 32) - @NotEmpty(message = "协会id不能为空") + @NotEmpty(message = "俱乐部id不能为空") private String clubId; @Column diff --git a/src/main/java/com/budwk/app/zhgh/club/model/SysClubSponsor.java b/src/main/java/com/budwk/app/zhgh/club/model/SysClubSponsor.java index 1869eaa..e0a7448 100644 --- a/src/main/java/com/budwk/app/zhgh/club/model/SysClubSponsor.java +++ b/src/main/java/com/budwk/app/zhgh/club/model/SysClubSponsor.java @@ -31,7 +31,7 @@ public class SysClubSponsor extends BaseModel { @Column @Comment("协会id") @ColDefine(type = ColType.VARCHAR, width = 32) - @NotEmpty(message = "协会不能为空") + @NotEmpty(message = "俱乐部不能为空") private String clubId; @Column diff --git a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java index a23b924..3f7d775 100644 --- a/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/club/service/impl/SysClubInfoManageServiceImpl.java @@ -317,7 +317,7 @@ public class SysClubInfoManageServiceImpl extends BaseServiceImpl map.getDouble("totalBudgetMoney")).sum(); list.add(new HashMap() {{ put("year", year); - put("type", "协会经费"); + put("type", "俱乐部经费"); put("value", totalBudgetMoney3); }}); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/conteoller/ActivityBudgetQueryStatisticsController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/conteoller/ActivityBudgetQueryStatisticsController.java index 78227dd..9ddfa5f 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/conteoller/ActivityBudgetQueryStatisticsController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/conteoller/ActivityBudgetQueryStatisticsController.java @@ -79,7 +79,7 @@ public class ActivityBudgetQueryStatisticsController { entities.add(new ExcelExportEntity("分工会", "unionName", 20)); }else if (outlayManageSource.equals("ACTIVITY_BUDGET_TYPE_THREE")) { entities.add(new ExcelExportEntity("编码", "clubCode", 20)); - entities.add(new ExcelExportEntity("协会名称", "clubName", 20)); + entities.add(new ExcelExportEntity("俱乐部名称", "clubName", 20)); } ExcelExportEntity draftCodeEntity = new ExcelExportEntity("预算金额", "totalBudgetMoney", 20); draftCodeEntity.setType(10); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/template/ActivityBudgetTemp.java b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/template/ActivityBudgetTemp.java index 5a84e3d..637e190 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/template/ActivityBudgetTemp.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/outlay/activityBudget/template/ActivityBudgetTemp.java @@ -47,7 +47,7 @@ public class ActivityBudgetTemp extends ExcelImportError { @ExcelProperty("审核意见") private String auditOpinion; - @ExcelProperty("申报单位(仅社团申报时可在下拉菜单中选择,其他类型不用填写)") + @ExcelProperty("申报单位(仅俱乐部申报时可在下拉菜单中选择,其他类型不用填写)") private String helpUnitName; diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java index adddbe9..1e65598 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/siteCug/service/impl/SiteCugApplyServiceImpl.java @@ -137,7 +137,7 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl imple ins.id AS instanceId, ins.processDefineId instanceProcessDefineId, CASE WHEN info.backOption LIKE 'YEARLY_BATCH:%' THEN 1 ELSE 0 END AS yearlyBatch, - CASE WHEN info.reserveType = 'club' THEN '协会预约' ELSE '分工会预约' END AS reserveTypeName, + CASE WHEN info.reserveType = 'club' THEN '俱乐部预约' ELSE '分工会预约' END AS reserveTypeName, CASE WHEN info.reserveType = 'club' THEN info.clubName ELSE info.applyUnionName END AS reserveTargetName, DATE_FORMAT(info.createdAt, '%Y-%m-%d %H:%i:%s') AS applyTime FROM @@ -298,7 +298,7 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl imple } if (StrUtil.equals(apply.getReserveType(), "club")) { if (StrUtil.isBlank(apply.getClubId())) { - return Map.of(false, "请选择协会"); + return Map.of(false, "请选择俱乐部"); } // 协会预约直接复用项目现成能力;getMyManageClub() 对 SYSADMIN 已放开,因此超级管理员也能代任意协会预约 List myManageClub = sysClubService.getMyManageClub(); @@ -307,7 +307,7 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl imple .findFirst() .orElse(null); if (club == null) { - return Map.of(false, "所选协会不是您当前管理的协会"); + return Map.of(false, "所选俱乐部不是您当前管理的俱乐部"); } apply.setClubName(club.getClubName()); apply.setApplyUnionId(""); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java index 711ee84..5add245 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/unionReimburse/controller/UnionReimburseApplyController.java @@ -224,7 +224,7 @@ public class UnionReimburseApplyController { public Result getClubBudget(@Param("clubId") String clubId) { // 检查clubId参数是否为空 if (StrUtil.isBlank(clubId)) { - return Result.error("社团ID不能为空"); + return Result.error("俱乐部ID不能为空"); } // 根据社团ID和年份查询特定社团的经费信息 OutlayManageClub outlayManageClub = dao.fetch(OutlayManageClub.class, diff --git a/src/main/resources/views/layouts/v4/home/user.js b/src/main/resources/views/layouts/v4/home/user.js index 7b12800..1343e0c 100644 --- a/src/main/resources/views/layouts/v4/home/user.js +++ b/src/main/resources/views/layouts/v4/home/user.js @@ -251,14 +251,19 @@ const user = { console.log('切换到:', tab.name); }, // 处理任务 - async openView(task) { - console.log(task) + openView(task) { const {taskId, taskKey, taskState, instanceId, businessNo, formKey} = task if (!formKey) { this.$message.warning("当前流程没有配置地址") return } - window.open(formKey + "?taskId=" + taskId + "bizId=" + businessNo + "taskKey=" + taskKey + "&tab=" + this.activeTab) + // 保留节点地址已有参数,分别传递任务、业务记录和页签,避免参数粘连导致无法定位申请。 + const targetUrl = new URL(formKey, window.location.origin) + if (taskId) targetUrl.searchParams.set("taskId", taskId) + if (businessNo) targetUrl.searchParams.set("bizId", businessNo) + if (taskKey) targetUrl.searchParams.set("taskKey", taskKey) + targetUrl.searchParams.set("tab", this.activeTab) + window.open(targetUrl.href) }, getRoleNames() { $.post("/platform/sys/role/getRoleNames").then(res => { diff --git a/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js b/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js index 84d02f7..910df75 100644 --- a/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js +++ b/src/main/resources/views/platform/zhgh/activity/culture/common/applyActivity.js @@ -60,9 +60,9 @@ const ACTIVITY_CULTURE_APPLY_ACTIVITY = { - + diff --git a/src/main/resources/views/platform/zhgh/activity/culture/common/singleSignUp.js b/src/main/resources/views/platform/zhgh/activity/culture/common/singleSignUp.js index 8d9808d..8ecadd0 100644 --- a/src/main/resources/views/platform/zhgh/activity/culture/common/singleSignUp.js +++ b/src/main/resources/views/platform/zhgh/activity/culture/common/singleSignUp.js @@ -23,7 +23,7 @@ const singleSignUp = { {{ viewData.activity_type === 40001 ? '校工会活动' : viewData.activity_type === 40002 ? '分工会活动' : - '协会/社团活动' }} + '俱乐部活动' }} diff --git a/src/main/resources/views/platform/zhgh/activity/declarereimbursement/declare/apply/form.html b/src/main/resources/views/platform/zhgh/activity/declarereimbursement/declare/apply/form.html index e1be85a..16f2953 100644 --- a/src/main/resources/views/platform/zhgh/activity/declarereimbursement/declare/apply/form.html +++ b/src/main/resources/views/platform/zhgh/activity/declarereimbursement/declare/apply/form.html @@ -28,9 +28,9 @@