diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourParticipantRosterController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourParticipantRosterController.java index 99538e0..c1cf1fd 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourParticipantRosterController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourParticipantRosterController.java @@ -236,8 +236,9 @@ public class TourParticipantRosterController { if (settingResult != null) { return settingResult; } - tourParticipantRosterService.rebuildSettingRosters(settingIdList); - return Result.success().addMsg("人员名单重新生成成功"); + List warningMessages = tourParticipantRosterService.rebuildSettingRosters(settingIdList); + NutMap data = NutMap.NEW().addv("warnings", warningMessages); + return Result.success(data).addMsg("人员名单重新生成成功"); } /** diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourParticipantRosterService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourParticipantRosterService.java index 8359812..dc82f3b 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourParticipantRosterService.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourParticipantRosterService.java @@ -7,49 +7,51 @@ import com.budwk.app.zhgh.dayofficework.tour.models.TourParticipantRoster; import java.util.List; /** - * Service for maintaining the tour participant roster used by yearly joined/not-joined statistics. + * 疗休养人员名单维护服务。 + * 用于按配置维护可参与人员基础名单,并同步报名台账中的参加状态。 */ public interface TourParticipantRosterService extends BaseService { /** - * Synchronizes eligible staff of one tour setting into the roster table. + * 将一个疗休养配置的可参与人员同步到人员名单表。 * - * @param settingId tour setting id whose activity group defines the eligible staff scope. + * @param settingId 疗休养配置 id,配置中的可参与人员组别决定人员范围。 */ void syncSettingRoster(String settingId); /** - * Synchronizes the setting roster only when no roster rows exist for that setting. + * 仅当配置下还没有人员名单数据时,同步配置可参与人员。 * - * @param settingId tour setting id whose roster should be lazily initialized. + * @param settingId 需要懒加载人员名单的疗休养配置 id。 */ void syncSettingRosterIfEmpty(String settingId); /** - * Rebuilds selected setting rosters from eligible staff scopes and existing tour ledgers. + * 根据可参与人员范围和现有疗休养台账,重新生成所选配置的人员名单。 * - * @param settingIds selected tour setting ids. + * @param settingIds 所选疗休养配置 id。 + * @return 已不在可参与人员范围中但存在台账、因此无法剔除的人员提示。 */ - void rebuildSettingRosters(List settingIds); + List rebuildSettingRosters(List settingIds); /** - * Clears roster rows when a tour setting is deleted. + * 删除疗休养配置时清理对应人员名单。 * - * @param settingId deleted tour setting id. + * @param settingId 已删除的疗休养配置 id。 */ void clearSettingRoster(String settingId); /** - * Marks the roster row as joined and links it with the newest tour ledger. + * 报名成功或修改报名后,将人员名单标记为已参加并关联最新台账。 * - * @param ledger signup ledger created or modified successfully. + * @param ledger 已创建或修改成功的报名台账。 */ void markJoinedByLedger(TourLedger ledger); /** - * Marks the roster row as not joined and clears the linked ledger id. + * 取消报名或删除报名后,将人员名单标记为未参加并清空关联台账。 * - * @param ledger signup ledger that is being cancelled or deleted. + * @param ledger 正在取消或删除的报名台账。 */ void markNotJoinedByLedger(TourLedger ledger); } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourParticipantRosterServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourParticipantRosterServiceImpl.java index a30543a..582f405 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourParticipantRosterServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourParticipantRosterServiceImpl.java @@ -22,11 +22,13 @@ import org.nutz.ioc.loader.annotation.IocBean; import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; /** - * Default implementation that keeps tour participant roster data in sync with settings and signup ledgers. + * 疗休养人员名单维护服务实现。 + * 负责同步配置可参与人员、回填台账参加状态,并保留已有台账人员的历史统计口径。 */ @IocBean(args = {"refer:dao"}) public class TourParticipantRosterServiceImpl extends BaseServiceImpl implements TourParticipantRosterService { @@ -41,7 +43,7 @@ public class TourParticipantRosterServiceImpl extends BaseServiceImpl userIds = activityBasicScopeService.listGroupUserIds(groupId); - if (CollUtil.isEmpty(userIds)) { - return; - } - - for (View_user user : queryUsers(userIds)) { - if (user == null || StrUtil.isBlank(user.getLoginname())) { - continue; - } + for (View_user user : listEligibleUsers(setting)) { upsertRosterUser(settingId, user); } } /** - * Keeps normal setting save lightweight: once a setting has roster rows, editing the setting will not reload users. + * 配置普通保存时只做懒加载:配置下已有名单数据则不重新拉取人员。 */ @Override public void syncSettingRosterIfEmpty(String settingId) { @@ -85,26 +75,40 @@ public class TourParticipantRosterServiceImpl extends BaseServiceImpl settingIds) { + public List rebuildSettingRosters(List settingIds) { + List warningMessages = new ArrayList<>(); if (CollUtil.isEmpty(settingIds)) { - return; + return warningMessages; } for (String settingId : settingIds) { if (StrUtil.isBlank(settingId)) { continue; } + TourSetting setting = dao().fetch(TourSetting.class, settingId); + if (setting == null) { + continue; + } + List eligibleUsers = listEligibleUsers(setting); + Set eligibleJobNos = collectEligibleJobNos(eligibleUsers); clear(Cnd.where("settingId", "=", settingId)); - syncSettingRoster(settingId); - replayLedgerJoinedState(settingId); + for (View_user user : eligibleUsers) { + upsertRosterUser(settingId, user); + } + List retainedUserNames = replayLedgerJoinedState(settingId, eligibleJobNos); + if (CollUtil.isNotEmpty(retainedUserNames)) { + warningMessages.add(buildRetainedLedgerUserMessage(setting, retainedUserNames)); + } } + return warningMessages; } /** - * Removes roster rows that belong to a deleted setting so statistics do not keep obsolete setting data. + * 删除配置下的人员名单,避免已删除配置继续参与统计查询。 */ @Override public void clearSettingRoster(String settingId) { @@ -115,7 +119,7 @@ public class TourParticipantRosterServiceImpl extends BaseServiceImpl listEligibleUsers(TourSetting setting) { + if (setting == null || StrUtil.isBlank(setting.getActivityGroupId())) { + return new ArrayList<>(); + } + Integer groupId = parseGroupId(setting.getActivityGroupId()); + if (groupId == null) { + return new ArrayList<>(); + } + List userIds = activityBasicScopeService.listGroupUserIds(groupId); + if (CollUtil.isEmpty(userIds)) { + return new ArrayList<>(); + } + List users = new ArrayList<>(); + for (View_user user : queryUsers(userIds)) { + if (user != null && StrUtil.isNotBlank(user.getLoginname())) { + users.add(user); + } + } + return users; + } + + /** + * 提取当前可参与人员工号集合,用于判断已有台账人员是否已被移出可参与范围。 + */ + private Set collectEligibleJobNos(List users) { + Set jobNos = new HashSet<>(); + if (CollUtil.isEmpty(users)) { + return jobNos; + } + for (View_user user : users) { + if (user != null && StrUtil.isNotBlank(user.getLoginname())) { + jobNos.add(user.getLoginname()); + } + } + return jobNos; + } + + /** + * 分批查询用户视图,避免大范围人员组别生成过长的 SQL IN 条件。 */ private List queryUsers(List userIds) { List users = new ArrayList<>(); @@ -193,7 +238,7 @@ public class TourParticipantRosterServiceImpl extends BaseServiceImpl replayLedgerJoinedState(String settingId, Set eligibleJobNos) { + Set retainedUserNames = new LinkedHashSet<>(); Sql sql = Sqls.create(""" SELECT t.* FROM tour_ledger t @@ -239,12 +286,16 @@ public class TourParticipantRosterServiceImpl extends BaseServiceImpl(retainedUserNames); } /** - * Finds an active roster row by setting and job number, which is the logical unique key. + * 按配置和工号查找有效人员名单记录,这两个字段是业务逻辑唯一键。 */ private TourParticipantRoster fetchRoster(String settingId, String jobNo) { return fetch(Cnd.where("settingId", "=", settingId) @@ -253,7 +304,7 @@ public class TourParticipantRosterServiceImpl extends BaseServiceImpl userNames) { + return StrUtil.blankToDefault(setting.getConfigName(), "未命名配置") + + "(" + setting.getYear() + "):【" + String.join("、", userNames) + + "】已不在“可参与人员”中,但已生成台账,无法剔除这类人员"; + } + + /** + * 优先显示台账姓名,姓名为空时使用工号,保证提示中始终能定位到具体人员。 + */ + private String displayLedgerUserName(TourLedger ledger) { + if (StrUtil.isNotBlank(ledger.getUserName())) { + return ledger.getUserName(); + } + return StrUtil.blankToDefault(ledger.getJobNo(), "未知人员"); + } } diff --git a/src/main/resources/views/platform/zhgh/dayofficework/Tour/participantRoster/index.html b/src/main/resources/views/platform/zhgh/dayofficework/Tour/participantRoster/index.html index c5b77d3..783b023 100644 --- a/src/main/resources/views/platform/zhgh/dayofficework/Tour/participantRoster/index.html +++ b/src/main/resources/views/platform/zhgh/dayofficework/Tour/participantRoster/index.html @@ -136,9 +136,16 @@ layout("/layouts/platform.html"){ - + - 取消 - 确认 + 取消 + 确认 @@ -215,6 +222,21 @@ layout("/layouts/platform.html"){ text-overflow: ellipsis; white-space: nowrap; } + + .tour-rebuild-warning-message { + width: 680px; + } + + .tour-rebuild-warning-list { + max-height: 360px; + overflow-y: auto; + line-height: 24px; + } + + .tour-rebuild-warning-list p { + margin: 0 0 8px; + color: #606266; + }