From 72cdae177ef81a16ef979ff795f7090402f408b2 Mon Sep 17 00:00:00 2001 From: zhangrui Date: Wed, 17 Jun 2026 17:31:34 +0800 Subject: [PATCH] 1 --- .../sys/services/impl/SysUserServiceImpl.java | 4 +- .../ActivityBasicScopeController.java | 4 +- .../tour/controller/TourGroupController.java | 127 +++- .../tour/controller/TourLineController.java | 92 ++- .../tour/controller/TourMatterController.java | 178 +++++- .../controller/TourMySignupController.java | 44 -- .../controller/TourSettingController.java | 4 - .../tour/controller/TourSignupController.java | 58 +- .../controller/TourUndertakeController.java | 196 +++--- .../dayofficework/tour/models/TourLedger.java | 26 + .../dayofficework/tour/models/TourLine.java | 4 +- .../tour/service/TourLedgerService.java | 51 ++ .../tour/service/TourMatterService.java | 12 + .../service/impl/TourLedgerServiceImpl.java | 591 ++++++++++++++++++ .../service/impl/TourMatterServiceImpl.java | 22 + .../db/tour_ledger_add_group_info.sql | 7 + .../db/tour_ledger_add_undertake_union.sql | 5 + .../db/tour_line_line_code_to_int.sql | 9 + .../zhgh/dayofficework/Tour/group/index.html | 295 +++++++-- .../zhgh/dayofficework/Tour/ledger/index.html | 10 + .../zhgh/dayofficework/Tour/matter/index.html | 147 ++++- .../zhgh/dayofficework/Tour/route/index.html | 29 +- .../dayofficework/Tour/setting/index.html | 11 - .../zhgh/dayofficework/Tour/signup/index.html | 4 +- .../dayofficework/Tour/undertake/index.html | 110 ++-- .../dayofficework/tour/signup/list.html | 2 +- 26 files changed, 1676 insertions(+), 366 deletions(-) create mode 100644 src/main/resources/db/tour_ledger_add_group_info.sql create mode 100644 src/main/resources/db/tour_ledger_add_undertake_union.sql create mode 100644 src/main/resources/db/tour_line_line_code_to_int.sql diff --git a/src/main/java/com/budwk/app/sys/services/impl/SysUserServiceImpl.java b/src/main/java/com/budwk/app/sys/services/impl/SysUserServiceImpl.java index 7b332c8..21eb8c7 100644 --- a/src/main/java/com/budwk/app/sys/services/impl/SysUserServiceImpl.java +++ b/src/main/java/com/budwk/app/sys/services/impl/SysUserServiceImpl.java @@ -309,9 +309,9 @@ public class SysUserServiceImpl extends BaseServiceImpl implements Sys String decodePwd = Base64Decoder.decodeStr(passowrd); String hashedPassword = PwdUtil.getPassword(decodePwd, user.getSalt()); if (!Strings.sNull(hashedPassword).equalsIgnoreCase(user.getPassword())) { - if (Globals.sso) { +// if (Globals.sso) { throw new BaseException("用户名或者密码不正确"); - } +// } } user = this.fetchLinks(user, "unit"); if (Lang.isNotEmpty(user.getUnit()) && StrUtil.isNotBlank(user.getUnit().getUnionId())) { diff --git a/src/main/java/com/budwk/app/zhgh/activity/basic/controller/ActivityBasicScopeController.java b/src/main/java/com/budwk/app/zhgh/activity/basic/controller/ActivityBasicScopeController.java index fb19ae9..0b6e941 100644 --- a/src/main/java/com/budwk/app/zhgh/activity/basic/controller/ActivityBasicScopeController.java +++ b/src/main/java/com/budwk/app/zhgh/activity/basic/controller/ActivityBasicScopeController.java @@ -115,7 +115,7 @@ public class ActivityBasicScopeController { """); try { Cnd cnd = getCnd(pageForm); - cnd.and("u.member", "=", 1); +// cnd.and("u.member", "=", 1); sql.setCondition(cnd); Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); return Result.success(pagination); @@ -393,7 +393,7 @@ public class ActivityBasicScopeController { throw new RuntimeException(e); } - cnd.and("u.member", "=", 1); +// cnd.and("u.member", "=", 1); return cnd; } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourGroupController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourGroupController.java index d67bc2b..65f7631 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourGroupController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourGroupController.java @@ -169,16 +169,53 @@ public class TourGroupController { return Result.success(sql.getList(NutMap.class)); } + /** + * 查询当前事项线路下已经写入疗休养台账的成团信息,供线路成团查看页展示团标签。 + */ @At @SaCheckPermission("tour.group") - public Result signupPageData(PageForm pageForm, String matterId, String keyword, String unionId) { + public Result groupOptions(String matterId) { + if (StrUtil.isBlank(matterId)) { + return Result.error("参数错误"); + } + Cnd cnd = buildGroupOptionCnd(matterId); + return Result.success(tourLedgerService.queryGroupOptions(cnd)); + } + + /** + * 自动将当前事项线路下未成团的报名人员按最大成团人数分配成团。 + */ + @At + @SaCheckPermission("tour.group") + public Result assignGroup(String matterId) { + if (StrUtil.isBlank(matterId)) { + return Result.error("参数错误"); + } + return tourLedgerService.assignGroup(matterId); + } + + /** + * 将勾选的教职工报名台账调整到指定成团中,家属随教职工一起调整。 + */ + @At + @SaCheckPermission("tour.group") + public Result adjustGroup(String matterId, String groupCode, String ledgerIds) { + if (StrUtil.isBlank(matterId) || StrUtil.isBlank(groupCode) || StrUtil.isBlank(ledgerIds)) { + return Result.error("参数错误"); + } + return tourLedgerService.adjustGroup(matterId, groupCode, ledgerIds); + } + + @At + @SaCheckPermission("tour.group") + public Result signupPageData(PageForm pageForm, String matterId, String keyword, String unionId, String groupCode) { if (StrUtil.isBlank(matterId)) { return Result.error("参数错误"); } if (StrUtil.isBlank(pageForm.getPageOrderName())) { - pageForm.defaultSortDesc("signupTime"); + pageForm.defaultSortAsc("groupCode"); } - Cnd cnd = buildSignupQueryCnd(matterId, keyword, unionId); + Cnd cnd = buildSignupQueryCnd(matterId, keyword, unionId, groupCode); Sql countSql = Sqls.create(""" SELECT COUNT(1) FROM tour_ledger t @@ -196,16 +233,27 @@ public class TourGroupController { t.id, t.jobNo, t.userName, + t.gender, '' AS mobile, t.idCard, t.unionId, t.unionName, - t.signupTime + t.signupTime, + t.groupCode, + t.groupName, + IFNULL(f.familyCount, 0) AS familyCount, + 1 + IFNULL(f.familyCount, 0) AS totalPeople FROM tour_ledger t INNER JOIN tour_matter m ON m.id = t.matterId INNER JOIN tour_line l ON l.id = m.lineId + LEFT JOIN ( + SELECT ledgerId, COUNT(1) AS familyCount + FROM tour_ledger_family + WHERE delFlag = 0 + GROUP BY ledgerId + ) f ON f.ledgerId = t.id $condition - ORDER BY $orderColumn $orderBy, t.signupTime DESC, t.createdAt DESC + ORDER BY $orderColumn $orderBy, t.signupTime ASC, t.createdAt ASC """); listSql.setCondition(cnd); listSql.setVar("orderColumn", getSignupOrderColumn(pageForm.getPageOrderName())); @@ -216,11 +264,30 @@ public class TourGroupController { List list = listSql.getList(NutMap.class); fillSignupMobile(list); - fillSignupFamilies(list); + flattenSignupFamilies(list); Pagination pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list); return Result.success(pagination); } + /** + * 构造成团标签查询条件:只取当前事项线路已分配成团编码的台账,并保持与列表一致的数据权限。 + */ + private Cnd buildGroupOptionCnd(String matterId) { + Cnd cnd = Cnd.NEW(); + cnd.and("t.delFlag", "=", false); + cnd.and("t.matterId", "=", matterId); + cnd.and("t.groupCode", "IS NOT", null); + cnd.and("t.groupCode", "<>", ""); + cnd.and("m.delFlag", "=", false); + cnd.and("m.enabled", "=", true); + cnd.and("m.lineId", "IS NOT", null); + cnd.and("m.lineId", "<>", ""); + cnd.and("l.delFlag", "=", false); + cnd.and("l.enabled", "=", true); + applyMatterDataScope(cnd); + return cnd; + } + @At @Ok("void") @SaCheckPermission("tour.group") @@ -238,7 +305,7 @@ public class TourGroupController { CommonDownloadUtil.download(lineName + "参加人员名单.xls", workbook, response); } - private Cnd buildSignupQueryCnd(String matterId, String keyword, String unionId) { + private Cnd buildSignupQueryCnd(String matterId, String keyword, String unionId, String groupCode) { Cnd cnd = Cnd.NEW(); cnd.and("t.delFlag", "=", false); cnd.and("t.matterId", "=", matterId); @@ -255,6 +322,8 @@ public class TourGroupController { cnd.and(keywordGroup); } cnd.andEX("t.unionId", "=", unionId); + // 团标签联动查询时只筛选当前团;未选择团标签时默认查询全部报名人员。 + cnd.andEX("t.groupCode", "=", groupCode); applyMatterDataScope(cnd); return cnd; } @@ -297,7 +366,7 @@ public class TourGroupController { } private List queryExportParticipants(String matterId) { - Cnd cnd = buildSignupQueryCnd(matterId, null, null); + Cnd cnd = buildSignupQueryCnd(matterId, null, null, null); Sql sql = Sqls.create(""" SELECT t.id AS ledgerId, @@ -515,9 +584,9 @@ public class TourGroupController { } /** - * 查看成团报名人员时按台账ID带出家属子列表,避免前端逐行请求家属信息。 + * 查看成团报名人员时将教职工和家属平铺在同一个列表,便于前端统一展示。 */ - private void fillSignupFamilies(List list) { + private void flattenSignupFamilies(List list) { if (list == null || list.isEmpty()) { return; } @@ -535,8 +604,9 @@ public class TourGroupController { Sql sql = Sqls.create(""" SELECT ledgerId, - staffJobNo AS jobNo, + '' AS jobNo, familyName AS userName, + gender, '' AS mobile, idCard, relationship @@ -551,11 +621,35 @@ public class TourGroupController { Map> familyMap = new HashMap<>(); for (NutMap family : sql.getList(NutMap.class)) { + family.put("staffRow", false); + family.put("selectable", false); + family.put("rowTypeName", "家属"); + family.put("unionName", ""); + family.put("groupCode", ""); + family.put("groupName", ""); + family.put("totalPeople", ""); familyMap.computeIfAbsent(family.getString("ledgerId", ""), key -> new ArrayList<>()).add(family); } + List flatList = new ArrayList<>(); for (NutMap item : list) { - item.put("families", familyMap.getOrDefault(item.getString("id", ""), List.of())); + // 教职工是后续人员调整的最小可选单位,家属随教职工一起分配。 + item.put("staffRow", true); + item.put("selectable", true); + item.put("rowTypeName", "教职工"); + item.put("relationship", ""); + item.put("relatedStaffName", item.getString("userName", "")); + flatList.add(item); + List families = familyMap.getOrDefault(item.getString("id", ""), List.of()); + for (NutMap family : families) { + // 家属行通过关联教职工名称标明所属教职工,便于单层列表中识别家庭关系。 + family.put("relatedStaffName", item.getString("userName", "")); + family.put("groupCode", item.getString("groupCode", "")); + family.put("groupName", item.getString("groupName", "")); + } + flatList.addAll(families); } + list.clear(); + list.addAll(flatList); } private Cnd buildQueryCnd(Integer year, String lineName, String lineType, String unionId) { @@ -662,12 +756,21 @@ public class TourGroupController { if ("userName".equals(orderName)) { return "t.userName"; } + if ("gender".equals(orderName)) { + return "t.gender"; + } if ("idCard".equals(orderName)) { return "t.idCard"; } if ("unionName".equals(orderName)) { return "t.unionName"; } + if ("groupCode".equals(orderName) || "groupName".equals(orderName)) { + return "t.groupCode"; + } + if ("totalPeople".equals(orderName)) { + return "totalPeople"; + } return "t.signupTime"; } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourLineController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourLineController.java index f2085e6..11e0c9e 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourLineController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourLineController.java @@ -1,5 +1,9 @@ package com.budwk.app.zhgh.dayofficework.tour.controller; +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 cn.dev33.satoken.annotation.SaCheckPermission; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.annotation.SLog; @@ -7,6 +11,7 @@ import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; +import com.budwk.app.base.utils.CommonDownloadUtil; import com.budwk.app.sys.models.Sys_dict; import com.budwk.app.sys.services.SysDictService; import com.budwk.app.sys.views.View_user; @@ -26,7 +31,10 @@ import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; +import org.apache.poi.ss.usermodel.Workbook; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -36,8 +44,6 @@ import java.util.stream.Collectors; @At("/platform/tour/route") public class TourLineController { - private static final String SORT_CODE_PATTERN = "^\\d+$"; - @Inject private TourLineService tourLineService; @@ -52,7 +58,7 @@ public class TourLineController { @At @SaCheckPermission("tour.route") - public Result pageData(PageForm pageForm, Integer year, String lineName, String lotId) { + public Result pageData(PageForm pageForm, Integer year, String lineName, String lotId, String lineType) { if (StrUtil.isBlank(pageForm.getPageOrderName())) { pageForm.defaultSortAsc("lineCode"); } @@ -62,6 +68,8 @@ public class TourLineController { cnd.andEX("l.`year`", "=", year); cnd.and(Cnd.likeEX("l.lineName", lineName)); cnd.andEX("l.lotId", "=", lotId); + // 线路类型查询使用线路表保存的字典名称,和新增/编辑线路时的取值保持一致。 + cnd.andEX("l.lineType", "=", lineType); boolean sysAdmin = AuthUtil.hasRole(RoleConstant.SYSADMIN.name()); boolean branchUnionScope = !sysAdmin && AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()); applyDataScope(cnd, sysAdmin, branchUnionScope); @@ -107,6 +115,78 @@ public class TourLineController { return Result.success(pagination); } + /** + * 导出线路关联事项数据,导出范围复用线路管理列表的筛选条件和数据权限。 + */ + @At + @Ok("void") + @SaCheckPermission("tour.route") + public void exportData(Integer year, String lineName, String lotId, String lineType, HttpServletResponse response) { + Cnd cnd = Cnd.NEW(); + cnd.and("l.delFlag", "=", false); + cnd.andEX("l.`year`", "=", year); + cnd.and(Cnd.likeEX("l.lineName", lineName)); + cnd.andEX("l.lotId", "=", lotId); + // 导出范围复用列表筛选条件,线路类型也要同步过滤。 + cnd.andEX("l.lineType", "=", lineType); + boolean sysAdmin = AuthUtil.hasRole(RoleConstant.SYSADMIN.name()); + boolean branchUnionScope = !sysAdmin && AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()); + applyDataScope(cnd, sysAdmin, branchUnionScope); + String creatorJoin = branchUnionScope ? """ + LEFT JOIN sys_unit lineUnit ON lineUnit.id = l.unitId + LEFT JOIN sys_user creatorUser ON creatorUser.id = COALESCE(NULLIF(l.creatorUserId, ''), l.createdBy) + LEFT JOIN sys_unit creatorUnit ON creatorUnit.id = creatorUser.unitId + """ : ""; + + // 线路导出需要带出事项信息,因此以线路为主表左关联事项,未关联事项的线路仍保留导出。 + Sql sql = Sqls.create(""" + SELECT + l.`year`, + l.lineName, + lot.lotName, + l.lineType, + m.matterName, + CASE + WHEN IFNULL(m.signupStartTime, '') <> '' AND IFNULL(m.signupEndTime, '') <> '' + THEN CONCAT(m.signupStartTime, ' 至 ', m.signupEndTime) + ELSE '' + END AS signupTime, + CASE + WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> '' + THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime) + ELSE '' + END AS travelTime, + m.contactName, + m.contactPhone + FROM tour_line l + LEFT JOIN tour_setting_lot lot ON lot.id = l.lotId + LEFT JOIN tour_matter m ON l.id = m.lineId AND m.delFlag = 0 + $creatorJoin + $condition + ORDER BY CAST(l.lineCode AS UNSIGNED) ASC, l.lineCode ASC, m.matterName ASC, l.lineName ASC + """); + sql.setVar("creatorJoin", creatorJoin); + sql.setCondition(cnd); + sql.setCallback(Sqls.callback.maps()); + tourLineService.dao().execute(sql); + + List entities = new ArrayList<>(); + entities.add(new ExcelExportEntity("创建年度", "year", 14)); + entities.add(new ExcelExportEntity("线路名称", "lineName", 30)); + entities.add(new ExcelExportEntity("时间标段", "lotName", 18)); + entities.add(new ExcelExportEntity("路线类型", "lineType", 18)); + entities.add(new ExcelExportEntity("事项名称", "matterName", 30)); + entities.add(new ExcelExportEntity("报名时间", "signupTime", 34)); + entities.add(new ExcelExportEntity("出行时间", "travelTime", 24)); + entities.add(new ExcelExportEntity("联系人", "contactName", 16)); + entities.add(new ExcelExportEntity("联系方式", "contactPhone", 18)); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, sql.getList(NutMap.class)); + CommonDownloadUtil.download("线路事项数据.xlsx", workbook, response); + } + @At @SaCheckPermission("tour.route") public Result detail(String id) { @@ -253,11 +333,11 @@ public class TourLineController { if (StrUtil.isBlank(line.getLineName())) { return Result.error("线路名称不能为空"); } - if (StrUtil.isBlank(line.getLineCode())) { + if (line.getLineCode() == null) { return Result.error("排序编号不能为空"); } - if (!line.getLineCode().matches(SORT_CODE_PATTERN)) { - return Result.error("排序编号必须为整数"); + if (line.getLineCode() < 0) { + return Result.error("排序编号不能小于0"); } if (StrUtil.isBlank(line.getLineType())) { return Result.error("线路类型不能为空"); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMatterController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMatterController.java index 5ca291e..7198ebf 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMatterController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMatterController.java @@ -1,12 +1,17 @@ package com.budwk.app.zhgh.dayofficework.tour.controller; import cn.dev33.satoken.annotation.SaCheckPermission; +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 cn.hutool.core.util.StrUtil; import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.param.PageForm; import com.budwk.app.base.result.Result; +import com.budwk.app.base.utils.CommonDownloadUtil; import com.budwk.app.sys.models.Sys_dict; import com.budwk.app.sys.models.Sys_union; import com.budwk.app.sys.services.SysDictService; @@ -27,7 +32,10 @@ import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.util.NutMap; import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.Ok; +import org.apache.poi.ss.usermodel.Workbook; +import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -53,18 +61,13 @@ public class TourMatterController { @At @SaCheckPermission("tour.matter") - public Result pageData(PageForm pageForm, Integer year, String matterName, String unionId, String organizationType) { - Cnd cnd = Cnd.NEW(); - cnd.and("m.delFlag", "=", false); - cnd.andEX("m.`year`", "=", year); - cnd.and(Cnd.likeEX("m.matterName", matterName)); - cnd.andEX("m.unionId", "=", unionId); - cnd.andEX("m.organizationType", "=", organizationType); - applyMatterDataScope(cnd); + public Result pageData(PageForm pageForm, Integer year, String matterName, String unionId, String organizationType, String lineType) { + Cnd cnd = buildMatterPageCnd(year, matterName, unionId, organizationType, lineType); Sql countSql = Sqls.create(""" SELECT COUNT(1) FROM tour_matter m + LEFT JOIN tour_line l ON l.id = m.lineId $condition """); countSql.setCondition(cnd); @@ -91,7 +94,7 @@ public class TourMatterController { LEFT JOIN sys_dict d ON d.`code` = m.organizationType AND d.parentId = (SELECT id FROM sys_dict WHERE `code` = 'organizationType' LIMIT 1) $condition - ORDER BY $orderColumn $orderBy, m.`year` DESC, m.createdAt DESC + ORDER BY $orderColumn $orderBy, CAST(l.lineCode AS UNSIGNED) ASC, m.`year` DESC, m.createdAt DESC """); listSql.setCondition(cnd); listSql.setVar("orderColumn", getOrderColumn(pageForm.getPageOrderName())); @@ -104,6 +107,109 @@ public class TourMatterController { return Result.success(pagination); } + /** + * 导出疗休养事项列表,导出范围复用页面查询条件和事项数据权限。 + */ + @At + @Ok("void") + @SaCheckPermission("tour.matter") + public void exportData(Integer year, String matterName, String unionId, String organizationType, String lineType, + HttpServletResponse response) { + Cnd cnd = buildMatterPageCnd(year, matterName, unionId, organizationType, lineType); + Sql sql = Sqls.create(""" + SELECT + m.`year`, + m.matterName, + l.lineName, + CASE + WHEN IFNULL(m.signupStartTime, '') <> '' AND IFNULL(m.signupEndTime, '') <> '' + THEN CONCAT(m.signupStartTime, ' 至 ', m.signupEndTime) + ELSE '' + END AS signupTime, + CASE + WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> '' + THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime) + ELSE '' + END AS travelTime, + d.name AS organizationTypeName, + m.contactName, + m.contactPhone, + CASE WHEN m.enabled = 1 THEN '启用' ELSE '禁用' END AS enabledName + FROM tour_matter m + LEFT JOIN tour_line l ON l.id = m.lineId + LEFT JOIN sys_union u ON u.id = m.unionId + LEFT JOIN sys_dict d ON d.`code` = m.organizationType + AND d.parentId = (SELECT id FROM sys_dict WHERE `code` = 'organizationType' LIMIT 1) + $condition + ORDER BY CAST(l.lineCode AS UNSIGNED) ASC, m.`year` DESC, m.createdAt DESC + """); + sql.setCondition(cnd); + sql.setCallback(Sqls.callback.maps()); + tourMatterService.dao().execute(sql); + + List rows = sql.getList(NutMap.class); + for (int i = 0; i < rows.size(); i++) { + rows.get(i).put("index", i + 1); + } + + List entities = new ArrayList<>(); + entities.add(new ExcelExportEntity("序号", "index", 8)); + entities.add(new ExcelExportEntity("年度", "year", 12)); + entities.add(new ExcelExportEntity("事项名称", "matterName", 30)); + entities.add(new ExcelExportEntity("线路名称", "lineName", 30)); + entities.add(new ExcelExportEntity("报名时间", "signupTime", 34)); + entities.add(new ExcelExportEntity("出行时间", "travelTime", 24)); + entities.add(new ExcelExportEntity("组织形式", "organizationTypeName", 16)); + entities.add(new ExcelExportEntity("联系人", "contactName", 16)); + entities.add(new ExcelExportEntity("联系方式", "contactPhone", 18)); + entities.add(new ExcelExportEntity("事项状态", "enabledName", 12)); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, rows); + CommonDownloadUtil.download("疗休养事项数据.xlsx", workbook, response); + } + + /** + * 按当前事项列表查询条件统一赋值报名时间,更新前端筛选范围内的全部事项。 + */ + @At + @Aop(TransAop.READ_COMMITTED) + @SaCheckPermission("tour.matter") + @SLog(type = "tour", tag = "疗休养事项", msg = "统一赋值报名时间") + public Result assignSignupTime(Integer year, String matterName, String unionId, String organizationType, String lineType, + String signupStartTime, String signupEndTime) { + if (StrUtil.isBlank(signupStartTime) || StrUtil.isBlank(signupEndTime)) { + return Result.error("报名开始时间和报名结束时间不能为空"); + } + if (signupStartTime.compareTo(signupEndTime) >= 0) { + return Result.error("报名开始时间必须小于报名结束时间"); + } + + // 批量赋值必须严格复用事项列表查询条件,确保“查询哪些数据就赋值哪些事项”。 + Cnd cnd = buildMatterPageCnd(year, matterName, unionId, organizationType, lineType); + Sql idSql = Sqls.create(""" + SELECT m.id + FROM tour_matter m + LEFT JOIN tour_line l ON l.id = m.lineId + $condition + """); + idSql.setCondition(cnd); + idSql.setCallback(Sqls.callback.maps()); + tourMatterService.dao().execute(idSql); + List ids = idSql.getList(NutMap.class).stream() + .map(item -> item.getString("id")) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toList()); + if (ids.isEmpty()) { + return Result.success("当前查询条件下暂无可赋值事项"); + } + + // 只更新报名时间字段,不影响事项的线路、联系人、成团人数等其他配置。 + int updateCount = tourMatterService.assignSignupTime(ids, signupStartTime, signupEndTime); + return Result.success("赋值成功,共更新" + updateCount + "条事项"); + } + @At @SaCheckPermission("tour.matter") public Result detail(String id) { @@ -142,17 +248,20 @@ public class TourMatterController { cnd.and("l.enabled", "=", true); cnd.and("l.openFlag", "=", true); + // 事项选择线路下拉需要展示线路类型和旅行社,因此这里补充旅行社名称返回给前端拼接显示。 Sql sql = Sqls.create(""" SELECT l.*, + a.agencyName AS travelAgencyName, lot.lotName AS lotName, lot.lotValue AS lotValue, lot.lotValue AS lotDays, lot.activityCost AS activityCost FROM tour_line l + LEFT JOIN tour_travel_agency a ON a.id = l.travelAgencyId LEFT JOIN tour_setting_lot lot ON lot.id = l.lotId $condition - ORDER BY l.lineCode ASC, l.lineName ASC + ORDER BY CAST(l.lineCode AS UNSIGNED) ASC, l.lineName ASC """); sql.setCondition(cnd); sql.setCallback(Sqls.callback.maps()); @@ -199,6 +308,17 @@ public class TourMatterController { return Result.success(list.stream().filter(item -> !item.isDisabled()).collect(Collectors.toList())); } + @At + @SaCheckPermission("tour.matter") + public Result lineTypeOptions() { + // 事项列表线路类型查询复用线路管理的 lineType 字典,保证筛选值与线路保存值一致。 + List list = sysDictService.getSubListByCode("lineType"); + if (list == null) { + return Result.success(Collections.emptyList()); + } + return Result.success(list.stream().filter(item -> !item.isDisabled()).collect(Collectors.toList())); + } + @At @Aop(TransAop.READ_COMMITTED) @SaCheckPermission("tour.matter") @@ -348,16 +468,14 @@ public class TourMatterController { if (setting == null) { return; } - if (setting.getMinGroupPeople() != null) { + // 配置只作为弹窗缺省值,不能覆盖用户在选择线路页面已经提交的成团人数和报名时间。 + if (matter.getMinGroupPeople() == null && setting.getMinGroupPeople() != null) { matter.setMinGroupPeople(setting.getMinGroupPeople()); } - if (setting.getMaxGroupPeople() != null) { - matter.setMaxGroupPeople(setting.getMaxGroupPeople()); - } - if (StrUtil.isNotBlank(setting.getSignupStartTime())) { + if (StrUtil.isBlank(matter.getSignupStartTime()) && StrUtil.isNotBlank(setting.getSignupStartTime())) { matter.setSignupStartTime(setting.getSignupStartTime()); } - if (StrUtil.isNotBlank(setting.getSignupEndTime())) { + if (StrUtil.isBlank(matter.getSignupEndTime()) && StrUtil.isNotBlank(setting.getSignupEndTime())) { matter.setSignupEndTime(setting.getSignupEndTime()); } } @@ -429,12 +547,6 @@ public class TourMatterController { if (matter.getMinGroupPeople() == null || matter.getMinGroupPeople() <= 0) { return Result.error("最少成团人数必须大于0"); } - if (matter.getMaxGroupPeople() == null || matter.getMaxGroupPeople() <= 0) { - return Result.error("最多成团人数必须大于0"); - } - if (matter.getMinGroupPeople() > matter.getMaxGroupPeople()) { - return Result.error("最少成团人数不能大于最多成团人数"); - } if (matter.getEstimatedCost() == null || matter.getEstimatedCost().signum() < 0) { return Result.error("预计费用不能小于0"); } @@ -462,6 +574,22 @@ public class TourMatterController { .and(TourLedger::getMatterId, "=", matterId)); } + /** + * 构建事项列表查询条件,批量赋值报名时间时复用该条件保证更新范围与列表查询一致。 + */ + private Cnd buildMatterPageCnd(Integer year, String matterName, String unionId, String organizationType, String lineType) { + Cnd cnd = Cnd.NEW(); + cnd.and("m.delFlag", "=", false); + cnd.andEX("m.`year`", "=", year); + cnd.and(Cnd.likeEX("m.matterName", matterName)); + cnd.andEX("m.unionId", "=", unionId); + cnd.andEX("m.organizationType", "=", organizationType); + // 事项列表和批量赋值报名时间共用该条件,按线路类型筛选时必须关联线路表字段。 + cnd.andEX("l.lineType", "=", lineType); + applyMatterDataScope(cnd); + return cnd; + } + /** * 事项数据范围:SYSADMIN 查看全部;分工会主席查看本工会事项;其它角色仅查看自己创建的事项。 */ @@ -512,6 +640,10 @@ public class TourMatterController { if ("lineName".equals(orderName)) { return "l.lineName"; } + if ("lineCode".equals(orderName)) { + // 线路序号按数字排序,避免 10 排在 2 前面。 + return "CAST(l.lineCode AS UNSIGNED)"; + } if ("settingName".equals(orderName)) { return "s.configName"; } @@ -521,6 +653,6 @@ public class TourMatterController { if ("enabled".equals(orderName)) { return "m.enabled"; } - return "m.`year`"; + return "CAST(l.lineCode AS UNSIGNED)"; } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMySignupController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMySignupController.java index 44bba94..be30bc4 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMySignupController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMySignupController.java @@ -444,10 +444,6 @@ public class TourMySignupController { if (ruleResult != null) { return ruleResult; } - Result maxPeopleResult = checkMaxGroupPeople(matter, oldLedger, familyList); - if (maxPeopleResult != null) { - return maxPeopleResult; - } TourLine line = tourLedgerService.dao().fetch(TourLine.class, matter.getLineId()); boolean directFamilyLine = line != null && Boolean.TRUE.equals(line.getDirectFamilyUnitLine()); TourSetting setting = tourLedgerService.dao().fetch(TourSetting.class, matter.getSettingId()); @@ -985,46 +981,6 @@ public class TourMySignupController { && idCard.matches("^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dX]$"); } - private Result checkMaxGroupPeople(TourMatter matter, TourLedger oldLedger, List familyList) { - if (matter == null || matter.getMaxGroupPeople() == null || matter.getMaxGroupPeople() <= 0) { - return null; - } - int currentPeople = 1 + (Lang.isEmpty(familyList) ? 0 : familyList.size()); - int signedPeople = countSignupPeople(matter.getId(), oldLedger == null ? null : oldLedger.getId()); - int totalPeople = signedPeople + currentPeople; - if (totalPeople > matter.getMaxGroupPeople()) { - return Result.error("当前报名人数 " + signedPeople + " 人,本次报名 " + currentPeople - + " 人,最多成团人数 " + matter.getMaxGroupPeople() + " 人,报名后将超过人数上限,不能提交"); - } - return null; - } - - private int countSignupPeople(String matterId, String excludeLedgerId) { - if (StrUtil.isBlank(matterId)) { - return 0; - } - String excludeSql = StrUtil.isBlank(excludeLedgerId) ? "" : " AND t.id <> @excludeLedgerId"; - Sql sql = Sqls.create(""" - SELECT IFNULL(SUM(1 + IFNULL(f.familyCount, 0)), 0) AS signupPeople - FROM tour_ledger t - LEFT JOIN ( - SELECT ledgerId, COUNT(1) AS familyCount - FROM tour_ledger_family - WHERE delFlag = 0 - GROUP BY ledgerId - ) f ON f.ledgerId = t.id - WHERE t.delFlag = 0 - AND t.matterId = @matterId - """ + excludeSql); - sql.setParam("matterId", matterId); - if (StrUtil.isNotBlank(excludeLedgerId)) { - sql.setParam("excludeLedgerId", excludeLedgerId); - } - sql.setCallback(Sqls.callback.integer()); - tourLedgerService.dao().execute(sql); - return sql.getInt(); - } - private String buildSelectedLineMessage(TourLedger ledger) { String selectedLineName = getLedgerLineName(ledger); return "您已经选择【" + selectedLineName + "】,取消后再重新选择。"; diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSettingController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSettingController.java index 2ad00ce..57fa7cf 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSettingController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSettingController.java @@ -205,10 +205,6 @@ public class TourSettingController { if (StrUtil.isBlank(tourSetting.getConfigName())) { return Result.error("配置名称不能为空"); } - if (tourSetting.getMinGroupPeople() != null && tourSetting.getMaxGroupPeople() != null - && tourSetting.getMinGroupPeople() > tourSetting.getMaxGroupPeople()) { - return Result.error("最少成团人数不能大于最多成团人数"); - } if (StrUtil.isBlank(tourSetting.getSignupStartTime()) || StrUtil.isBlank(tourSetting.getSignupEndTime())) { return Result.error("报名时间不能为空"); } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java index bfd524a..1f747ff 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java @@ -136,7 +136,7 @@ public class TourSignupController { @SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR) public Result h5PageData(PageForm pageForm, Integer year, String lineName, String lineType, String unionId, Boolean directFamilyUnitLine) { if (StrUtil.isBlank(pageForm.getPageOrderName())) { - pageForm.defaultSortAsc("lineName"); + pageForm.defaultSortAsc("lineCode"); } Cnd cnd = buildQueryCnd(year, lineName, lineType, unionId); cnd.andEX("l.directFamilyUnitLine", "=", directFamilyUnitLine); @@ -222,7 +222,7 @@ public class TourSignupController { GROUP BY t.matterId, t.jobNo ) my ON my.matterId = m.id $condition - ORDER BY $orderColumn $orderBy, m.`year` DESC, m.travelStartTime ASC, l.lineName ASC, m.createdAt DESC + ORDER BY $orderColumn $orderBy, CAST(l.lineCode AS UNSIGNED) ASC, m.`year` DESC, m.travelStartTime ASC, l.lineName ASC, m.createdAt DESC """); listSql.setCondition(cnd); listSql.setParam("currentJobNo", currentJobNo); @@ -241,7 +241,7 @@ public class TourSignupController { @SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR) public Result pageData(PageForm pageForm, Integer year, String lineName, String lineType, String unionId) { if (StrUtil.isBlank(pageForm.getPageOrderName())) { - pageForm.defaultSortAsc("lineName"); + pageForm.defaultSortAsc("lineCode"); } Cnd cnd = buildQueryCnd(year, lineName, lineType, unionId); String currentJobNo = currentJobNo(); @@ -325,7 +325,7 @@ public class TourSignupController { GROUP BY t.matterId, t.jobNo ) my ON my.matterId = m.id $condition - ORDER BY $orderColumn $orderBy, m.`year` DESC, m.travelStartTime ASC, l.lineName ASC, m.createdAt DESC + ORDER BY $orderColumn $orderBy, CAST(l.lineCode AS UNSIGNED) ASC, m.`year` DESC, m.travelStartTime ASC, l.lineName ASC, m.createdAt DESC """); listSql.setCondition(cnd); listSql.setParam("currentJobNo", currentJobNo); @@ -751,10 +751,6 @@ public class TourSignupController { update = true; } } - Result maxPeopleResult = checkMaxGroupPeople(matter, oldLedger, familyList); - if (maxPeopleResult != null) { - return maxPeopleResult; - } TourLine line = tourLedgerService.dao().fetch(TourLine.class, matter.getLineId()); boolean directFamilyLine = line != null && Boolean.TRUE.equals(line.getDirectFamilyUnitLine()); TourSetting setting = tourLedgerService.dao().fetch(TourSetting.class, matter.getSettingId()); @@ -962,20 +958,6 @@ public class TourSignupController { return null; } - private Result checkMaxGroupPeople(TourMatter matter, TourLedger oldLedger, List familyList) { - if (matter == null || matter.getMaxGroupPeople() == null || matter.getMaxGroupPeople() <= 0) { - return null; - } - int currentPeople = 1 + (Lang.isEmpty(familyList) ? 0 : familyList.size()); - int signedPeople = countSignupPeople(matter.getId(), oldLedger == null ? null : oldLedger.getId()); - int totalPeople = signedPeople + currentPeople; - if (totalPeople > matter.getMaxGroupPeople()) { - return Result.error("当前报名人数 " + signedPeople + " 人,本次报名 " + currentPeople - + " 人,最多成团人数 " + matter.getMaxGroupPeople() + " 人,报名后将超过人数上限,不能提交"); - } - return null; - } - private boolean isValidIdCard(String idCard) { return StrUtil.isNotBlank(idCard) && idCard.matches("^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dX]$"); @@ -1252,32 +1234,6 @@ public class TourSignupController { return sql.getInt(); } - private int countSignupPeople(String matterId, String excludeLedgerId) { - if (StrUtil.isBlank(matterId)) { - return 0; - } - String excludeSql = StrUtil.isBlank(excludeLedgerId) ? "" : " AND t.id <> @excludeLedgerId"; - Sql sql = Sqls.create(""" - SELECT IFNULL(SUM(1 + IFNULL(f.familyCount, 0)), 0) AS signupPeople - FROM tour_ledger t - LEFT JOIN ( - SELECT ledgerId, COUNT(1) AS familyCount - FROM tour_ledger_family - WHERE delFlag = 0 - GROUP BY ledgerId - ) f ON f.ledgerId = t.id - WHERE t.delFlag = 0 - AND t.matterId = @matterId - """ + excludeSql); - sql.setParam("matterId", matterId); - if (StrUtil.isNotBlank(excludeLedgerId)) { - sql.setParam("excludeLedgerId", excludeLedgerId); - } - sql.setCallback(Sqls.callback.integer()); - tourLedgerService.dao().execute(sql); - return sql.getInt(); - } - private String buildSelectedLineMessage(TourLedger ledger) { String selectedLineName = getLedgerLineName(ledger); return "您已经选择【" + selectedLineName + "】,取消后再重新选择。"; @@ -1537,6 +1493,10 @@ public class TourSignupController { } private String getOrderColumn(String orderName) { + if ("lineCode".equals(orderName)) { + // 线路序号按数字排序,避免字符串排序导致 10 排在 2 前面。 + return "CAST(l.lineCode AS UNSIGNED)"; + } if ("lineName".equals(orderName)) { return "l.lineName"; } @@ -1561,6 +1521,6 @@ public class TourSignupController { if ("signupCount".equals(orderName)) { return "signupCount"; } - return "l.lineName"; + return "CAST(l.lineCode AS UNSIGNED)"; } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourUndertakeController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourUndertakeController.java index 1166b2d..19e033b 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourUndertakeController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourUndertakeController.java @@ -9,7 +9,6 @@ import com.budwk.app.base.result.Result; import com.budwk.app.sys.models.Sys_union; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; -import com.budwk.app.zhgh.dayofficework.tour.models.TourMatter; import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService; import com.budwk.app.zhgh.dayofficework.tour.service.TourMatterService; import org.nutz.dao.Cnd; @@ -61,7 +60,17 @@ public class TourUndertakeController { SELECT COUNT(1) FROM tour_matter m INNER JOIN tour_line l ON l.id = m.lineId - LEFT JOIN sys_union u ON u.id = m.unionId + INNER JOIN ( + SELECT + t.matterId, + t.groupCode, + MAX(t.undertakeUnionId) AS undertakeUnionId + FROM tour_ledger t + WHERE t.delFlag = 0 + AND t.groupCode IS NOT NULL + AND t.groupCode <> '' + GROUP BY t.matterId, t.groupCode + ) gs ON gs.matterId = m.id $condition """); countSql.setCondition(cnd); @@ -74,14 +83,14 @@ public class TourUndertakeController { m.id AS matterId, m.`year`, m.matterName, - m.unionId, + gs.undertakeUnionId AS unionId, + COALESCE(NULLIF(gs.undertakeUnionName, ''), uu.name, '') AS unionName, CASE - WHEN IFNULL(NULLIF(m.undertakeStatus, ''), 'unclaimed') = 'claimed' THEN COALESCE(u.name, '校工会') - ELSE '' - END AS unionName, - IFNULL(NULLIF(m.undertakeStatus, ''), 'unclaimed') AS undertakeStatus, + WHEN IFNULL(NULLIF(gs.undertakeUnionId, ''), '') <> '' THEN 'claimed' + ELSE 'unclaimed' + END AS undertakeStatus, CASE - WHEN IFNULL(NULLIF(m.undertakeStatus, ''), 'unclaimed') = 'claimed' THEN '已领' + WHEN IFNULL(NULLIF(gs.undertakeUnionId, ''), '') <> '' THEN '已领' ELSE '未领' END AS undertakeStatusName, m.lineId, @@ -95,25 +104,20 @@ public class TourUndertakeController { THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime) ELSE '' END AS travelPeriod, - m.minGroupPeople, - m.maxGroupPeople, - IFNULL(sc.signupCount, 0) AS signupCount, - IFNULL(usc.currentUnionSignupCount, 0) AS currentUnionSignupCount, - CASE - WHEN m.maxGroupPeople IS NOT NULL AND m.maxGroupPeople > 0 AND IFNULL(sc.signupCount, 0) > m.maxGroupPeople THEN 'over' - WHEN m.minGroupPeople IS NOT NULL AND m.minGroupPeople > 0 AND IFNULL(sc.signupCount, 0) >= m.minGroupPeople THEN 'formed' - ELSE 'unformed' - END AS groupStatus, - CASE - WHEN m.maxGroupPeople IS NOT NULL AND m.maxGroupPeople > 0 AND IFNULL(sc.signupCount, 0) > m.maxGroupPeople THEN '超员' - WHEN m.minGroupPeople IS NOT NULL AND m.minGroupPeople > 0 AND IFNULL(sc.signupCount, 0) >= m.minGroupPeople THEN '已成团' - ELSE '未成团' - END AS groupStatusName + gs.groupCode, + gs.groupName, + gs.signupCount, + IFNULL(ugs.currentUnionSignupCount, 0) AS currentUnionSignupCount FROM tour_matter m INNER JOIN tour_line l ON l.id = m.lineId - LEFT JOIN sys_union u ON u.id = m.unionId - LEFT JOIN ( - SELECT t.matterId, SUM(1 + IFNULL(f.familyCount, 0)) AS signupCount + INNER JOIN ( + SELECT + t.matterId, + t.groupCode, + MAX(t.groupName) AS groupName, + MAX(t.undertakeUnionId) AS undertakeUnionId, + MAX(t.undertakeUnionName) AS undertakeUnionName, + SUM(1 + IFNULL(f.familyCount, 0)) AS signupCount FROM tour_ledger t LEFT JOIN ( SELECT ledgerId, COUNT(1) AS familyCount @@ -122,12 +126,16 @@ public class TourUndertakeController { GROUP BY ledgerId ) f ON f.ledgerId = t.id WHERE t.delFlag = 0 - AND t.matterId IS NOT NULL - AND t.matterId <> '' - GROUP BY t.matterId - ) sc ON sc.matterId = m.id + AND t.groupCode IS NOT NULL + AND t.groupCode <> '' + GROUP BY t.matterId, t.groupCode + ) gs ON gs.matterId = m.id + LEFT JOIN sys_union uu ON uu.id = gs.undertakeUnionId LEFT JOIN ( - SELECT t.matterId, SUM(1 + IFNULL(f.familyCount, 0)) AS currentUnionSignupCount + SELECT + t.matterId, + t.groupCode, + SUM(1 + IFNULL(f.familyCount, 0)) AS currentUnionSignupCount FROM tour_ledger t LEFT JOIN ( SELECT ledgerId, COUNT(1) AS familyCount @@ -136,13 +144,13 @@ public class TourUndertakeController { GROUP BY ledgerId ) f ON f.ledgerId = t.id WHERE t.delFlag = 0 - AND t.matterId IS NOT NULL - AND t.matterId <> '' + AND t.groupCode IS NOT NULL + AND t.groupCode <> '' AND t.unionId = @currentUnionId - GROUP BY t.matterId - ) usc ON usc.matterId = m.id + GROUP BY t.matterId, t.groupCode + ) ugs ON ugs.matterId = m.id AND ugs.groupCode = gs.groupCode $condition - ORDER BY $orderColumn $orderBy, m.`year` DESC, m.travelStartTime ASC, l.lineName ASC, m.createdAt DESC + ORDER BY $orderColumn $orderBy, m.`year` DESC, m.travelStartTime ASC, l.lineName ASC, gs.groupCode ASC, m.createdAt DESC """); listSql.setCondition(cnd); listSql.setParam("currentUnionId", currentUnionId); @@ -180,49 +188,35 @@ public class TourUndertakeController { return Result.success(unionInfo); } + /** + * 领办当前成团行,接口必须同时接收事项ID和成团编码,避免按事项误领办全部团。 + */ @At @SaCheckPermission("tour.undertake") - public Result undertakeLine(String matterId) { - if (StrUtil.isBlank(matterId)) { + public Result undertakeLine(String matterId, String groupCode) { + if (StrUtil.isBlank(matterId) || StrUtil.isBlank(groupCode)) { return Result.error("参数错误"); } NutMap unionInfo = currentUnionInfo(); if (unionInfo.isEmpty()) { return Result.error("未获取到当前登录人工会"); } - TourMatter matter = tourMatterService.fetch(matterId); - if (matter == null || Boolean.TRUE.equals(matter.getDelFlag())) { - return Result.error("事项不存在"); - } - if ("claimed".equals(matter.getUndertakeStatus())) { - return Result.error("当前线路已领办,不允许修改"); - } - matter.setUnionId(unionInfo.getString("unionId")); - matter.setUndertakeStatus("claimed"); - tourMatterService.update(matter); - return Result.success(unionInfo); + return tourLedgerService.undertakeGroup(matterId, groupCode, unionInfo.getString("unionId"), unionInfo.getString("unionName")); } + /** + * 解除当前成团行的领办信息,清空范围只限定到当前事项的当前团。 + */ @At @SaCheckPermission("tour.undertake") - public Result releaseUndertakeLine(String matterId) { + public Result releaseUndertakeLine(String matterId, String groupCode) { if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { return Result.error("仅系统管理员可以解除领办"); } - if (StrUtil.isBlank(matterId)) { + if (StrUtil.isBlank(matterId) || StrUtil.isBlank(groupCode)) { return Result.error("参数错误"); } - TourMatter matter = tourMatterService.fetch(matterId); - if (matter == null || Boolean.TRUE.equals(matter.getDelFlag())) { - return Result.error("事项不存在"); - } - if (!"claimed".equals(matter.getUndertakeStatus())) { - return Result.error("当前线路未领办,无需解除"); - } - matter.setUnionId(""); - matter.setUndertakeStatus("unclaimed"); - tourMatterService.update(matter); - return Result.success(); + return tourLedgerService.releaseUndertakeGroup(matterId, groupCode); } @At @@ -259,14 +253,14 @@ public class TourUndertakeController { @At @SaCheckPermission("tour.undertake") - public Result signupPageData(PageForm pageForm, String matterId, String keyword, String unionId) { - if (StrUtil.isBlank(matterId)) { + public Result signupPageData(PageForm pageForm, String matterId, String keyword, String unionId, String groupCode) { + if (StrUtil.isBlank(matterId) || StrUtil.isBlank(groupCode)) { return Result.error("参数错误"); } if (StrUtil.isBlank(pageForm.getPageOrderName())) { - pageForm.defaultSortDesc("signupTime"); + pageForm.defaultSortAsc("signupTime"); } - Cnd cnd = buildSignupQueryCnd(matterId, keyword, unionId); + Cnd cnd = buildSignupQueryCnd(matterId, keyword, unionId, groupCode); Sql countSql = Sqls.create(""" SELECT COUNT(1) FROM tour_ledger t @@ -284,16 +278,19 @@ public class TourUndertakeController { t.id, t.jobNo, t.userName, + t.gender, '' AS mobile, t.idCard, t.unionId, t.unionName, - t.signupTime + t.signupTime, + t.groupCode, + t.groupName FROM tour_ledger t INNER JOIN tour_matter m ON m.id = t.matterId INNER JOIN tour_line l ON l.id = m.lineId $condition - ORDER BY $orderColumn $orderBy, t.signupTime DESC, t.createdAt DESC + ORDER BY $orderColumn $orderBy, t.signupTime ASC, t.createdAt ASC """); listSql.setCondition(cnd); listSql.setVar("orderColumn", getSignupOrderColumn(pageForm.getPageOrderName())); @@ -304,15 +301,19 @@ public class TourUndertakeController { List list = listSql.getList(NutMap.class); fillSignupMobile(list); - fillSignupFamilies(list); + flattenSignupFamilies(list); Pagination pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list); return Result.success(pagination); } - private Cnd buildSignupQueryCnd(String matterId, String keyword, String unionId) { + /** + * 领办线路查看以团为单位,报名人员查询必须固定到当前点击的成团编码。 + */ + private Cnd buildSignupQueryCnd(String matterId, String keyword, String unionId, String groupCode) { Cnd cnd = Cnd.NEW(); cnd.and("t.delFlag", "=", false); cnd.and("t.matterId", "=", matterId); + cnd.and("t.groupCode", "=", groupCode); cnd.and("m.delFlag", "=", false); cnd.and("m.enabled", "=", true); cnd.and("m.lineId", "IS NOT", null); @@ -360,9 +361,9 @@ public class TourUndertakeController { } /** - * 查看成团报名人员时按台账ID带出家属子列表,避免前端逐行请求家属信息。 + * 查看单个团报名人员时将教职工和家属平铺到同一列表,便于领办线路复用线路成团的展示方式。 */ - private void fillSignupFamilies(List list) { + private void flattenSignupFamilies(List list) { if (list == null || list.isEmpty()) { return; } @@ -380,8 +381,9 @@ public class TourUndertakeController { Sql sql = Sqls.create(""" SELECT ledgerId, - staffJobNo AS jobNo, + '' AS jobNo, familyName AS userName, + gender, '' AS mobile, idCard, relationship @@ -396,11 +398,29 @@ public class TourUndertakeController { Map> familyMap = new HashMap<>(); for (NutMap family : sql.getList(NutMap.class)) { + family.put("staffRow", false); + family.put("rowTypeName", "家属"); + family.put("unionName", ""); familyMap.computeIfAbsent(family.getString("ledgerId", ""), key -> new ArrayList<>()).add(family); } + List flatList = new ArrayList<>(); for (NutMap item : list) { - item.put("families", familyMap.getOrDefault(item.getString("id", ""), List.of())); + item.put("staffRow", true); + item.put("rowTypeName", "教职工"); + item.put("relationship", ""); + item.put("relatedStaffName", item.getString("userName", "")); + flatList.add(item); + List families = familyMap.getOrDefault(item.getString("id", ""), List.of()); + for (NutMap family : families) { + // 家属行继承教职工成团信息,并标明关联教职工名称。 + family.put("relatedStaffName", item.getString("userName", "")); + family.put("groupCode", item.getString("groupCode", "")); + family.put("groupName", item.getString("groupName", "")); + } + flatList.addAll(families); } + list.clear(); + list.addAll(flatList); } private Cnd buildQueryCnd(Integer year, String lineName, String lineType, String unionId, String undertakeStatus) { @@ -414,14 +434,14 @@ public class TourUndertakeController { cnd.andEX("m.`year`", "=", year == null ? LocalDate.now().getYear() : year); cnd.and(Cnd.likeEX("l.lineName", lineName)); cnd.andEX("l.lineType", "=", lineType); - cnd.andEX("m.unionId", "=", unionId); + cnd.andEX("gs.undertakeUnionId", "=", unionId); if ("claimed".equals(undertakeStatus)) { - cnd.and("m.undertakeStatus", "=", "claimed"); + cnd.and("gs.undertakeUnionId", "IS NOT", null); + cnd.and("gs.undertakeUnionId", "<>", ""); } else if ("unclaimed".equals(undertakeStatus)) { SqlExpressionGroup statusGroup = new SqlExpressionGroup(); - statusGroup.or("m.undertakeStatus", "=", "unclaimed"); - statusGroup.or("m.undertakeStatus", "IS", null); - statusGroup.or("m.undertakeStatus", "=", ""); + statusGroup.or("gs.undertakeUnionId", "IS", null); + statusGroup.or("gs.undertakeUnionId", "=", ""); cnd.and(statusGroup); } applyMatterDataScope(cnd); @@ -489,7 +509,7 @@ public class TourUndertakeController { return "m.matterName"; } if ("unionName".equals(orderName)) { - return "u.name"; + return "unionName"; } if ("undertakeStatusName".equals(orderName)) { return "undertakeStatus"; @@ -498,16 +518,10 @@ public class TourUndertakeController { return "l.lineType"; } if ("signupCount".equals(orderName)) { - return "signupCount"; + return "gs.signupCount"; } - if ("minGroupPeople".equals(orderName)) { - return "m.minGroupPeople"; - } - if ("maxGroupPeople".equals(orderName)) { - return "m.maxGroupPeople"; - } - if ("groupStatusName".equals(orderName)) { - return "groupStatus"; + if ("groupName".equals(orderName) || "groupCode".equals(orderName)) { + return "gs.groupCode"; } return "l.lineName"; } @@ -519,12 +533,18 @@ public class TourUndertakeController { if ("userName".equals(orderName)) { return "t.userName"; } + if ("gender".equals(orderName)) { + return "t.gender"; + } if ("idCard".equals(orderName)) { return "t.idCard"; } if ("unionName".equals(orderName)) { return "t.unionName"; } + if ("groupName".equals(orderName)) { + return "t.groupCode"; + } return "t.signupTime"; } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLedger.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLedger.java index fd34f70..0bce3ee 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLedger.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLedger.java @@ -101,6 +101,32 @@ public class TourLedger extends BaseModel implements Serializable { @ColDefine(type = ColType.VARCHAR, width = 50) private String lineType; + @Column + @Comment("成团编码") + @ColDefine(type = ColType.VARCHAR, width = 20) + private String groupCode; + + @Column + @Comment("成团名称") + @ColDefine(type = ColType.VARCHAR, width = 50) + private String groupName; + + /** + * 领办工会编码按团回填到台账,避免领办动作再修改事项归属工会。 + */ + @Column + @Comment("领办工会编码") + @ColDefine(type = ColType.VARCHAR, width = 32) + private String undertakeUnionId; + + /** + * 领办工会名称与编码一起落台账,列表按团展示时直接读取当前团领办信息。 + */ + @Column + @Comment("领办工会") + @ColDefine(type = ColType.VARCHAR, width = 100) + private String undertakeUnionName; + @Column @Comment("报名酒店") @ColDefine(type = ColType.VARCHAR, width = 100) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLine.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLine.java index 007999a..146a763 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLine.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLine.java @@ -33,8 +33,8 @@ public class TourLine extends BaseModel implements Serializable { @Column @Comment("排序编号") - @ColDefine(type = ColType.VARCHAR, width = 50) - private String lineCode; + @ColDefine(type = ColType.INT, width = 11) + private Integer lineCode; @Column @Comment("线路名称") diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourLedgerService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourLedgerService.java index 076a5dd..e5ac0d8 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourLedgerService.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourLedgerService.java @@ -1,7 +1,58 @@ package com.budwk.app.zhgh.dayofficework.tour.service; import com.budwk.app.base.service.BaseService; +import com.budwk.app.base.result.Result; import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger; +import org.nutz.dao.Cnd; +import org.nutz.lang.util.NutMap; + +import java.util.List; public interface TourLedgerService extends BaseService { + + /** + * 按查询条件获取疗休养台账中已分配的成团编码和成团名称,返回值已按成团信息去重。 + * + * @param cnd 已包含事项、线路、台账状态和数据权限的查询条件 + * @return 去重后的成团信息列表 + */ + List queryGroupOptions(Cnd cnd); + + /** + * 按事项最大成团人数,将当前事项下未成团的报名教职工及家属整体自动分配到成团中。 + * + * @param matterId 疗休养事项ID + * @return 分配结果,包含成功提示或具体业务校验错误 + */ + Result assignGroup(String matterId); + + /** + * 将选中的教职工报名台账调整到指定成团中,并校验调整后目标团人数不超过事项最大成团人数。 + * + * @param matterId 当前疗休养事项ID + * @param groupCode 目标成团编码 + * @param ledgerIds 选中的教职工台账ID,多个ID使用逗号分隔 + * @return 调整结果,包含成功提示或具体业务校验错误 + */ + Result adjustGroup(String matterId, String groupCode, String ledgerIds); + + /** + * 按事项和成团编码领办当前团,只回填当前团台账的领办工会信息,不修改事项表归属工会。 + * + * @param matterId 当前疗休养事项ID + * @param groupCode 当前成团编码 + * @param unionId 当前登录人所属工会编码 + * @param unionName 当前登录人所属工会名称 + * @return 领办结果,包含参数、重复领办和有效团数据校验 + */ + Result undertakeGroup(String matterId, String groupCode, String unionId, String unionName); + + /** + * 按事项和成团编码解除当前团领办,只清空当前团台账的领办工会信息。 + * + * @param matterId 当前疗休养事项ID + * @param groupCode 当前成团编码 + * @return 解除结果,包含参数和当前团是否已领办校验 + */ + Result releaseUndertakeGroup(String matterId, String groupCode); } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourMatterService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourMatterService.java index 8171b59..d710bc9 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourMatterService.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourMatterService.java @@ -3,5 +3,17 @@ package com.budwk.app.zhgh.dayofficework.tour.service; import com.budwk.app.base.service.BaseService; import com.budwk.app.zhgh.dayofficework.tour.models.TourMatter; +import java.util.List; + public interface TourMatterService extends BaseService { + + /** + * 按事项ID批量更新报名起止时间,仅处理报名时间字段。 + * + * @param ids 当前查询条件命中的事项ID集合 + * @param signupStartTime 报名开始时间 + * @param signupEndTime 报名结束时间 + * @return 实际更新数量 + */ + int assignSignupTime(List ids, String signupStartTime, String signupEndTime); } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourLedgerServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourLedgerServiceImpl.java index 88c2f66..0283631 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourLedgerServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourLedgerServiceImpl.java @@ -1,10 +1,28 @@ package com.budwk.app.zhgh.dayofficework.tour.service.impl; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.base.result.Result; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.web.commons.auth.utils.AuthUtil; +import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger; import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; import org.nutz.dao.Dao; +import org.nutz.dao.Sqls; +import org.nutz.dao.sql.Sql; +import org.nutz.dao.util.cri.SqlExpressionGroup; +import org.nutz.ioc.aop.Aop; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.lang.util.NutMap; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; @IocBean(args = {"refer:dao"}) public class TourLedgerServiceImpl extends BaseServiceImpl implements TourLedgerService { @@ -12,4 +30,577 @@ public class TourLedgerServiceImpl extends BaseServiceImpl implement public TourLedgerServiceImpl(Dao dao) { super(dao); } + + /** + * 根据传入条件查询台账中已经存在的成团信息,按成团编码和成团名称去重后返回给页面展示。 + */ + @Override + public List queryGroupOptions(Cnd cnd) { + Sql sql = Sqls.create(""" + SELECT DISTINCT + t.groupCode, + t.groupName + FROM tour_ledger t + INNER JOIN tour_matter m ON m.id = t.matterId + INNER JOIN tour_line l ON l.id = m.lineId + $condition + ORDER BY t.groupCode ASC, t.groupName ASC + """); + sql.setCondition(cnd); + sql.setCallback(Sqls.callback.maps()); + dao().execute(sql); + return sql.getList(NutMap.class); + } + + /** + * 自动分配当前事项下未成团的教职工台账,分配过程中出现容量不合法时整体中止。 + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public Result assignGroup(String matterId) { + if (StrUtil.isBlank(matterId)) { + return Result.error("参数错误"); + } + NutMap matter = fetchAssignMatter(matterId); + if (matter == null || matter.isEmpty()) { + return Result.error("事项不存在或线路信息不完整"); + } + int maxGroupPeople = mapInt(matter, "maxGroupPeople"); + if (maxGroupPeople <= 0) { + return Result.error("未配置最大成团人数,无法分配成团"); + } + + List groups = queryGroupBuckets(matterId); + List signupUnits = queryUnassignedSignupUnits(matterId); + if (signupUnits.isEmpty()) { + return Result.success("暂无未成团人员需要分配"); + } + for (SignupUnit unit : signupUnits) { + if (unit.peopleCount > maxGroupPeople) { + return Result.error(unit.userName + "含家属共" + unit.peopleCount + "人,超过最大成团人数" + maxGroupPeople + "人,无法分配"); + } + } + + int nextGroupIndex = nextGroupIndex(groups); + int assignCount = 0; + for (SignupUnit unit : signupUnits) { + GroupBucket targetGroup = findAvailableGroup(groups, unit.peopleCount, maxGroupPeople); + if (targetGroup == null) { + targetGroup = createGroupBucket(nextGroupIndex++); + groups.add(targetGroup); + } + updateLedgerGroup(unit.ledgerId, targetGroup.groupCode, targetGroup.groupName); + targetGroup.peopleCount += unit.peopleCount; + assignCount++; + } + return Result.success("分配成功,共分配" + assignCount + "名教职工"); + } + + /** + * 将选中的教职工台账调整到目标团,家属随教职工一起计入目标团人数。 + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public Result adjustGroup(String matterId, String groupCode, String ledgerIds) { + if (StrUtil.isBlank(matterId) || StrUtil.isBlank(groupCode)) { + return Result.error("参数错误"); + } + List ledgerIdList = parseLedgerIds(ledgerIds); + if (ledgerIdList.isEmpty()) { + return Result.error("请选择需要调整的教职工"); + } + + NutMap matter = fetchAssignMatter(matterId); + if (matter == null || matter.isEmpty()) { + return Result.error("事项不存在或线路信息不完整"); + } + int maxGroupPeople = mapInt(matter, "maxGroupPeople"); + if (maxGroupPeople <= 0) { + return Result.error("未配置最大成团人数,无法调整成团"); + } + + NutMap targetGroup = fetchTargetGroup(matterId, groupCode); + if (targetGroup == null || targetGroup.isEmpty()) { + return Result.error("请选择有效的成团信息"); + } + List selectedUnits = querySelectedSignupUnits(matterId, ledgerIdList); + if (selectedUnits.size() != ledgerIdList.size()) { + return Result.error("所选人员信息无效,请刷新后重试"); + } + + int selectedPeopleCount = selectedUnits.stream().mapToInt(unit -> unit.peopleCount).sum(); + int targetPeopleCount = countTargetGroupPeople(matterId, groupCode, ledgerIdList); + if (targetPeopleCount + selectedPeopleCount > maxGroupPeople) { + return Result.error("调整后目标团共" + (targetPeopleCount + selectedPeopleCount) + + "人,超过最大成团人数" + maxGroupPeople + "人,无法调整"); + } + + updateLedgerGroup(matterId, ledgerIdList, groupCode, targetGroup.getString("groupName", "")); + return Result.success("调整成功,共调整" + selectedUnits.size() + "名教职工"); + } + + /** + * 查询事项基础信息,最大成团人数是自动分配的核心容量上限。 + */ + /** + * 当前领办以团为单位执行,写入范围限定为同一事项、同一成团编码下的有效台账。 + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public Result undertakeGroup(String matterId, String groupCode, String unionId, String unionName) { + if (StrUtil.isBlank(matterId) || StrUtil.isBlank(groupCode) || StrUtil.isBlank(unionId)) { + return Result.error("参数错误"); + } + NutMap group = fetchUndertakeGroup(matterId, groupCode); + if (group == null || group.isEmpty()) { + return Result.error("当前团不存在或未成团,无法领办"); + } + if (StrUtil.isNotBlank(group.getString("undertakeUnionId", ""))) { + return Result.error("当前团已领办,不允许修改"); + } + Cnd cnd = buildUndertakeGroupCnd(matterId, groupCode); + Chain chain = Chain.make("undertakeUnionId", unionId) + .add("undertakeUnionName", StrUtil.blankToDefault(unionName, "")); + dao().update(TourLedger.class, chain, cnd); + return Result.success(NutMap.NEW().addv("unionId", unionId).addv("unionName", StrUtil.blankToDefault(unionName, ""))); + } + + /** + * 解除领办同样按团处理,仅清空当前团台账领办字段,保留事项表原有归属信息。 + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public Result releaseUndertakeGroup(String matterId, String groupCode) { + if (StrUtil.isBlank(matterId) || StrUtil.isBlank(groupCode)) { + return Result.error("参数错误"); + } + NutMap group = fetchUndertakeGroup(matterId, groupCode); + if (group == null || group.isEmpty()) { + return Result.error("当前团不存在或未成团,无法解除"); + } + if (StrUtil.isBlank(group.getString("undertakeUnionId", ""))) { + return Result.error("当前团未领办,无需解除"); + } + Cnd cnd = buildUndertakeGroupCnd(matterId, groupCode); + dao().update(TourLedger.class, Chain.make("undertakeUnionId", "").add("undertakeUnionName", ""), cnd); + return Result.success(); + } + + private NutMap fetchAssignMatter(String matterId) { + Cnd cnd = buildAssignMatterCnd(matterId); + Sql sql = Sqls.create(""" + SELECT + m.id, + m.maxGroupPeople + FROM tour_matter m + INNER JOIN tour_line l ON l.id = m.lineId + $condition + """); + sql.setCondition(cnd); + sql.setCallback(Sqls.callback.map()); + dao().execute(sql); + return sql.getObject(NutMap.class); + } + + /** + * 查询当前事项下已存在的目标成团信息,人员调整只能选择当前线路台账中已有的团。 + */ + private NutMap fetchTargetGroup(String matterId, String groupCode) { + Sql sql = Sqls.create(""" + SELECT + t.groupCode, + MAX(t.groupName) AS groupName + FROM tour_ledger t + WHERE t.delFlag = 0 + AND t.matterId = @matterId + AND t.groupCode = @groupCode + GROUP BY t.groupCode + """); + sql.setParam("matterId", matterId); + sql.setParam("groupCode", groupCode); + sql.setCallback(Sqls.callback.map()); + dao().execute(sql); + return sql.getObject(NutMap.class); + } + + /** + * 构造自动分配事项查询条件,确保自动分配的数据范围与线路成团查看列表保持一致。 + */ + /** + * 查询当前事项下指定团的领办字段,供领办和解除领办前置校验使用。 + */ + private NutMap fetchUndertakeGroup(String matterId, String groupCode) { + Cnd cnd = buildAssignMatterCnd(matterId); + cnd.and("t.delFlag", "=", false); + cnd.and("t.groupCode", "=", groupCode); + Sql sql = Sqls.create(""" + SELECT + t.groupCode, + MAX(t.groupName) AS groupName, + MAX(t.undertakeUnionId) AS undertakeUnionId, + MAX(t.undertakeUnionName) AS undertakeUnionName + FROM tour_ledger t + INNER JOIN tour_matter m ON m.id = t.matterId + INNER JOIN tour_line l ON l.id = m.lineId + $condition + GROUP BY t.groupCode + """); + sql.setCondition(cnd); + sql.setCallback(Sqls.callback.map()); + dao().execute(sql); + return sql.getObject(NutMap.class); + } + + /** + * 构造当前团领办字段更新范围,避免同一事项下其它团被误更新。 + */ + private Cnd buildUndertakeGroupCnd(String matterId, String groupCode) { + return Cnd.where("matterId", "=", matterId) + .and("groupCode", "=", groupCode) + .and("delFlag", "=", false); + } + + private Cnd buildAssignMatterCnd(String matterId) { + Cnd cnd = Cnd.NEW(); + cnd.and("m.delFlag", "=", false); + cnd.and("m.enabled", "=", true); + cnd.and("m.id", "=", matterId); + cnd.and("m.lineId", "IS NOT", null); + cnd.and("m.lineId", "<>", ""); + cnd.and("l.delFlag", "=", false); + cnd.and("l.enabled", "=", true); + applyMatterDataScope(cnd); + return cnd; + } + + /** + * 查询已存在成团及当前占用人数,后续未成团人员优先从最早团开始补足。 + */ + private List queryGroupBuckets(String matterId) { + Sql sql = Sqls.create(""" + SELECT + t.groupCode, + MAX(t.groupName) AS groupName, + SUM(1 + IFNULL(f.familyCount, 0)) AS peopleCount + FROM tour_ledger t + LEFT JOIN ( + SELECT ledgerId, COUNT(1) AS familyCount + FROM tour_ledger_family + WHERE delFlag = 0 + GROUP BY ledgerId + ) f ON f.ledgerId = t.id + WHERE t.delFlag = 0 + AND t.matterId = @matterId + AND t.groupCode IS NOT NULL + AND t.groupCode <> '' + GROUP BY t.groupCode + ORDER BY t.groupCode ASC + """); + sql.setParam("matterId", matterId); + sql.setCallback(Sqls.callback.maps()); + dao().execute(sql); + + List groups = new ArrayList<>(); + for (NutMap item : sql.getList(NutMap.class)) { + GroupBucket group = new GroupBucket(); + group.groupCode = item.getString("groupCode", ""); + group.groupName = item.getString("groupName", ""); + group.peopleCount = mapInt(item, "peopleCount"); + groups.add(group); + } + return groups; + } + + /** + * 查询未成团教职工台账,并合并家属人数,保证教职工和家属作为一个整体分配。 + */ + private List queryUnassignedSignupUnits(String matterId) { + Sql sql = Sqls.create(""" + SELECT + t.id AS ledgerId, + t.userName, + 1 + IFNULL(f.familyCount, 0) AS peopleCount + FROM tour_ledger t + LEFT JOIN ( + SELECT ledgerId, COUNT(1) AS familyCount + FROM tour_ledger_family + WHERE delFlag = 0 + GROUP BY ledgerId + ) f ON f.ledgerId = t.id + WHERE t.delFlag = 0 + AND t.matterId = @matterId + AND (t.groupCode IS NULL OR t.groupCode = '') + ORDER BY t.signupTime ASC, t.createdAt ASC + """); + sql.setParam("matterId", matterId); + sql.setCallback(Sqls.callback.maps()); + dao().execute(sql); + + List signupUnits = new ArrayList<>(); + for (NutMap item : sql.getList(NutMap.class)) { + SignupUnit unit = new SignupUnit(); + unit.ledgerId = item.getString("ledgerId", ""); + unit.userName = item.getString("userName", ""); + unit.peopleCount = mapInt(item, "peopleCount"); + signupUnits.add(unit); + } + return signupUnits; + } + + /** + * 查询选中教职工台账及其家属人数,确保只调整当前事项下有效的教职工报名记录。 + */ + private List querySelectedSignupUnits(String matterId, List ledgerIds) { + Sql sql = Sqls.create(""" + SELECT + t.id AS ledgerId, + t.userName, + 1 + IFNULL(f.familyCount, 0) AS peopleCount + FROM tour_ledger t + LEFT JOIN ( + SELECT ledgerId, COUNT(1) AS familyCount + FROM tour_ledger_family + WHERE delFlag = 0 + GROUP BY ledgerId + ) f ON f.ledgerId = t.id + WHERE t.delFlag = 0 + AND t.matterId = @matterId + AND t.id IN (@ledgerIds) + ORDER BY t.signupTime ASC, t.createdAt ASC + """); + sql.setParam("matterId", matterId); + sql.setParam("ledgerIds", ledgerIds.toArray(new String[0])); + sql.setCallback(Sqls.callback.maps()); + dao().execute(sql); + + List signupUnits = new ArrayList<>(); + for (NutMap item : sql.getList(NutMap.class)) { + SignupUnit unit = new SignupUnit(); + unit.ledgerId = item.getString("ledgerId", ""); + unit.userName = item.getString("userName", ""); + unit.peopleCount = mapInt(item, "peopleCount"); + signupUnits.add(unit); + } + return signupUnits; + } + + /** + * 统计目标团调整前人数,排除本次选中人员,避免原本已在目标团的人员被重复计数。 + */ + private int countTargetGroupPeople(String matterId, String groupCode, List excludeLedgerIds) { + Sql sql = Sqls.create(""" + SELECT IFNULL(SUM(1 + IFNULL(f.familyCount, 0)), 0) AS peopleCount + FROM tour_ledger t + LEFT JOIN ( + SELECT ledgerId, COUNT(1) AS familyCount + FROM tour_ledger_family + WHERE delFlag = 0 + GROUP BY ledgerId + ) f ON f.ledgerId = t.id + WHERE t.delFlag = 0 + AND t.matterId = @matterId + AND t.groupCode = @groupCode + AND t.id NOT IN (@excludeLedgerIds) + """); + sql.setParam("matterId", matterId); + sql.setParam("groupCode", groupCode); + sql.setParam("excludeLedgerIds", excludeLedgerIds.toArray(new String[0])); + sql.setCallback(Sqls.callback.map()); + dao().execute(sql); + return mapInt(sql.getObject(NutMap.class), "peopleCount"); + } + + /** + * 从最早的团开始查找可容纳当前报名单位的团,避免前面团未满但后面新建团的情况。 + */ + private GroupBucket findAvailableGroup(List groups, int peopleCount, int maxGroupPeople) { + for (GroupBucket group : groups) { + if (group.peopleCount + peopleCount <= maxGroupPeople) { + return group; + } + } + return null; + } + + /** + * 根据现有最大团序号生成下一个团序号,兼容已有非标准团编码。 + */ + private int nextGroupIndex(List groups) { + int maxIndex = 0; + for (GroupBucket group : groups) { + maxIndex = Math.max(maxIndex, parseGroupIndex(group.groupCode)); + } + return maxIndex + 1; + } + + /** + * 创建新的成团桶,编码使用T001递增,名称使用中文序号加“团”。 + */ + private GroupBucket createGroupBucket(int index) { + GroupBucket group = new GroupBucket(); + group.groupCode = String.format("T%03d", index); + group.groupName = toChineseNumber(index) + "团"; + group.peopleCount = 0; + return group; + } + + /** + * 自动分配按事项归属控制数据范围:校工会查看全部;分工会查看本工会事项;其它角色仅处理自己创建的事项。 + */ + private void applyMatterDataScope(Cnd cnd) { + if (hasSchoolUnionScope() || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())) { + return; + } + if (hasBranchUnionScope()) { + cnd.and("m.unionId", "=", SecurityUtil.getUnionId()); + return; + } + SqlExpressionGroup creatorGroup = new SqlExpressionGroup(); + creatorGroup.or("m.creatorUserId", "=", SecurityUtil.getUserId()); + creatorGroup.or("m.createdBy", "=", SecurityUtil.getUserId()); + cnd.and(creatorGroup); + } + + /** + * 判断当前用户是否具备校工会级别的事项处理范围。 + */ + private boolean hasSchoolUnionScope() { + return AuthUtil.hasRole(RoleConstant.SYSADMIN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_CHAIRMAN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_VICE_CHAIRMAN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_WELFARE_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_MEMBER_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_TC_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_WC_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_DC_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_PROPOSAL_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_CLUB_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.SCHOOL_UNION_ACTIVITY_ADMIN.name()); + } + + /** + * 判断当前用户是否具备分工会级别的事项处理范围。 + */ + private boolean hasBranchUnionScope() { + return AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_VICE_CHAIRMAN.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_OPERATOR.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_GROUP_LEADER.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ARTICLE_WRITER.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WY.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ZUZHI_WY.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_XUANCHUAN_WY.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_NVGONG_WY.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_QINGNIAN_WY.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WENTI_WY.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_SHENGGHUO_WY.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_TIAOJIE_WY.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WENTI_SPORTS.name()); + } + + /** + * 更新教职工台账成团信息,家属行展示时继承教职工台账的成团信息。 + */ + private void updateLedgerGroup(String ledgerId, String groupCode, String groupName) { + dao().update(TourLedger.class, Chain.make("groupCode", groupCode).add("groupName", groupName), Cnd.where("id", "=", ledgerId)); + } + + /** + * 批量更新选中的教职工台账成团信息,人员调整只写教职工台账,家属展示继续继承教职工。 + */ + private void updateLedgerGroup(String matterId, List ledgerIds, String groupCode, String groupName) { + Cnd cnd = Cnd.where("id", "in", ledgerIds) + .and("matterId", "=", matterId) + .and("delFlag", "=", false); + dao().update(TourLedger.class, Chain.make("groupCode", groupCode).add("groupName", groupName), cnd); + } + + /** + * 解析前端逗号分隔的台账ID,去重并过滤空值,避免重复计数和重复更新。 + */ + private List parseLedgerIds(String ledgerIds) { + Set idSet = new LinkedHashSet<>(); + if (StrUtil.isBlank(ledgerIds)) { + return new ArrayList<>(); + } + for (String id : ledgerIds.split(",")) { + if (StrUtil.isNotBlank(id)) { + idSet.add(id.trim()); + } + } + return new ArrayList<>(idSet); + } + + /** + * 解析T001这类标准团编码中的数字序号,非标准编码返回0。 + */ + private int parseGroupIndex(String groupCode) { + if (StrUtil.isBlank(groupCode) || !groupCode.startsWith("T")) { + return 0; + } + try { + return Integer.parseInt(groupCode.substring(1)); + } catch (NumberFormatException e) { + return 0; + } + } + + /** + * 将数字序号转为中文团名序号,支持常见的两位和多位团序号展示。 + */ + private String toChineseNumber(int number) { + String[] digits = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"}; + if (number <= 0) { + return String.valueOf(number); + } + if (number < 10) { + return "第" + digits[number]; + } + if (number < 100) { + int ten = number / 10; + int one = number % 10; + String prefix = ten == 1 ? "十" : digits[ten] + "十"; + return "第" + prefix + (one == 0 ? "" : digits[one]); + } + String value = String.valueOf(number); + StringBuilder builder = new StringBuilder("第"); + for (int i = 0; i < value.length(); i++) { + builder.append(digits[value.charAt(i) - '0']); + } + return builder.toString(); + } + + /** + * 安全读取SQL返回的整数值,避免不同数据库驱动返回Number或字符串时转换失败。 + */ + private int mapInt(NutMap map, String key) { + Object value = map == null ? null : map.get(key); + if (value instanceof Number number) { + return number.intValue(); + } + if (value == null || StrUtil.isBlank(String.valueOf(value))) { + return 0; + } + return Integer.parseInt(String.valueOf(value)); + } + + /** + * 已有成团容量桶,记录每个团当前人数以及后续分配后的占用人数。 + */ + private static class GroupBucket { + private String groupCode; + private String groupName; + private int peopleCount; + } + + /** + * 待分配报名单位,教职工及其家属作为不可拆分的最小分配单位。 + */ + private static class SignupUnit { + private String ledgerId; + private String userName; + private int peopleCount; + } } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourMatterServiceImpl.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourMatterServiceImpl.java index c7e10db..626555d 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourMatterServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/impl/TourMatterServiceImpl.java @@ -3,13 +3,35 @@ package com.budwk.app.zhgh.dayofficework.tour.service.impl; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.zhgh.dayofficework.tour.models.TourMatter; import com.budwk.app.zhgh.dayofficework.tour.service.TourMatterService; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; import org.nutz.dao.Dao; import org.nutz.ioc.loader.annotation.IocBean; +import java.util.List; + @IocBean(args = {"refer:dao"}) public class TourMatterServiceImpl extends BaseServiceImpl implements TourMatterService { public TourMatterServiceImpl(Dao dao) { super(dao); } + + /** + * 按事项ID批量更新报名起止时间,供事项列表“一键赋值报名时间”使用。 + * + * @param ids 当前查询条件命中的事项ID集合 + * @param signupStartTime 报名开始时间 + * @param signupEndTime 报名结束时间 + * @return 实际更新数量 + */ + @Override + public int assignSignupTime(List ids, String signupStartTime, String signupEndTime) { + if (ids == null || ids.isEmpty()) { + return 0; + } + return dao().update(TourMatter.class, + Chain.make("signupStartTime", signupStartTime).add("signupEndTime", signupEndTime), + Cnd.where("id", "in", ids)); + } } diff --git a/src/main/resources/db/tour_ledger_add_group_info.sql b/src/main/resources/db/tour_ledger_add_group_info.sql new file mode 100644 index 0000000..e2f3448 --- /dev/null +++ b/src/main/resources/db/tour_ledger_add_group_info.sql @@ -0,0 +1,7 @@ +-- 疗休养台账增加成团信息字段,用于线路成团自动分配和人员调整。 +ALTER TABLE `tour_ledger` + ADD COLUMN `groupCode` varchar(20) DEFAULT NULL COMMENT '成团编码' AFTER `lineType`, + ADD COLUMN `groupName` varchar(50) DEFAULT NULL COMMENT '成团名称' AFTER `groupCode`; + +ALTER TABLE `tour_ledger` + ADD KEY `idx_tour_ledger_matter_line_group` (`matterId`, `lineId`, `groupCode`, `delFlag`); diff --git a/src/main/resources/db/tour_ledger_add_undertake_union.sql b/src/main/resources/db/tour_ledger_add_undertake_union.sql new file mode 100644 index 0000000..2274fef --- /dev/null +++ b/src/main/resources/db/tour_ledger_add_undertake_union.sql @@ -0,0 +1,5 @@ +ALTER TABLE tour_ledger + ADD COLUMN undertakeUnionId VARCHAR(32) NULL COMMENT '领办工会编码' AFTER groupName, + ADD COLUMN undertakeUnionName VARCHAR(100) NULL COMMENT '领办工会' AFTER undertakeUnionId; + +CREATE INDEX idx_tour_ledger_undertake_union ON tour_ledger (matterId, groupCode, undertakeUnionId); diff --git a/src/main/resources/db/tour_line_line_code_to_int.sql b/src/main/resources/db/tour_line_line_code_to_int.sql new file mode 100644 index 0000000..d320347 --- /dev/null +++ b/src/main/resources/db/tour_line_line_code_to_int.sql @@ -0,0 +1,9 @@ +-- Convert tour line sort code to numeric type for stable numeric ascending order. +UPDATE tour_line +SET lineCode = '0' +WHERE lineCode IS NULL + OR TRIM(lineCode) = '' + OR lineCode NOT REGEXP '^[0-9]+$'; + +ALTER TABLE tour_line + MODIFY COLUMN lineCode INT(11) NOT NULL DEFAULT 0 COMMENT '排序编号'; diff --git a/src/main/resources/views/platform/zhgh/dayofficework/Tour/group/index.html b/src/main/resources/views/platform/zhgh/dayofficework/Tour/group/index.html index 90886bb..eeac73a 100644 --- a/src/main/resources/views/platform/zhgh/dayofficework/Tour/group/index.html +++ b/src/main/resources/views/platform/zhgh/dayofficework/Tour/group/index.html @@ -56,31 +56,16 @@ layout("/layouts/platform.html"){ - + - - - - - - - - - @@ -120,33 +105,48 @@ layout("/layouts/platform.html"){ -
报名人员列表
+
+
报名人员列表
+
+ + {{ formatGroupOption(item) }} + +
+
+ 分配成团 + 人员调整 +
+
- - - + + + + + + + + + + + + + + + + + + + + 取消 + 确认 + + @@ -181,6 +206,15 @@ layout("/layouts/platform.html"){ viewTitle: "", viewRow: {}, viewTableData: [], + viewGroupOptions: [], + viewSelection: [], + syncingFamilySelection: false, + assignGroupLoading: false, + adjustVisible: false, + adjustLoading: false, + adjustForm: { + groupCode: "" + }, pageForm: { pageNumber: 1, pageSize: 10, @@ -196,10 +230,11 @@ layout("/layouts/platform.html"){ pageNumber: 1, pageSize: 10, totalCount: 0, - pageOrderName: "signupTime", - pageOrderBy: "descending", + pageOrderName: "groupCode", + pageOrderBy: "ascending", keyword: "", - unionId: "" + unionId: "", + groupCode: "" } } }, @@ -226,6 +261,14 @@ layout("/layouts/platform.html"){ || row.directFamilyUnitLine === 1 || row.directFamilyUnitLine === "1" }, + formatGroupInfo(row) { + if (!row || !row.groupCode) return "未成团" + return row.groupName ? row.groupCode + " " + row.groupName : row.groupCode + }, + formatGroupOption(item) { + if (!item || !item.groupCode) return "" + return item.groupName ? item.groupCode + " " + item.groupName : item.groupCode + }, openView(row) { this.viewRow = row || {} this.viewTitle = this.viewRow.lineName || "报名人员" @@ -233,11 +276,17 @@ layout("/layouts/platform.html"){ this.viewForm.pageNumber = 1 this.viewForm.pageSize = 10 this.viewForm.totalCount = 0 - this.viewForm.pageOrderName = "signupTime" - this.viewForm.pageOrderBy = "descending" + this.viewForm.pageOrderName = "groupCode" + this.viewForm.pageOrderBy = "ascending" this.viewForm.keyword = "" this.viewForm.unionId = "" + this.viewForm.groupCode = "" this.viewTableData = [] + this.viewGroupOptions = [] + this.viewSelection = [] + this.assignGroupLoading = false + this.resetAdjustForm() + this.loadViewGroupOptions() this.loadViewData() }, viewSearch() { @@ -247,12 +296,118 @@ layout("/layouts/platform.html"){ viewReset() { this.viewForm.keyword = "" this.viewForm.unionId = "" + this.viewForm.groupCode = "" + this.viewForm.pageNumber = 1 + this.loadViewData() + }, + toggleGroupFilter(item) { + const groupCode = item && item.groupCode ? item.groupCode : "" + this.viewForm.groupCode = this.viewForm.groupCode === groupCode ? "" : groupCode this.viewForm.pageNumber = 1 this.loadViewData() }, viewIndexMethod(index) { return (this.viewForm.pageNumber - 1) * this.viewForm.pageSize + index + 1 }, + isViewRowSelectable(row) { + return !!(row && row.selectable) + }, + viewSelectionChange(selection) { + this.viewSelection = selection || [] + this.syncFamilySelection(selection || []) + }, + syncFamilySelection(selection) { + if (this.syncingFamilySelection || !this.$refs.viewTable) { + return + } + this.syncingFamilySelection = true + const selectedStaffIds = selection.filter(item => item && item.staffRow).map(item => item.id) + this.viewTableData.forEach(row => { + if (!row || row.staffRow || !row.ledgerId) { + return + } + this.$refs.viewTable.toggleRowSelection(row, selectedStaffIds.indexOf(row.ledgerId) !== -1) + }) + this.$nextTick(() => { + this.syncingFamilySelection = false + }) + }, + confirmAssignGroup() { + if (!this.viewRow || !this.viewRow.matterId) { + this.$message.warning("线路信息不完整") + return + } + this.$confirm("确认要自动分配未成团人员吗?", "提示", { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + }).then(() => { + this.doAssignGroup() + }).catch(() => {}) + }, + doAssignGroup() { + this.assignGroupLoading = true + this.$axios.post(loc() + "/assignGroup", { + matterId: this.viewRow.matterId + }).then((res) => { + if (res.code === 0) { + this.$message.success(res.msg || "分配成功") + this.loadViewGroupOptions() + this.loadViewData() + this.pageData() + } else { + this.$message.warning(res.msg || "分配失败") + } + }).finally(() => { + this.assignGroupLoading = false + }) + }, + openAdjustGroup() { + if (!this.viewSelection.length) { + this.$message.warning("请选择需要调整的教职工") + return + } + if (!this.viewGroupOptions.length) { + this.$message.warning("暂无可调整的成团信息") + return + } + this.adjustVisible = true + }, + submitAdjustGroup() { + if (!this.adjustForm.groupCode) { + this.$message.warning("请选择成团信息") + return + } + const ledgerIds = this.viewSelection.filter(item => item && item.staffRow).map(item => item.id).filter(id => id).join(",") + if (!ledgerIds) { + this.$message.warning("请选择需要调整的教职工") + return + } + this.adjustLoading = true + this.$axios.post(loc() + "/adjustGroup", { + matterId: this.viewRow.matterId, + groupCode: this.adjustForm.groupCode, + ledgerIds: ledgerIds + }).then((res) => { + if (res.code === 0) { + this.$message.success(res.msg || "调整成功") + this.adjustVisible = false + this.loadViewGroupOptions() + this.loadViewData() + this.pageData() + } else { + this.$message.warning(res.msg || "调整失败") + } + }).finally(() => { + this.adjustLoading = false + }) + }, + resetAdjustForm() { + this.adjustLoading = false + this.adjustForm = { + groupCode: "" + } + }, viewSizeChange(size) { this.viewForm.pageSize = size this.viewForm.pageNumber = 1 @@ -271,6 +426,7 @@ layout("/layouts/platform.html"){ loadViewData() { if (!this.viewRow || !this.viewRow.matterId) { this.viewTableData = [] + this.viewSelection = [] this.viewForm.totalCount = 0 return } @@ -280,6 +436,7 @@ layout("/layouts/platform.html"){ matterId: this.viewRow.matterId, keyword: this.viewForm.keyword, unionId: this.viewForm.unionId, + groupCode: this.viewForm.groupCode, pageNumber: this.viewForm.pageNumber, pageSize: this.viewForm.pageSize, pageOrderName: this.viewForm.pageOrderName, @@ -289,6 +446,12 @@ layout("/layouts/platform.html"){ if (res.code === 0) { const data = res.data || {} this.viewTableData = data.list || [] + this.viewSelection = [] + this.$nextTick(() => { + if (this.$refs.viewTable) { + this.$refs.viewTable.clearSelection() + } + }) this.viewForm.totalCount = data.totalCount || 0 } else { this.$message.warning(res.msg || "查询失败") @@ -299,6 +462,21 @@ layout("/layouts/platform.html"){ } }) }, + loadViewGroupOptions() { + if (!this.viewRow || !this.viewRow.matterId) { + this.viewGroupOptions = [] + return + } + this.$axios.post(loc() + "/groupOptions", { + matterId: this.viewRow.matterId + }).then((res) => { + if (res.code === 0) { + this.viewGroupOptions = res.data || [] + } else { + this.$message.warning(res.msg || "查询成团信息失败") + } + }) + }, exportLine(row) { if (!row || !row.matterId) { this.$message.warning("线路信息不完整") @@ -347,21 +525,52 @@ layout("/layouts/platform.html"){ margin-top: 12px; text-align: right; } + .tour-group-view-header { + align-items: flex-start; + display: flex; + gap: 12px; + justify-content: flex-start; + margin-bottom: 12px; + max-width: 100%; + } .tour-group-view-title { border-left: 4px solid #0079c2; color: #0079c2; font-size: 14px; font-weight: 600; line-height: 16px; - margin-bottom: 12px; padding-left: 10px; + white-space: nowrap; } - .tour-family-list { - padding: 8px 32px; + .tour-group-options { + display: flex; + flex: 0 1 820px; + flex-wrap: nowrap; + gap: 8px; + justify-content: flex-start; + max-width: calc(100% - 120px); + min-width: 0; + overflow-x: auto; + overflow-y: hidden; + padding-bottom: 4px; } - .tour-family-empty { - color: #909399; - padding: 8px 32px; + .tour-group-actions { + display: flex; + flex: 0 0 auto; + gap: 8px; + margin-left: auto; + } + .tour-group-option { + cursor: pointer; + flex: 0 0 auto; + max-width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .tour-group-option.is-active { + border-color: #67c23a; + font-weight: 600; } .el-table .direct-family-row > td, .el-table .direct-family-row > td .cell { diff --git a/src/main/resources/views/platform/zhgh/dayofficework/Tour/ledger/index.html b/src/main/resources/views/platform/zhgh/dayofficework/Tour/ledger/index.html index a225591..fb412cd 100644 --- a/src/main/resources/views/platform/zhgh/dayofficework/Tour/ledger/index.html +++ b/src/main/resources/views/platform/zhgh/dayofficework/Tour/ledger/index.html @@ -108,6 +108,12 @@ layout("/layouts/platform.html"){ + + + +