diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourBranchUserAssignmentController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourBranchUserAssignmentController.java index c31faf68..4e9c5fa7 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourBranchUserAssignmentController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourBranchUserAssignmentController.java @@ -109,6 +109,24 @@ public class ThirtyTeachTourBranchUserAssignmentController { return Result.success(tourUserAssignmentService.branchQuotaInfo(settingId)); } + /** + * 查询当前配置下已启用的出行时间段,供人员分配弹窗按钮组使用。 + */ + @At + @SaCheckPermission("thirtyTeachTour.branchUserAssignment.assign") + public Result travelPeriodOptions(String settingId) { + return Result.success(tourUserAssignmentService.listBranchEnabledTravelPeriods(settingId)); + } + + /** + * 查询当前分工会在指定出行时间段内的正式名额,正式人员分配按该口径控制。 + */ + @At + @SaCheckPermission("thirtyTeachTour.branchUserAssignment.assign") + public Result periodQuotaInfo(String settingId, String periodId) { + return Result.success(tourUserAssignmentService.branchPeriodQuotaInfo(settingId, periodId)); + } + /** * 保存分工会人员分配,保存时数据来源固定为 BRANCH_UNION。 */ @@ -116,7 +134,22 @@ public class ThirtyTeachTourBranchUserAssignmentController { @Aop(TransAop.READ_COMMITTED) @SaCheckPermission("thirtyTeachTour.branchUserAssignment.assign") @SLog(type = "tour", tag = "疗休养分工会人员分配", msg = "保存分工会人员分配") - public Result doAssign(String settingId, String matterId, String personType, @Param("userIds") String userIds) { + public Result doAssign(String settingId, String matterId, String periodId, String personType, + @Param("userIds") String userIds, String photoFiles, + @Param("assignItems") String assignItems) { + if (StrUtil.isNotBlank(assignItems)) { + List parsedItems; + try { + parsedItems = parseAssignItems(assignItems); + } catch (Exception e) { + return Result.error("人员分配明细参数错误"); + } + try { + return Result.success(tourUserAssignmentService.assignBranchUsers(settingId, matterId, periodId, personType, parsedItems)); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + } List parsedUserIds; try { parsedUserIds = parseUserIds(userIds); @@ -124,7 +157,8 @@ public class ThirtyTeachTourBranchUserAssignmentController { return Result.error("人员参数错误"); } try { - return Result.success(tourUserAssignmentService.assignBranchUsers(settingId, matterId, personType, parsedUserIds)); + return Result.success(tourUserAssignmentService.assignBranchUsers(settingId, matterId, periodId, personType, + buildAssignItems(parsedUserIds, photoFiles))); } catch (IllegalArgumentException e) { return Result.error(e.getMessage()); } @@ -152,9 +186,25 @@ public class ThirtyTeachTourBranchUserAssignmentController { @Aop(TransAop.READ_COMMITTED) @SaCheckPermission("thirtyTeachTour.branchUserAssignment.selectMatter") @SLog(type = "tour", tag = "疗休养分工会人员分配", msg = "选择分工会分配事项") - public Result selectMatter(String id, String matterId) { + public Result selectMatter(String id, String matterId, String photoFiles) { try { - return Result.success(tourUserAssignmentService.selectCurrentBranchAssignmentMatter(id, matterId)); + return Result.success(tourUserAssignmentService.selectCurrentBranchAssignmentMatter(id, matterId, photoFiles)); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + } + + /** + * 更新当前分工会人员分配记录的图片材料,已生成台账时同步回写台账图片。 + */ + @At + @Aop(TransAop.READ_COMMITTED) + @SaCheckPermission("thirtyTeachTour.branchUserAssignment.assign") + @SLog(type = "tour", tag = "疗休养分工会人员分配", msg = "上传分工会人员分配图片") + public Result updatePhotoFiles(String id, String photoFiles) { + try { + tourUserAssignmentService.updateCurrentBranchAssignmentPhotoFiles(id, photoFiles); + return Result.success(); } catch (IllegalArgumentException e) { return Result.error(e.getMessage()); } @@ -215,6 +265,29 @@ public class ThirtyTeachTourBranchUserAssignmentController { return Lang.isEmpty(list) ? Collections.emptyList() : list; } + /** + * 解析前端批量人员分配明细,支持每个人携带独立图片材料。 + */ + private List parseAssignItems(String assignItems) { + if (StrUtil.isBlank(assignItems)) { + return Collections.emptyList(); + } + List list = Json.fromJsonAsList(NutMap.class, assignItems); + return Lang.isEmpty(list) ? Collections.emptyList() : list; + } + + /** + * 兼容旧的 userIds 入参,把公共图片材料转换成按人员明细的统一 service 入参。 + */ + private List buildAssignItems(List userIds, String photoFiles) { + if (Lang.isEmpty(userIds)) { + return Collections.emptyList(); + } + List list = new ArrayList<>(); + userIds.forEach(userId -> list.add(NutMap.NEW().addv("userId", userId).addv("photoFiles", photoFiles))); + return list; + } + /** * 定义人员分配导出字段,保持与页面列表核心字段一致并补充人员基础信息。 */ diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourMySignupController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourMySignupController.java index 5b50e85f..d6e2dcf1 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourMySignupController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourMySignupController.java @@ -512,7 +512,8 @@ public class ThirtyTeachTourMySignupController { tourLedgerService.updateIgnoreNull(ledger); // 报名台账更新后,仅回填已存在的人员分配记录事项信息,不新增记录、不改变分配来源。 - tourUserAssignmentService.backfillExistingAssignmentAfterSignup(matter.getSettingId(), matter.getId(), SecurityUtil.getUserId(), ledger.getBoardingPlace()); + tourUserAssignmentService.backfillExistingAssignmentAfterSignup(matter.getSettingId(), matter.getId(), SecurityUtil.getUserId(), + ledger.getBoardingPlace(), ledger.getPhotoFiles()); tourLedgerFamilyService.clear(Cnd.where(ThirtyTeachTourLedgerFamily::getLedgerId, "=", ledger.getId())); tourLedgerDirectRelativeService.clear(Cnd.where(ThirtyTeachTourLedgerDirectRelative::getLedgerId, "=", ledger.getId())); if (Lang.isNotEmpty(familyList)) { diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSchoolUserAssignmentController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSchoolUserAssignmentController.java index ae411ca3..f1e8dfc3 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSchoolUserAssignmentController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSchoolUserAssignmentController.java @@ -100,8 +100,10 @@ public class ThirtyTeachTourSchoolUserAssignmentController { */ @At @SaCheckPermission("thirtyTeachTour.schoolUserAssignment") - public Result candidatePageData(PageForm pageForm, String settingId, String unionId, String keyword, @Param("userIds") String userIds) { - return Result.success(tourUserAssignmentService.schoolCandidatePage(pageForm, settingId, unionId, keyword, parseUserIds(userIds))); + public Result candidatePageData(PageForm pageForm, String settingId, String unionId, String keyword, @Param("userIds") String userIds, + Boolean summerJoined, Boolean lastThirtyJoined) { + return Result.success(tourUserAssignmentService.schoolCandidatePage(pageForm, settingId, unionId, keyword, + parseUserIds(userIds), summerJoined, lastThirtyJoined)); } /** @@ -138,7 +140,7 @@ public class ThirtyTeachTourSchoolUserAssignmentController { @Aop(TransAop.READ_COMMITTED) @SaCheckPermission("thirtyTeachTour.schoolUserAssignment") @SLog(type = "tour", tag = "疗休养校工会人员分配", msg = "保存校工会人员分配") - public Result doAssign(String settingId, String matterId, String personType, @Param("userIds") String userIds) { + public Result doAssign(String settingId, String matterId, String personType, @Param("userIds") String userIds, String photoFiles) { List parsedUserIds; try { parsedUserIds = parseUserIds(userIds); @@ -146,7 +148,7 @@ public class ThirtyTeachTourSchoolUserAssignmentController { return Result.error("人员参数错误"); } try { - return Result.success(tourUserAssignmentService.assignSchoolUsers(settingId, matterId, personType, parsedUserIds)); + return Result.success(tourUserAssignmentService.assignSchoolUsers(settingId, matterId, personType, parsedUserIds, photoFiles)); } catch (IllegalArgumentException e) { return Result.error(e.getMessage()); } @@ -180,9 +182,25 @@ public class ThirtyTeachTourSchoolUserAssignmentController { @Aop(TransAop.READ_COMMITTED) @SaCheckPermission("thirtyTeachTour.schoolUserAssignment") @SLog(type = "tour", tag = "疗休养校工会人员分配", msg = "选择校工会分配事项") - public Result selectMatter(String id, String matterId) { + public Result selectMatter(String id, String matterId, String photoFiles) { try { - return Result.success(tourUserAssignmentService.selectSchoolAssignmentMatter(id, matterId)); + return Result.success(tourUserAssignmentService.selectSchoolAssignmentMatter(id, matterId, photoFiles)); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + } + + /** + * 更新校工会人员分配记录的图片材料,已生成台账时同步回写台账图片。 + */ + @At + @Aop(TransAop.READ_COMMITTED) + @SaCheckPermission("thirtyTeachTour.schoolUserAssignment") + @SLog(type = "tour", tag = "疗休养校工会人员分配", msg = "上传校工会人员分配图片") + public Result updatePhotoFiles(String id, String photoFiles) { + try { + tourUserAssignmentService.updateSchoolAssignmentPhotoFiles(id, photoFiles); + return Result.success(); } catch (IllegalArgumentException e) { return Result.error(e.getMessage()); } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSettingController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSettingController.java index fcd3e819..91ef1d74 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSettingController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSettingController.java @@ -8,6 +8,7 @@ import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSetting; import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingLot; +import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingTravelPeriod; import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service.ThirtyTeachTourSettingService; import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service.ThirtyTeachTourUserAssignmentService; import org.nutz.aop.interceptor.ioc.TransAop; @@ -22,8 +23,11 @@ import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; @IocBean @@ -77,7 +81,9 @@ public class ThirtyTeachTourSettingController { Cnd lotCnd = Cnd.NEW(); lotCnd.desc(ThirtyTeachTourSettingLot::getLotValue); tourSettingService.dao().fetchLinks(tourSetting, "lots", lotCnd); - tourSetting.setUnionQuotas(tourSettingService.listUnionQuotaRows(id)); + tourSetting.setUnionQuotas(tourSettingService.listUnionQuotaRows(id, tourSetting.getYear(), tourSetting.getActivityGroupId())); + tourSetting.setTravelPeriods(tourSettingService.listTravelPeriods(id)); + tourSetting.setPeriodQuotas(tourSettingService.listPeriodQuotas(id)); return Result.success(tourSetting); } @@ -98,15 +104,17 @@ public class ThirtyTeachTourSettingController { Cnd lotCnd = Cnd.NEW(); lotCnd.desc(ThirtyTeachTourSettingLot::getLotValue); tourSettingService.dao().fetchLinks(tourSetting, "lots", lotCnd); - tourSetting.setUnionQuotas(tourSettingService.listUnionQuotaRows(tourSetting.getId())); + tourSetting.setUnionQuotas(tourSettingService.listUnionQuotaRows(tourSetting.getId(), tourSetting.getYear(), tourSetting.getActivityGroupId())); + tourSetting.setTravelPeriods(tourSettingService.listTravelPeriods(tourSetting.getId())); + tourSetting.setPeriodQuotas(tourSettingService.listPeriodQuotas(tourSetting.getId())); return Result.success(tourSetting); } @At @SaCheckPermission("thirtyTeachTour.setting") - public Result unionQuotaRows(String settingId) { - // 新增配置时 settingId 为空,service 会返回所有分工会的空名额行;编辑时合并已保存名额。 - return Result.success(tourSettingService.listUnionQuotaRows(settingId)); + public Result unionQuotaRows(String settingId, Integer year, String activityGroupId) { + // 新增配置时 settingId 为空,前端传入年度和可参加人员范围;编辑时 service 会兜底读取配置并合并已保存名额。 + return Result.success(tourSettingService.listUnionQuotaRows(settingId, year, activityGroupId)); } @At @@ -123,6 +131,8 @@ public class ThirtyTeachTourSettingController { public Result doSubmit(ThirtyTeachTourSetting tourSetting, @Param(value = "lots") String lots, @Param(value = "unionQuotas") String unionQuotas, + @Param(value = "travelPeriods") String travelPeriods, + @Param(value = "periodQuotas") String periodQuotas, @Param(value = "lotDeleteList") String[] lotDeleteList) { Result checkResult = check(tourSetting); if (checkResult != null) { @@ -145,6 +155,10 @@ public class ThirtyTeachTourSettingController { if (StrUtil.isNotBlank(quotaLimitMessage)) { return Result.error(quotaLimitMessage); } + String periodQuotaLimitMessage = tourSettingService.checkPeriodQuotaLimit(periodQuotas, unionQuotas); + if (StrUtil.isNotBlank(periodQuotaLimitMessage)) { + return Result.error(periodQuotaLimitMessage); + } if (tourSetting.getEnabled() == null) { tourSetting.setEnabled(true); @@ -186,6 +200,10 @@ public class ThirtyTeachTourSettingController { if (lotCheckResult != null) { return lotCheckResult; } + Result periodCheckResult = checkTravelPeriods(travelPeriods); + if (periodCheckResult != null) { + return periodCheckResult; + } if (StrUtil.isBlank(tourSetting.getId())) { if (Lang.isEmpty(tourSetting.getLots())) { tourSettingService.insert(tourSetting); @@ -193,6 +211,8 @@ public class ThirtyTeachTourSettingController { tourSettingService.insertWith(tourSetting, "lots"); } tourSettingService.saveUnionQuotas(tourSetting.getId(), unionQuotas); + Set validPeriodIds = tourSettingService.saveTravelPeriods(tourSetting.getId(), travelPeriods); + tourSettingService.savePeriodQuotas(tourSetting.getId(), periodQuotas, validPeriodIds); } else { // 编辑时先处理页面删除的标段,再保存配置和当前标段行。 if (Lang.isNotEmpty(lotDeleteList)) { @@ -201,6 +221,8 @@ public class ThirtyTeachTourSettingController { tourSettingService.updateIgnoreNull(tourSetting); saveLots(tourSetting); tourSettingService.saveUnionQuotas(tourSetting.getId(), unionQuotas); + Set validPeriodIds = tourSettingService.saveTravelPeriods(tourSetting.getId(), travelPeriods); + tourSettingService.savePeriodQuotas(tourSetting.getId(), periodQuotas, validPeriodIds); } return Result.success(); } @@ -215,6 +237,7 @@ public class ThirtyTeachTourSettingController { } tourSettingService.dao().clear(ThirtyTeachTourSettingLot.class, Cnd.where(ThirtyTeachTourSettingLot::getSettingId, "=", id)); tourSettingService.clearUnionQuotas(id); + tourSettingService.clearTravelPeriodsAndQuotas(id); tourUserAssignmentService.clearBySettingId(id); tourSettingService.delete(id); return Result.success(); @@ -339,4 +362,42 @@ public class ThirtyTeachTourSettingController { } return null; } + + private Result checkTravelPeriods(String travelPeriods) { + if (StrUtil.isBlank(travelPeriods)) { + return null; + } + List periodList; + try { + periodList = Json.fromJsonAsList(ThirtyTeachTourSettingTravelPeriod.class, travelPeriods); + } catch (Exception e) { + return Result.error("时间段数据格式不正确"); + } + if (Lang.isEmpty(periodList)) { + return null; + } + for (int i = 0; i < periodList.size(); i++) { + ThirtyTeachTourSettingTravelPeriod period = periodList.get(i); + if (period == null) { + continue; + } + String rowNo = "第" + (i + 1) + "行"; + if (StrUtil.isBlank(period.getPeriodName())) { + return Result.error(rowNo + "时间段名称不能为空"); + } + if (StrUtil.isBlank(period.getStartDate()) || StrUtil.isBlank(period.getEndDate())) { + return Result.error(rowNo + "开始日期和结束日期不能为空"); + } + try { + LocalDate startDate = LocalDate.parse(period.getStartDate()); + LocalDate endDate = LocalDate.parse(period.getEndDate()); + if (startDate.isAfter(endDate)) { + return Result.error(rowNo + "开始日期不能大于结束日期"); + } + } catch (DateTimeParseException e) { + return Result.error(rowNo + "日期格式不正确"); + } + } + return null; + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSignupController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSignupController.java index 0790b740..9ada4e3f 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSignupController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/controller/ThirtyTeachTourSignupController.java @@ -919,7 +919,8 @@ public class ThirtyTeachTourSignupController { tourLedgerService.insert(ledger); } // 报名台账落库后,仅回填已存在的人员分配记录事项信息,不新增记录、不改变分配来源。 - tourUserAssignmentService.backfillExistingAssignmentAfterSignup(matter.getSettingId(), matter.getId(), SecurityUtil.getUserId(), ledger.getBoardingPlace()); + tourUserAssignmentService.backfillExistingAssignmentAfterSignup(matter.getSettingId(), matter.getId(), SecurityUtil.getUserId(), + ledger.getBoardingPlace(), ledger.getPhotoFiles()); if (Lang.isNotEmpty(familyList)) { familyList.forEach(item -> { item.setLedgerId(ledger.getId()); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourLedger.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourLedger.java index d208542c..6d9d7a89 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourLedger.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourLedger.java @@ -126,6 +126,11 @@ public class ThirtyTeachTourLedger extends BaseModel implements Serializable { @ColDefine(type = ColType.VARCHAR, width = 100) private String boardingPlace; + @Column + @Comment("报名图片材料") + @ColDefine(type = ColType.TEXT) + private String photoFiles; + @Column @Comment("是否携带家属") @ColDefine(type = ColType.BOOLEAN) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourSetting.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourSetting.java index 1bc7587a..d8f71d0f 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourSetting.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourSetting.java @@ -155,11 +155,22 @@ public class ThirtyTeachTourSetting extends BaseModel implements Serializable { @Many(field = "settingId") private List lots; + /** + * 时间段管理,仅用于配置弹窗回显与提交,不作为 thirty_teach_tour_setting 表字段保存。 + */ + @Many(field = "settingId") + private List travelPeriods; + /** * 分工会名额分配,仅用于配置弹窗回显与提交,不作为 thirty_teach_tour_setting 表字段保存。 */ private List unionQuotas; + /** + * 时间段名额分配,仅用于配置弹窗回显与提交,不作为 thirty_teach_tour_setting 表字段保存。 + */ + private List periodQuotas; + @Column @Comment("服务须知") @ColDefine(type = ColType.TEXT) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourSettingPeriodQuota.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourSettingPeriodQuota.java new file mode 100644 index 00000000..61a5e477 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourSettingPeriodQuota.java @@ -0,0 +1,66 @@ +package com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.ColDefine; +import org.nutz.dao.entity.annotation.ColType; +import org.nutz.dao.entity.annotation.Column; +import org.nutz.dao.entity.annotation.Comment; +import org.nutz.dao.entity.annotation.Default; +import org.nutz.dao.entity.annotation.Name; +import org.nutz.dao.entity.annotation.Table; +import org.nutz.dao.entity.annotation.TableMeta; +import org.nutz.dao.interceptor.annotation.PrevInsert; + +import java.io.Serializable; + +/** + * 30年教龄疗休养配置时间段名额。 + * 该表把分工会正式名额继续拆分到各时间段,保存时会校验同一分工会合计不超过正式名额。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("thirty_teach_tour_setting_period_quota") +@TableMeta("{'mysql-charset':'utf8mb4'}") +@Comment("30年教龄疗休养配置时间段名额") +public class ThirtyTeachTourSettingPeriodQuota extends BaseModel implements Serializable { + + @Column + @Name + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + @PrevInsert(uu32 = true) + private String id; + + @Column + @Comment("所属疗休养配置ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String settingId; + + @Column + @Comment("分工会ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String unionId; + + @Column + @Comment("分工会名称") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String unionName; + + @Column + @Comment("时间段ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String periodId; + + @Column + @Comment("时间段名称") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String periodName; + + @Column + @Comment("正式人员名额") + @ColDefine(type = ColType.INT) + @Default("0") + private Integer formalQuota; +} diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourSettingTravelPeriod.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourSettingTravelPeriod.java new file mode 100644 index 00000000..250bd830 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourSettingTravelPeriod.java @@ -0,0 +1,59 @@ +package com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models; + +import com.budwk.app.base.model.BaseModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.nutz.dao.entity.annotation.ColDefine; +import org.nutz.dao.entity.annotation.ColType; +import org.nutz.dao.entity.annotation.Column; +import org.nutz.dao.entity.annotation.Comment; +import org.nutz.dao.entity.annotation.Default; +import org.nutz.dao.entity.annotation.Name; +import org.nutz.dao.entity.annotation.Table; +import org.nutz.dao.entity.annotation.TableMeta; + +import java.io.Serializable; + +/** + * 30年教龄疗休养配置时间段。 + * 时间段只负责配置端细分名额的维度,报名/线路占用校验由后续业务接入时再按该配置扩展。 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("thirty_teach_tour_setting_travel_period") +@TableMeta("{'mysql-charset':'utf8mb4'}") +@Comment("30年教龄疗休养配置时间段") +public class ThirtyTeachTourSettingTravelPeriod extends BaseModel implements Serializable { + + @Column + @Name + @Comment("ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String id; + + @Column + @Comment("所属疗休养配置ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String settingId; + + @Column + @Comment("时间段名称") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String periodName; + + @Column + @Comment("开始日期") + @ColDefine(type = ColType.VARCHAR, width = 10) + private String startDate; + + @Column + @Comment("结束日期") + @ColDefine(type = ColType.VARCHAR, width = 10) + private String endDate; + + @Column + @Comment("是否启用") + @ColDefine(type = ColType.BOOLEAN) + @Default("1") + private Boolean enabled; +} diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourUserAssignment.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourUserAssignment.java index cca419a8..cfd54e69 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourUserAssignment.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/models/ThirtyTeachTourUserAssignment.java @@ -77,6 +77,21 @@ public class ThirtyTeachTourUserAssignment extends BaseModel implements Serializ @ColDefine(type = ColType.VARCHAR, width = 100) private String boardingPlace; + @Column + @Comment("报名图片材料") + @ColDefine(type = ColType.TEXT) + private String photoFiles; + + @Column + @Comment("出行时间段ID") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String periodId; + + @Column + @Comment("出行时间段名称") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String periodName; + @Column @Comment("人员ID") @ColDefine(type = ColType.VARCHAR, width = 32) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/service/ThirtyTeachTourSettingService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/service/ThirtyTeachTourSettingService.java index 8e0f7f2c..7123f07d 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/service/ThirtyTeachTourSettingService.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/thirtyTeachTour/service/ThirtyTeachTourSettingService.java @@ -2,19 +2,40 @@ package com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service; import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSetting; +import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingPeriodQuota; +import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingTravelPeriod; import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingUnionQuota; import java.util.List; +import java.util.Set; public interface ThirtyTeachTourSettingService extends BaseService { /** - * 查询所有分工会在指定疗休养配置下的名额,实时补充当前工会会员数供页面分配时参考。 + * 查询所有分工会在指定疗休养配置下的名额,并实时补充可参加人数供页面分配时参考。 * - * @param settingId 疗休养配置ID,新增时可为空 + * @param settingId 疗休养配置ID,新增时可为空 + * @param year 配置年度,新增未落库时用于计算历史参加情况 + * @param activityGroupId 可参加人员范围分组ID,新增未落库时用于计算可参加人员 * @return 按分工会编码排序后的名额列表 */ - List listUnionQuotaRows(String settingId); + List listUnionQuotaRows(String settingId, Integer year, String activityGroupId); + + /** + * 查询配置下维护的时间段,按开始日期和创建时间排序。 + * + * @param settingId 疗休养配置ID + * @return 时间段列表 + */ + List listTravelPeriods(String settingId); + + /** + * 查询配置下已保存的时间段名额。 + * + * @param settingId 疗休养配置ID + * @return 时间段名额列表 + */ + List listPeriodQuotas(String settingId); /** * 保存指定疗休养配置下的分工会名额,前端传入的是 JSON 数组字符串。 @@ -24,6 +45,25 @@ public interface ThirtyTeachTourSettingService extends BaseService saveTravelPeriods(String settingId, String travelPeriods); + + /** + * 保存指定配置下的时间段名额,保存前会清理无效时间段和零名额行。 + * + * @param settingId 疗休养配置ID + * @param periodQuotas 时间段名额 JSON + * @param validPeriodIds 当前配置下本次实际保存且已启用的时间段ID集合 + */ + void savePeriodQuotas(String settingId, String periodQuotas, Set validPeriodIds); + /** * 校验分工会正式人员名额合计是否超过分工会总名额。 * @@ -33,10 +73,26 @@ public interface ThirtyTeachTourSettingService extends BaseService schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword, List userIds); + Pagination schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword, List userIds, + Boolean summerJoinedFilter, Boolean lastThirtyJoinedFilter); /** * 分页查询当前登录人所在分工会的人员分配记录,列表只读取人员分配表,不读取报名台账。 @@ -157,11 +160,11 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService userIds); + NutMap assignSchoolUsers(String settingId, String matterId, String personType, List userIds, String photoFiles); /** * 保存校工会人员分配,支持每个候选人员单独选择分配线路。 - * assignItems 中每项包含 userId、matterId;matterId 为空时只保存人员分配,不写台账。 + * assignItems 中每项包含 userId、matterId、photoFiles;matterId 为空时只保存人员分配,不写台账。 * * @param settingId 疗休养配置ID * @param assignItems 人员和分配线路明细 @@ -177,7 +180,7 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService listBranchEnabledTravelPeriods(String settingId); + + /** + * 查询当前登录人所在分工会在指定时间段下的正式名额使用情况。 + * + * @param settingId 疗休养配置ID + * @param periodId 出行时间段ID + * @return 时间段正式名额、已分配人数和剩余人数 + */ + NutMap branchPeriodQuotaInfo(String settingId, String periodId); /** * 保存分工会人员分配。保存前会重新按配置可参加人员范围和当前登录人所在分工会过滤,并校验正式/替补名额。 + * 正式人员必须选择出行时间段,并按“当前分工会 + 出行时间段”校验正式名额;替补人员沿用分工会替补名额。 * 疗休养事项为可选;正式人员选择事项时视为代报名并同步写入疗休养台账,替补人员不能分配事项。 * * @param settingId 疗休养配置ID * @param matterId 疗休养事项ID,可为空 + * @param periodId 出行时间段ID,正式人员必填 * @param personType 人员类型:FORMAL 正式人员,BACKUP 替补人员 - * @param userIds 待分配人员ID列表 + * @param assignItems 待分配人员明细,每项包含 userId、photoFiles * @return 保存结果,包含实际分配数量、跳过数量、代报名台账写入数量和剩余名额 */ - NutMap assignBranchUsers(String settingId, String matterId, String personType, List userIds); + NutMap assignBranchUsers(String settingId, String matterId, String periodId, String personType, List assignItems); /** * 切换当前分工会人员分配记录的正式/替补状态。 @@ -251,7 +273,7 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService listUnionQuotaRows(String settingId) { - // 一次性查出所有分工会、已保存名额和实时会员数,避免页面打开时按分工会循环统计造成 N+1 查询。 + public List listUnionQuotaRows(String settingId, Integer year, String activityGroupId) { + ThirtyTeachTourSetting setting = resolveQuotaSetting(settingId); + Integer effectiveYear = year == null && setting != null ? setting.getYear() : year; + String effectiveActivityGroupId = StrUtil.isBlank(activityGroupId) && setting != null ? setting.getActivityGroupId() : activityGroupId; + String quotaSettingId = settingId == null ? "" : settingId; + int previousYear = effectiveYear == null ? 0 : effectiveYear - 1; + // 一次性查出所有分工会、已保存名额和可参加人数,避免页面打开时按分工会循环统计造成 N+1 查询。 + // 可参加人数口径:当前活动人员范围内人员,扣除去年已参加30年教龄疗休养和本年度已参加暑期疗休养的人员。 Sql sql = Sqls.create(""" SELECT quota.id AS id, - @settingId AS settingId, + @quotaSettingId AS settingId, un.id AS unionId, un.name AS unionName, COALESCE(quota.formalQuota, 0) AS formalQuota, @@ -41,16 +51,37 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl 0 + AND NOT EXISTS ( + SELECT 1 + FROM thirty_teach_tour_ledger previous_ledger + WHERE previous_ledger.delFlag = 0 + AND previous_ledger.joined = 1 + AND previous_ledger.`year` = @previousYear + AND previous_ledger.jobNo = u.loginname + ) + AND NOT EXISTS ( + SELECT 1 + FROM tour_ledger summer_ledger + WHERE summer_ledger.delFlag = 0 + AND summer_ledger.joined = 1 + AND summer_ledger.`year` = @year + AND summer_ledger.jobNo = u.loginname + ) + GROUP BY u.unionid ) user_count ON user_count.unionId = un.id ORDER BY un.unionCode ASC """); - sql.setParam("settingId", settingId == null ? "" : settingId); + sql.setParam("quotaSettingId", quotaSettingId); + sql.setParam("activityGroupId", StrUtil.blankToDefault(effectiveActivityGroupId, "")); + sql.setParam("year", effectiveYear == null ? 0 : effectiveYear); + sql.setParam("previousYear", previousYear); List rows = listMap(sql); return rows.stream().map(row -> { ThirtyTeachTourSettingUnionQuota quota = new ThirtyTeachTourSettingUnionQuota(); @@ -65,6 +96,37 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl listTravelPeriods(String settingId) { + if (StrUtil.isBlank(settingId)) { + return List.of(); + } + Cnd cnd = Cnd.NEW(); + cnd.and("settingId", "=", settingId) + .and("delFlag", "=", false) + .asc("startDate") + .asc("createdAt"); + return dao().query(ThirtyTeachTourSettingTravelPeriod.class, cnd); + } + + @Override + public List listPeriodQuotas(String settingId) { + if (StrUtil.isBlank(settingId)) { + return List.of(); + } + Cnd cnd = Cnd.where("settingId", "=", settingId) + .and("delFlag", "=", false); + return dao().query(ThirtyTeachTourSettingPeriodQuota.class, cnd); + } + @Override public void saveUnionQuotas(String settingId, String unionQuotas) { if (StrUtil.isBlank(settingId)) { @@ -90,6 +152,76 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl saveTravelPeriods(String settingId, String travelPeriods) { + if (StrUtil.isBlank(settingId)) { + return Collections.emptySet(); + } + dao().clear(ThirtyTeachTourSettingTravelPeriod.class, + Cnd.where("settingId", "=", settingId)); + if (StrUtil.isBlank(travelPeriods)) { + return Collections.emptySet(); + } + List periodList = Json.fromJsonAsList(ThirtyTeachTourSettingTravelPeriod.class, travelPeriods); + if (Lang.isEmpty(periodList)) { + return Collections.emptySet(); + } + List saveList = periodList.stream() + .filter(item -> item != null + && StrUtil.isNotBlank(item.getId()) + && StrUtil.isNotBlank(item.getPeriodName()) + && StrUtil.isNotBlank(item.getStartDate()) + && StrUtil.isNotBlank(item.getEndDate())) + .peek(item -> { + // 前端会为新增时间段生成临时ID,服务端保留该ID以便时间段名额能稳定关联。 + item.setSettingId(settingId); + item.setEnabled(Boolean.TRUE.equals(item.getEnabled())); + }) + .collect(Collectors.toList()); + if (Lang.isNotEmpty(saveList)) { + dao().insert(saveList); + } + return saveList.stream() + .filter(item -> Boolean.TRUE.equals(item.getEnabled())) + .map(ThirtyTeachTourSettingTravelPeriod::getId) + .collect(Collectors.toSet()); + } + + @Override + public void savePeriodQuotas(String settingId, String periodQuotas, Set validPeriodIds) { + if (StrUtil.isBlank(settingId)) { + return; + } + dao().clear(ThirtyTeachTourSettingPeriodQuota.class, + Cnd.where("settingId", "=", settingId)); + if (StrUtil.isBlank(periodQuotas)) { + return; + } + List quotaList = Json.fromJsonAsList(ThirtyTeachTourSettingPeriodQuota.class, periodQuotas); + if (Lang.isEmpty(quotaList)) { + return; + } + Set periodIds = Lang.isEmpty(validPeriodIds) + ? listTravelPeriods(settingId).stream() + .filter(item -> Boolean.TRUE.equals(item.getEnabled())) + .map(ThirtyTeachTourSettingTravelPeriod::getId) + .collect(Collectors.toSet()) + : validPeriodIds; + Map unionMap = dao().query(Sys_union.class, Cnd.NEW()).stream() + .collect(Collectors.toMap(Sys_union::getId, item -> item, (a, b) -> a)); + List saveList = quotaList.stream() + .filter(item -> item != null + && StrUtil.isNotBlank(item.getUnionId()) + && StrUtil.isNotBlank(item.getPeriodId()) + && periodIds.contains(item.getPeriodId()) + && defaultInt(item.getFormalQuota()) > 0) + .map(item -> normalizePeriodQuota(settingId, item, unionMap)) + .collect(Collectors.toList()); + if (Lang.isNotEmpty(saveList)) { + dao().insert(saveList); + } + } + @Override public String checkBranchFormalQuotaLimit(String unionQuotas, int branchTotalQuota) { if (StrUtil.isBlank(unionQuotas)) { @@ -109,6 +241,37 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl periodQuotaList = Json.fromJsonAsList(ThirtyTeachTourSettingPeriodQuota.class, periodQuotas); + if (Lang.isEmpty(periodQuotaList)) { + return ""; + } + List unionQuotaList = StrUtil.isBlank(unionQuotas) + ? List.of() + : Json.fromJsonAsList(ThirtyTeachTourSettingUnionQuota.class, unionQuotas); + Map unionQuotaMap = unionQuotaList.stream() + .filter(item -> item != null && StrUtil.isNotBlank(item.getUnionId())) + .collect(Collectors.toMap(ThirtyTeachTourSettingUnionQuota::getUnionId, item -> item, (a, b) -> a)); + Map periodQuotaTotalMap = periodQuotaList.stream() + .filter(item -> item != null && StrUtil.isNotBlank(item.getUnionId())) + .collect(Collectors.groupingBy(ThirtyTeachTourSettingPeriodQuota::getUnionId, + Collectors.summingInt(item -> defaultInt(item.getFormalQuota())))); + for (Map.Entry entry : periodQuotaTotalMap.entrySet()) { + ThirtyTeachTourSettingUnionQuota unionQuota = unionQuotaMap.get(entry.getKey()); + int formalQuota = unionQuota == null ? 0 : defaultInt(unionQuota.getFormalQuota()); + if (entry.getValue() > formalQuota) { + String unionName = unionQuota == null ? "" : unionQuota.getUnionName(); + return "分工会【" + StrUtil.blankToDefault(unionName, entry.getKey()) + "】时间段名额合计 " + + entry.getValue() + ",不能大于当前分工会正式名额 " + formalQuota; + } + } + return ""; + } + @Override public void clearUnionQuotas(String settingId) { if (StrUtil.isNotBlank(settingId)) { @@ -116,6 +279,15 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl candidateUsers = listMap(userSql); + fillCandidatePhotoFiles(candidateUsers, normalizePhotoFiles(photoFiles)); List assignments = candidateUsers.stream() .map(user -> buildSchoolAssignment(settingId, matterInfo, normalizedPersonType, user)) .collect(Collectors.toList()); @@ -392,6 +394,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl matterByUserId = new HashMap<>(); + Map photoFilesByUserId = new HashMap<>(); List userIds = assignItems.stream() .map(item -> item == null ? "" : item.getString("userId", "")) .filter(StrUtil::isNotBlank) @@ -406,6 +409,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl candidateUsers = listMap(userSql); + candidateUsers.forEach(user -> user.put("photoFiles", photoFilesByUserId.get(user.getString("userId", "")))); List assignments = candidateUsers.stream() .map(user -> { @@ -456,7 +461,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl users = Collections.singletonList(fetchAssignmentUserSnapshot(assignment)); checkProxySignupMaxGroupPeople(matterInfo, users); - updateAssignmentMatterSnapshot(id, matterInfo, ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION); + updateAssignmentMatterSnapshot(id, matterInfo, ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION, photoFiles); int ledgerCount = insertProxySignupLedgers(matterInfo, users); return NutMap.NEW().addv("ledgerCount", ledgerCount); } @Override - public NutMap assignBranchUsers(String settingId, String matterId, String personType, List userIds) { + public NutMap assignBranchUsers(String settingId, String matterId, String periodId, String personType, List assignItems) { String unionId = SecurityUtil.getUnionId(); if (StrUtil.isBlank(unionId)) { throw new IllegalArgumentException("当前登录人未绑定分工会"); } - List distinctUserIds = normalizeUserIds(userIds); - if (StrUtil.isBlank(settingId) || Lang.isEmpty(distinctUserIds)) { + if (StrUtil.isBlank(settingId) || Lang.isEmpty(assignItems)) { throw new IllegalArgumentException("请选择疗休养配置和分配人员"); } + Map photoFilesByUserId = new HashMap<>(); + List distinctUserIds = assignItems.stream() + .map(item -> item == null ? "" : item.getString("userId", "")) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + assignItems.forEach(item -> { + if (item == null) { + return; + } + String userId = item.getString("userId", ""); + if (StrUtil.isBlank(userId)) { + return; + } + // 图片材料来自人员分配弹窗的行级暂存,按人员写入后续分配表和台账。 + photoFilesByUserId.put(userId, normalizePhotoFiles(item.getString("photoFiles"))); + }); + if (Lang.isEmpty(distinctUserIds)) { + throw new IllegalArgumentException("请选择分配人员"); + } String normalizedPersonType = normalizePersonType(personType); NutMap matterInfo = prepareMatterInfoForAssignment(settingId, matterId, normalizedPersonType); // 分工会代报名占用事项最大成团人数;在同一事务中锁定事项行,避免多个分工会同时校验通过后并发写入导致超额。 @@ -539,26 +565,47 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl candidateUsers = listMap(userSql); + candidateUsers.forEach(user -> user.put("photoFiles", photoFilesByUserId.get(user.getString("userId", "")))); if (candidateUsers.size() > remainingQuota) { throw new IllegalArgumentException("选择人数超过当前分工会剩余名额"); } // 分工会代报名会占用线路成团人数,人员分配和台账写入前先做后端兜底校验。 checkProxySignupMaxGroupPeople(matterInfo, candidateUsers); final NutMap assignmentMatterInfo = matterInfo; + final ThirtyTeachTourSettingTravelPeriod assignmentPeriod = selectedPeriod; List assignments = candidateUsers.stream() - .map(user -> buildAssignment(settingId, assignmentMatterInfo, normalizedPersonType, user, - ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION)) + .map(user -> { + ThirtyTeachTourUserAssignment assignment = buildAssignment(settingId, assignmentMatterInfo, normalizedPersonType, user, + ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION); + fillTravelPeriodSnapshot(assignment, assignmentPeriod); + return assignment; + }) .collect(Collectors.toList()); if (Lang.isNotEmpty(assignments)) { dao().insert(assignments); @@ -566,11 +613,14 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl listBranchEnabledTravelPeriods(String settingId) { + if (StrUtil.isBlank(settingId)) { + return Collections.emptyList(); + } + List periods = dao().query(ThirtyTeachTourSettingTravelPeriod.class, + Cnd.where("settingId", "=", settingId) + .and("enabled", "=", true) + .and("delFlag", "=", false) + .asc("startDate") + .asc("createdAt")); + return periods.stream() + .map(period -> NutMap.NEW() + .addv("id", period.getId()) + .addv("periodName", period.getPeriodName()) + .addv("startDate", period.getStartDate()) + .addv("endDate", period.getEndDate()) + .addv("label", travelPeriodButtonLabel(period))) + .collect(Collectors.toList()); + } + + @Override + public NutMap branchPeriodQuotaInfo(String settingId, String periodId) { + String unionId = SecurityUtil.getUnionId(); + if (StrUtil.isBlank(settingId) || StrUtil.isBlank(periodId) || StrUtil.isBlank(unionId)) { + return emptyPeriodQuotaInfo(periodId); + } + Sql sql = Sqls.create(""" + SELECT + COALESCE(q.formalQuota, 0) AS formalQuota, + COALESCE(used_quota.formalUsed, 0) AS formalUsed + FROM thirty_teach_tour_setting_period_quota q + LEFT JOIN ( + SELECT + settingId, + unionId, + periodId, + COUNT(1) AS formalUsed + FROM thirty_teach_tour_user_assignment + WHERE delFlag = 0 + AND assignSource = @assignSource + AND personType = @formalType + AND settingId = @settingId + AND unionId = @unionId + AND periodId = @periodId + AND (cancelled = 0 OR cancelled IS NULL) + GROUP BY settingId, unionId, periodId + ) used_quota ON used_quota.settingId = q.settingId + AND used_quota.unionId = q.unionId + AND used_quota.periodId = q.periodId + WHERE q.settingId = @settingId + AND q.unionId = @unionId + AND q.periodId = @periodId + AND q.delFlag = 0 + LIMIT 1 + """); + sql.setParam("assignSource", ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION); + sql.setParam("formalType", ThirtyTeachTourUserAssignment.PERSON_TYPE_FORMAL); + sql.setParam("settingId", settingId); + sql.setParam("unionId", unionId); + sql.setParam("periodId", periodId); + List rows = listMap(sql); + NutMap quotaInfo = Lang.isEmpty(rows) ? emptyPeriodQuotaInfo(periodId) : rows.get(0); + int formalQuota = defaultInt(quotaInfo.getInt("formalQuota")); + int formalUsed = defaultInt(quotaInfo.getInt("formalUsed")); + // NutMap.addv 对已存在的同名 key 会形成数组,这里直接构造最终返回值,避免前端显示 [0, 12]。 + return NutMap.NEW() + .addv("periodId", periodId) + .addv("formalQuota", formalQuota) + .addv("formalUsed", formalUsed) + .addv("formalRemaining", Math.max(formalQuota - formalUsed, 0)); + } + @Override public NutMap switchCurrentBranchPersonType(String id, String personType) { String unionId = SecurityUtil.getUnionId(); @@ -651,6 +774,14 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl 0 THEN 1 ELSE 0 END AS summerJoined, + CASE WHEN IFNULL(last_thirty_joined.joinedCount, 0) > 0 THEN 1 ELSE 0 END AS lastThirtyJoined FROM activity_user_scope aus LEFT JOIN vw_user u ON u.id = aus.userId LEFT JOIN thirty_teach_tour_user_assignment a ON a.settingId = @settingId AND a.userId = aus.userId AND a.delFlag = 0 + LEFT JOIN ( + SELECT jobNo, COUNT(1) AS joinedCount + FROM tour_ledger + WHERE delFlag = 0 + AND joined = 1 + AND `year` = @currentYear + GROUP BY jobNo + ) summer_joined ON summer_joined.jobNo = u.loginname + LEFT JOIN ( + SELECT jobNo, COUNT(1) AS joinedCount + FROM thirty_teach_tour_ledger + WHERE delFlag = 0 + AND joined = 1 + AND `year` = @lastThirtyYear + GROUP BY jobNo + ) last_thirty_joined ON last_thirty_joined.jobNo = u.loginname $condition - """).setParam("settingId", settingId); + """).setParam("settingId", settingId) + .setParam("currentYear", currentYear) + .setParam("lastThirtyYear", currentYear - 1); } - private Cnd buildCandidateCnd(String settingId, String activityGroupId, String unionId, String keyword) { + /** + * 组装候选人过滤条件,参游记录过滤为null时表示不限制该条件。 + */ + private Cnd buildCandidateCnd(String activityGroupId, String unionId, String keyword, + Boolean summerJoinedFilter, Boolean lastThirtyJoinedFilter) { Cnd cnd = Cnd.NEW(); cnd.and("aus.groupId", "=", activityGroupId); cnd.and("u.id", "is not", null); cnd.and("a.id", "is", null); cnd.andEX("u.unionid", "=", unionId); + appendJoinedFilter(cnd, "summer_joined.joinedCount", summerJoinedFilter); + appendJoinedFilter(cnd, "last_thirty_joined.joinedCount", lastThirtyJoinedFilter); if (StrUtil.isNotBlank(keyword)) { SqlExpressionGroup group = new SqlExpressionGroup(); group.orLike("u.username", keyword); @@ -1037,6 +1218,16 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl" : "=", 0); + } + private NutMap assignmentDeleteInfo(ThirtyTeachTourUserAssignment assignment) { int ledgerCount = countAssignmentLedgers(assignment); return NutMap.NEW() @@ -1089,6 +1280,28 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl users = listMap(sql); if (Lang.isNotEmpty(users)) { - return users.get(0); + NutMap user = users.get(0); + user.put("photoFiles", assignment.getPhotoFiles()); + return user; } // 视图中找不到人员时使用分配表快照兜底,保证补选事项不会因非关键字段缺失中断。 return NutMap.NEW() @@ -1496,7 +1730,8 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl photos = StrUtil.splitTrim(photoFiles, ","); + if (Lang.isEmpty(photos)) { + return ""; + } + return photos.stream() + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.joining(",")); + } + + /** + * 批量人员共用同一组上传图片时,将图片路径写入候选人快照,后续构建分配表和台账时直接读取。 + */ + private void fillCandidatePhotoFiles(List candidateUsers, String photoFiles) { + if (Lang.isEmpty(candidateUsers)) { + return; + } + candidateUsers.forEach(user -> user.put("photoFiles", photoFiles)); + } + private Pagination emptyPagination(PageForm pageForm) { int pageNumber = pageForm == null || pageForm.getPageNumber() == null ? 1 : pageForm.getPageNumber(); int pageSize = pageForm == null || pageForm.getPageSize() == null ? 10 : pageForm.getPageSize(); @@ -1591,6 +1854,60 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl rows = listMap(sql); + if (Lang.isEmpty(rows)) { + throw new IllegalArgumentException("当前分工会未配置该出行时间名额"); + } + } + + private String travelPeriodButtonLabel(ThirtyTeachTourSettingTravelPeriod period) { + if (period == null) { + return ""; + } + if (StrUtil.isNotBlank(period.getStartDate()) && StrUtil.isNotBlank(period.getEndDate())) { + return period.getStartDate().replace("-", "") + "-" + period.getEndDate().replace("-", ""); + } + return StrUtil.blankToDefault(period.getPeriodName(), ""); + } + private String remainingQuotaKey(String personType) { return ThirtyTeachTourUserAssignment.PERSON_TYPE_BACKUP.equals(personType) ? "backupRemaining" : "formalRemaining"; } diff --git a/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_add_photo_files.sql b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_add_photo_files.sql new file mode 100644 index 00000000..97673cae --- /dev/null +++ b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_add_photo_files.sql @@ -0,0 +1,7 @@ +-- 30年教龄疗休养报名图片材料字段。 +-- PC端选择线路、移动端提交报名上传的多张图片统一保存为逗号分隔路径。 +ALTER TABLE `thirty_teach_tour_user_assignment` + ADD COLUMN `photoFiles` text DEFAULT NULL COMMENT '报名图片材料' AFTER `boardingPlace`; + +ALTER TABLE `thirty_teach_tour_ledger` + ADD COLUMN `photoFiles` text DEFAULT NULL COMMENT '报名图片材料' AFTER `boardingPlace`; diff --git a/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_ledger_create.sql b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_ledger_create.sql index 39a98baf..4d603d19 100644 --- a/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_ledger_create.sql +++ b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_ledger_create.sql @@ -22,6 +22,7 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_ledger` ( `travelAgencyId` varchar(32) DEFAULT NULL COMMENT '旅行社ID', `travelAgencyName` varchar(100) DEFAULT NULL COMMENT '旅行社', `boardingPlace` varchar(100) DEFAULT NULL COMMENT '乘车地点', + `photoFiles` text DEFAULT NULL COMMENT '报名图片材料', `hasFamily` tinyint(1) DEFAULT 0 COMMENT '是否携带家属', `intendedRoommate` varchar(100) DEFAULT NULL COMMENT '意向拼床人', `bedType` varchar(50) DEFAULT NULL COMMENT '床型', diff --git a/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_setting_create.sql b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_setting_create.sql index 3f549e5f..efde134e 100644 --- a/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_setting_create.sql +++ b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_setting_create.sql @@ -74,6 +74,42 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_union_quota` ( KEY `idx_thirty_teach_tour_setting_union_quota_union` (`unionId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='疗休养配置分工会名额分配'; +CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_travel_period` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID', + `periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称', + `startDate` varchar(10) DEFAULT NULL COMMENT '开始日期', + `endDate` varchar(10) DEFAULT NULL COMMENT '结束日期', + `enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_thirty_teach_tour_setting_period_setting` (`settingId`), + KEY `idx_thirty_teach_tour_setting_period_date` (`startDate`, `endDate`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段'; + +CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_period_quota` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID', + `unionId` varchar(32) DEFAULT NULL COMMENT '分工会ID', + `unionName` varchar(100) DEFAULT NULL COMMENT '分工会名称', + `periodId` varchar(32) DEFAULT NULL COMMENT '时间段ID', + `periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称', + `formalQuota` int DEFAULT 0 COMMENT '正式人员名额', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_thirty_teach_tour_period_quota_setting` (`settingId`), + KEY `idx_thirty_teach_tour_period_quota_union` (`unionId`), + KEY `idx_thirty_teach_tour_period_quota_period` (`periodId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段名额'; + -- 疗休养人员分配表。 CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` ( `id` varchar(32) NOT NULL COMMENT 'ID', @@ -85,6 +121,8 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` ( `lineId` varchar(32) DEFAULT NULL COMMENT '线路ID', `lineName` varchar(100) DEFAULT NULL COMMENT '线路名称', `boardingPlace` varchar(100) DEFAULT NULL COMMENT '乘车地点', + `periodId` varchar(32) DEFAULT NULL COMMENT '出行时间段ID', + `periodName` varchar(50) DEFAULT NULL COMMENT '出行时间段名称', `userId` varchar(32) DEFAULT NULL COMMENT '人员ID', `loginName` varchar(50) DEFAULT NULL COMMENT '工号', `userName` varchar(100) DEFAULT NULL COMMENT '姓名', @@ -112,6 +150,7 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` ( KEY `idx_thirty_teach_tour_user_assignment_setting` (`settingId`), KEY `idx_thirty_teach_tour_user_assignment_user` (`userId`), KEY `idx_thirty_teach_tour_user_assignment_union` (`unionId`), + KEY `idx_thirty_teach_tour_user_assignment_period` (`periodId`), KEY `idx_thirty_teach_tour_user_assignment_source` (`assignSource`), KEY `idx_thirty_teach_tour_user_assignment_person_type` (`personType`), KEY `idx_thirty_teach_tour_user_assignment_cancelled` (`cancelled`) diff --git a/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_setting_period_create.sql b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_setting_period_create.sql new file mode 100644 index 00000000..a6b903e1 --- /dev/null +++ b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_setting_period_create.sql @@ -0,0 +1,35 @@ +CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_travel_period` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID', + `periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称', + `startDate` varchar(10) DEFAULT NULL COMMENT '开始日期', + `endDate` varchar(10) DEFAULT NULL COMMENT '结束日期', + `enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_thirty_teach_tour_setting_period_setting` (`settingId`), + KEY `idx_thirty_teach_tour_setting_period_date` (`startDate`, `endDate`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段'; + +CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_period_quota` ( + `id` varchar(32) NOT NULL COMMENT 'ID', + `settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID', + `unionId` varchar(32) DEFAULT NULL COMMENT '分工会ID', + `unionName` varchar(100) DEFAULT NULL COMMENT '分工会名称', + `periodId` varchar(32) DEFAULT NULL COMMENT '时间段ID', + `periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称', + `formalQuota` int DEFAULT 0 COMMENT '正式人员名额', + `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', + `createdAt` bigint DEFAULT NULL COMMENT '创建时间', + `updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人', + `updatedAt` bigint DEFAULT NULL COMMENT '修改时间', + `delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记', + PRIMARY KEY (`id`), + KEY `idx_thirty_teach_tour_period_quota_setting` (`settingId`), + KEY `idx_thirty_teach_tour_period_quota_union` (`unionId`), + KEY `idx_thirty_teach_tour_period_quota_period` (`periodId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段名额'; diff --git a/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_user_assignment_add_period.sql b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_user_assignment_add_period.sql new file mode 100644 index 00000000..5b9659a2 --- /dev/null +++ b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_user_assignment_add_period.sql @@ -0,0 +1,5 @@ +-- 分工会正式人员分配按配置时间段占用名额,人员分配表保留所选时间段快照。 +ALTER TABLE `thirty_teach_tour_user_assignment` + ADD COLUMN `periodId` varchar(32) DEFAULT NULL COMMENT '出行时间段ID' AFTER `boardingPlace`, + ADD COLUMN `periodName` varchar(50) DEFAULT NULL COMMENT '出行时间段名称' AFTER `periodId`, + ADD KEY `idx_thirty_teach_tour_user_assignment_period` (`periodId`); diff --git a/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_user_assignment_create.sql b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_user_assignment_create.sql index 10462d28..da4d71bc 100644 --- a/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_user_assignment_create.sql +++ b/src/main/resources/db/thirtyTeachTour/thirty_teach_tour_user_assignment_create.sql @@ -9,6 +9,9 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` ( `lineId` varchar(32) DEFAULT NULL COMMENT '线路ID', `lineName` varchar(100) DEFAULT NULL COMMENT '线路名称', `boardingPlace` varchar(100) DEFAULT NULL COMMENT '乘车地点', + `photoFiles` text DEFAULT NULL COMMENT '报名图片材料', + `periodId` varchar(32) DEFAULT NULL COMMENT '出行时间段ID', + `periodName` varchar(50) DEFAULT NULL COMMENT '出行时间段名称', `userId` varchar(32) DEFAULT NULL COMMENT '人员ID', `loginName` varchar(50) DEFAULT NULL COMMENT '工号', `userName` varchar(100) DEFAULT NULL COMMENT '姓名', @@ -36,6 +39,7 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` ( KEY `idx_thirty_teach_tour_user_assignment_setting` (`settingId`), KEY `idx_thirty_teach_tour_user_assignment_user` (`userId`), KEY `idx_thirty_teach_tour_user_assignment_union` (`unionId`), + KEY `idx_thirty_teach_tour_user_assignment_period` (`periodId`), KEY `idx_thirty_teach_tour_user_assignment_source` (`assignSource`), KEY `idx_thirty_teach_tour_user_assignment_person_type` (`personType`), KEY `idx_thirty_teach_tour_user_assignment_cancelled` (`cancelled`) diff --git a/src/main/resources/views/platform/zhgh/dayofficework/ThirtyTeachTour/branchUserAssignment/index.html b/src/main/resources/views/platform/zhgh/dayofficework/ThirtyTeachTour/branchUserAssignment/index.html index 65fe4795..358622ad 100644 --- a/src/main/resources/views/platform/zhgh/dayofficework/ThirtyTeachTour/branchUserAssignment/index.html +++ b/src/main/resources/views/platform/zhgh/dayofficework/ThirtyTeachTour/branchUserAssignment/index.html @@ -69,7 +69,13 @@ layout("/layouts/platform.html"){ + + + + - + + + + + + + + + + + + + + + + 取消 + 确定 + + + + + + + + + + + + + + + 取消 + 保存 + + + + + + + 取消 @@ -350,9 +441,24 @@ layout("/layouts/platform.html"){ selectedCandidates: [], selectedCandidateIds: [], assignSubmitting: false, + photoUploadDialogVisible: false, + photoUploadRow: null, + photoUploadForm: { + userId: "", + userName: "", + photoFiles: "" + }, selectMatterDialogVisible: false, selectMatterSubmitting: false, selectMatterOptions: [], + listPhotoDialogVisible: false, + listPhotoSubmitting: false, + listPhotoRow: null, + listPhotoForm: { + id: "", + userName: "", + photoFiles: "" + }, pageForm: { pageNumber: 1, pageSize: 10, @@ -378,13 +484,16 @@ layout("/layouts/platform.html"){ pageSize: 10, totalCount: 0, unionId: "", + summerJoined: "false", + lastThirtyJoined: "false", keyword: "" }, selectMatterForm: { id: "", settingId: "", matterId: "", - userName: "" + userName: "", + photoFiles: "" }, remindForm: { content: "" @@ -415,6 +524,8 @@ layout("/layouts/platform.html"){ pageSize: 10, totalCount: 0, unionId: "", + summerJoined: "false", + lastThirtyJoined: "false", keyword: "" } }, @@ -423,7 +534,8 @@ layout("/layouts/platform.html"){ id: "", settingId: "", matterId: "", - userName: "" + userName: "", + photoFiles: "" } }, defaultRemindForm() { @@ -649,13 +761,16 @@ layout("/layouts/platform.html"){ pageSize: this.candidateForm.pageSize, settingId: this.assignForm.settingId, unionId: this.candidateForm.unionId, + summerJoined: this.candidateForm.summerJoined, + lastThirtyJoined: this.candidateForm.lastThirtyJoined, keyword: (this.selectedCandidateIds || []).length > 0 ? "" : this.candidateForm.keyword, userIds: JSON.stringify(this.selectedCandidateIds || []) }).then((res) => { if (res.code === 0) { const data = res.data || {} this.candidateData = (data.list || []).map(item => Object.assign({}, item, { - assignmentMatterId: "" + assignmentMatterId: "", + photoFiles: "" })) this.candidateForm.totalCount = data.totalCount || 0 this.mergeCandidateOptions(this.candidateData) @@ -678,6 +793,8 @@ layout("/layouts/platform.html"){ }, resetCandidateSearch() { this.candidateForm.unionId = "" + this.candidateForm.summerJoined = "false" + this.candidateForm.lastThirtyJoined = "false" this.candidateForm.keyword = "" this.candidateForm.pageNumber = 1 this.selectedCandidateIds = [] @@ -723,6 +840,8 @@ layout("/layouts/platform.html"){ pageSize: 20, settingId: this.assignForm.settingId, unionId: this.candidateForm.unionId, + summerJoined: this.candidateForm.summerJoined, + lastThirtyJoined: this.candidateForm.lastThirtyJoined, keyword: keyword || "" }).then((res) => { if (res.code === 0) { @@ -758,6 +877,77 @@ layout("/layouts/platform.html"){ isCandidateSelected(row) { return !!row && this.selectedCandidates.some(item => item.userId === row.userId) }, + openPhotoUpload(row) { + if (!this.isCandidateSelected(row)) { + this.$message.warning("请先勾选人员") + return + } + this.photoUploadRow = row + // 行级图片只是暂存到候选人员对象中,再次打开时从当前行回显。 + this.photoUploadForm = { + userId: row.userId || "", + userName: row.userName || "", + photoFiles: row.photoFiles || "" + } + this.photoUploadDialogVisible = true + }, + confirmPhotoUpload() { + if (this.photoUploadRow) { + this.$set(this.photoUploadRow, "photoFiles", this.photoUploadForm.photoFiles || "") + } + this.photoUploadDialogVisible = false + }, + resetPhotoUploadDialog() { + this.photoUploadRow = null + this.photoUploadForm = { + userId: "", + userName: "", + photoFiles: "" + } + }, + openListPhotoUpload(row) { + this.listPhotoRow = row + // 主列表上传读取当前行 photoFiles;人员分配弹窗已保存过图片时可直接回显。 + this.listPhotoForm = { + id: row.id || "", + userName: row.userName || "", + photoFiles: row.photoFiles || "" + } + this.listPhotoDialogVisible = true + }, + resetListPhotoUploadDialog() { + this.listPhotoSubmitting = false + this.listPhotoRow = null + this.listPhotoForm = { + id: "", + userName: "", + photoFiles: "" + } + }, + saveListPhotoFiles() { + if (!this.listPhotoForm.id) { + this.$message.warning("人员分配记录不存在") + return + } + this.listPhotoSubmitting = true + this.$axios.post(loc() + "/updatePhotoFiles", { + id: this.listPhotoForm.id, + photoFiles: this.listPhotoForm.photoFiles || "" + }).then((res) => { + if (res.code === 0) { + if (this.listPhotoRow) { + this.$set(this.listPhotoRow, "photoFiles", this.listPhotoForm.photoFiles || "") + } + this.$message.success("图片材料保存成功") + this.listPhotoDialogVisible = false + this.doSearch() + } else { + this.$message.warning(res.msg || "图片材料保存失败") + } + }).finally(() => { + this.listPhotoSubmitting = false + }) + }, clearCandidateSelection() { this.selectedCandidates = [] if (this.$refs.candidateTable) { @@ -779,7 +969,8 @@ layout("/layouts/platform.html"){ const assignItems = this.selectedCandidates.map(item => { return { userId: item.userId, - matterId: item.assignmentMatterId || this.assignForm.matterId || "" + matterId: item.assignmentMatterId || this.assignForm.matterId || "", + photoFiles: item.photoFiles || "" } }) this.assignSubmitting = true @@ -806,7 +997,8 @@ layout("/layouts/platform.html"){ id: row.id, settingId: row.settingId, matterId: "", - userName: row.userName || "" + userName: row.userName || "", + photoFiles: "" } this.selectMatterOptions = [] this.selectMatterDialogVisible = true @@ -834,7 +1026,8 @@ layout("/layouts/platform.html"){ this.selectMatterSubmitting = true this.$axios.post(loc() + "/selectMatter", { id: this.selectMatterForm.id, - matterId: this.selectMatterForm.matterId + matterId: this.selectMatterForm.matterId, + photoFiles: this.selectMatterForm.photoFiles }).then((res) => { if (res.code === 0) { const data = res.data || {} @@ -895,6 +1088,12 @@ layout("/layouts/platform.html"){ isCancelled(row) { return row && (row.cancelled === true || row.cancelled === 1) }, + photoFileCount(row) { + if (!row || !row.photoFiles) { + return 0 + } + return String(row.photoFiles).split(",").map(item => item.trim()).filter(item => item).length + }, restoreCancel(row) { this.$confirm("确定将【" + row.userName + "】恢复为未退出状态吗?", "提示", { confirmButtonText: "确定", diff --git a/src/main/resources/views/platform/zhgh/dayofficework/ThirtyTeachTour/setting/index.html b/src/main/resources/views/platform/zhgh/dayofficework/ThirtyTeachTour/setting/index.html index b0b2cc55..cc0fd448 100644 --- a/src/main/resources/views/platform/zhgh/dayofficework/ThirtyTeachTour/setting/index.html +++ b/src/main/resources/views/platform/zhgh/dayofficework/ThirtyTeachTour/setting/index.html @@ -347,7 +347,7 @@ layout("/layouts/platform.html"){ {{ branchTotalQuota }}
- 分工会总会员数 + 分工会可参加总人数 {{ branchTotalMemberCount }}
- +