diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index cb1a291..f807d2f 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -1,6 +1,21 @@ + + + + + + - \ No newline at end of file + diff --git a/.idea/misc.xml b/.idea/misc.xml index 9ae45b6..ec149a9 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,6 @@ - - \ No newline at end of file + diff --git a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareListController.java b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareListController.java index 635be84..fbc4a58 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareListController.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareListController.java @@ -165,6 +165,60 @@ public class WelfareListController { welfareListService.exportXlsx(pageForm, response); } + /** + * 导出指定福利项目和套餐的报销表。 + * + * @param projectId 福利项目 ID + * @param optionId 福利套餐 ID,必须属于 projectId 对应项目 + * @param response XLSX 文件下载响应 + */ + @At + @Ok("void") + @SaCheckPermission("welfare.list.mange") + @ApiOperation("导出福利报销表") + public void doExcelByOptionId(@Param("projectId") String projectId, + @Param("optionId") String optionId, + HttpServletResponse response) { + welfareListService.doExcelByOptionId(projectId, optionId, response); + } + + /** + * 导出供货商使用的套餐选择数据,按套餐拆分工作表。 + * + * @param projectId 福利项目 ID + * @param unionId 可选的分工会 ID;为空时按当前用户的数据权限导出 + * @param response XLSX 文件下载响应 + */ + @At + @Ok("void") + @SaCheckPermission("welfare.list.mange") + @ApiOperation("导出供货商选择数据") + public void exportSelectData(@Param("projectId") String projectId, + @Param("unionId") String unionId, + HttpServletResponse response) { + welfareListService.exportSelectData(projectId, unionId, response); + } + + /** + * 导入供应商提供的快递单号文件。 + * + * @param file XLS 或 XLSX 文件,包含工号与最多四个快递单号列 + * @param projectId 福利项目 ID + * @param optionId 福利套餐 ID,必须属于 projectId 对应项目 + * @return 导入结果;成功时返回成功提示,失败时返回具体原因 + */ + @At + @Aop(TransAop.READ_COMMITTED) + @SaCheckPermission("welfare.list.mange") + @AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"}) + @SLog(tag = "福利名单管理", msg = "导入福利快递单号") + @ApiOperation("导入福利快递单号") + public Result importCourierNumber(@Param("file") TempFile file, + @Param("projectId") String projectId, + @Param("optionId") String optionId) { + return welfareListService.importCourierNumber(file, projectId, optionId); + } + @At @SaCheckPermission("welfare.list.mange") @ApiOperation("编辑备注") diff --git a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareStatisticsController.java b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareStatisticsController.java index 56218a0..994edf0 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareStatisticsController.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/controller/WelfareStatisticsController.java @@ -110,8 +110,8 @@ public class WelfareStatisticsController { @Ok("void") @SaCheckPermission("welfare.statistics") @ApiOperation("按福利选项导出") - public void exportByWelfareOptions(String projectId, HttpServletResponse response) { - welfareStatisticsService.exportByWelfareOptions(projectId, response); + public void exportByWelfareOptions(String projectId, String unionId, HttpServletResponse response) { + welfareStatisticsService.exportByWelfareOptions(projectId, unionId, response); } @At diff --git a/src/main/java/com/budwk/app/zhgh/welfare/mode/WelfareExportEntityTc.java b/src/main/java/com/budwk/app/zhgh/welfare/mode/WelfareExportEntityTc.java index 500108f..de86840 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/mode/WelfareExportEntityTc.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/mode/WelfareExportEntityTc.java @@ -30,9 +30,19 @@ public class WelfareExportEntityTc { @Excel(name = "已选福利", width = 80d) private String optionName; - @Excel(name = "收货信息", width = 100d) + // V4 原“收货信息”导出列保留字段,现按 V3 规则拆分为收货人、联系方式和收货地址。 +// @Excel(name = "收货信息", width = 100d) private String receiveAddress; + @Excel(name = "收货人", width = 15d) + private String recipient; + + @Excel(name = "联系方式", width = 20d) + private String phone; + + @Excel(name = "收货地址", width = 60d) + private String address; + @Excel(name = "快递单号", width = 40d) private String courierNumber; diff --git a/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareSelectionSituationPageForm.java b/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareSelectionSituationPageForm.java index 6d17fbd..756891d 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareSelectionSituationPageForm.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/param/WelfareSelectionSituationPageForm.java @@ -37,4 +37,10 @@ public class WelfareSelectionSituationPageForm extends PageForm { @ApiModelProperty("人员属性") private String userAttribute; + + @ApiModelProperty("人员类型") + private String[] personTypes; + + @ApiModelProperty("在职状态") + private String[] userStates; } diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareListService.java b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareListService.java index 04eb36b..5f710c5 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareListService.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareListService.java @@ -13,10 +13,32 @@ import javax.servlet.http.HttpServletResponse; public interface WelfareListService extends BaseService { + /** + * 导出一个福利项目下指定套餐的报销表。 + * + * @param projectId 福利项目 ID + * @param optionId 福利套餐 ID,必须属于指定福利项目 + * @param response XLSX 文件下载响应 + */ void doExcelByOptionId(String projectId, String optionId, HttpServletResponse response); + /** + * 导出供应商所需的选择数据,并按套餐创建工作表。 + * + * @param projectId 福利项目 ID + * @param unionId 可选的分工会 ID;为空时按当前登录人的数据范围导出 + * @param response XLSX 文件下载响应 + */ void exportSelectData(String projectId, String unionId, HttpServletResponse response); + /** + * 导入指定福利项目、套餐下的快递单号。 + * + * @param file XLS 或 XLSX 文件,列为工号、姓名和最多四个快递单号 + * @param projectId 福利项目 ID + * @param optionId 福利套餐 ID,必须属于指定福利项目 + * @return 成功或失败的导入结果 + */ Result importCourierNumber(TempFile file, String projectId, String optionId); void doEditWelfareData(String editWelfareData, String welfareOptions); diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareStatisticsService.java b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareStatisticsService.java index 1217d76..59d3070 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareStatisticsService.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/WelfareStatisticsService.java @@ -62,7 +62,7 @@ public interface WelfareStatisticsService extends BaseService { * @param projectId * @param response */ - void exportByWelfareOptions(String projectId, HttpServletResponse response); + void exportByWelfareOptions(String projectId, String unionId, HttpServletResponse response); /** * 导出汇总表 diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java index 0d06541..03a4aa5 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareListServiceImpl.java @@ -61,8 +61,17 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme @Override public void doExcelByOptionId(String projectId, String optionId, HttpServletResponse response) { + if (StrUtil.isBlank(projectId) || StrUtil.isBlank(optionId)) { + log.warn("导出报销表失败:福利项目或套餐不能为空"); + return; + } WelfareProject project = dao().fetch(WelfareProject.class, projectId); - WelfareProjectSubjectOption subjectOption = dao().fetch(WelfareProjectSubjectOption.class, optionId); + WelfareProjectSubjectOption subjectOption = dao().fetch(WelfareProjectSubjectOption.class, + Cnd.where("id", "=", optionId).and("welfareId", "=", projectId)); + if (project == null || subjectOption == null) { + log.warn("导出报销表失败:福利项目或套餐不存在,projectId={}, optionId={}", projectId, optionId); + return; + } Sql sql = Sqls.create(""" SELECT @@ -75,30 +84,35 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme wpus.selectNum FROM welfare_project_user_selection wpus - LEFT JOIN `welfare_list` wl ON wpus.selectUserId = wl.userId + INNER JOIN `welfare_list` wl ON wl.userId = wpus.selectUserId + AND wl.projectId = wpus.welfareId LEFT JOIN `sys_user` u ON u.id = wl.userId - LEFT JOIN sys_union un ON un.id = wl.welfareUnitId - LEFT JOIN sys_unit it ON it.id = wl.welfareSecondLevelUnitId + LEFT JOIN sys_union un ON un.id = wl.welfareUnionId + LEFT JOIN sys_unit it ON it.id = wl.welfareUnitId WHERE - wl.projectId = @welfareId - AND wpus.selectOptionId = @selectOptionId and wpus.selectNum!=0 - + wpus.welfareId = @welfareId + AND wpus.selectOptionId = @selectOptionId + AND IFNULL(wpus.selectNum, 0) <> 0 + ORDER BY wl.welfareUnionName, wl.welfareUnitName, u.loginname """).setParam("welfareId", projectId).setParam("selectOptionId", optionId); List mapList = listMap(sql); List userId = mapList.stream().map(m -> m.getString("id")).collect(Collectors.toList()); - List courierNumberList = dao().query(WelfareCourierNumber.class, + List courierNumberList = userId.isEmpty() + ? Collections.emptyList() + : dao().query(WelfareCourierNumber.class, Cnd.where("selectUserId", "in", userId) .and("welfareId", "=", projectId) - .and("selectOptionId", "=", optionId)); + .and("selectOptionId", "=", optionId) + .asc("createdAt")); List nameList = List.of("one", "two", "three", "four"); for (NutMap map : mapList) { List userCourierNumbers = courierNumberList.stream() .filter(c -> c.getSelectUserId().equals(map.getString("id"))).collect(Collectors.toList()); - for (int i = 0; i < userCourierNumbers.size(); i++) { + for (int i = 0; i < userCourierNumbers.size() && i < nameList.size(); i++) { map.setv(nameList.get(i) + "CourierNumber", userCourierNumbers.get(i).getCourierNumber()); } } @@ -138,14 +152,13 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme exportParams.setSheetName(un); service.createSheetForMap(workbook, exportParams, exportEntities, v); }*/ - try { - ExportParams exportParams = new ExportParams(); - exportParams.setType(ExcelType.XSSF); - exportParams.setTitle(project.getName() + subjectOption.getOptionName() + "发放名单"); - Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, mapList); + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + exportParams.setTitle(project.getName() + subjectOption.getOptionName() + "报销表"); + try (Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, mapList)) { CommonDownloadUtil.download(project.getName() + subjectOption.getOptionName() + ".xlsx", workbook, response); } catch (Exception e) { - e.printStackTrace(); + log.error("导出报销表失败,projectId={}, optionId={}", projectId, optionId, e); } } @@ -153,6 +166,10 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme @Override public void exportSelectData(String projectId, String unionId, HttpServletResponse response) { WelfareProject welfareProject = dao().fetch(WelfareProject.class, Cnd.where("id", "=", projectId)); + if (welfareProject == null) { + log.warn("导出供货商选择数据失败:福利项目不存在,projectId={}", projectId); + return; + } String projectName = welfareProject.getName(); Cnd cnd = Cnd.NEW(); Sql sql = Sqls.create(""" @@ -170,16 +187,18 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme left join `sys_user` u on u.id = wpus.selectUserId left join welfare_project_subject_option wpso on wpso.id = wpus.selectOptionId left join welfare_list wl on wl.userId = wpus.selectUserId and wl.projectId=wpus.welfareId - LEFT JOIN sys_union un ON un.id = wl.welfareUnitId - LEFT JOIN sys_unit it ON it.id = wl.welfareSecondLevelUnitId + LEFT JOIN sys_union un ON un.id = wl.welfareUnionId + LEFT JOIN sys_unit it ON it.id = wl.welfareUnitId $condition """); cnd.and("wpus.welfareId", "=", projectId); - if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name(), RoleConstant.SCHOOL_UNION_WELFARE_ADMIN.name())) { - cnd.and("wl.welfareUnitId", "=", SecurityUtil.getUnionId()); + if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WENTI_SPORTS.name())) { + cnd.and("wl.welfareUnionId", "=", SecurityUtil.getUnionId()); } - cnd.andEX("wl.welfareUnitId", "=", unionId); + cnd.andEX("wl.welfareUnionId", "=", unionId); cnd.groupBy("wpus.selectUserId", "wpus.selectOptionId"); cnd.asc("it.unitcode"); sql.setCondition(cnd); @@ -207,12 +226,21 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme try { CommonDownloadUtil.download(projectName + ".xlsx", workbook, response); } catch (Exception e) { - e.printStackTrace(); + log.error("导出供货商选择数据失败,projectId={}", projectId, e); } } @Override + @Aop(TransAop.READ_COMMITTED) public Result importCourierNumber(TempFile file, String projectId, String optionId) { + if (StrUtil.isBlank(projectId) || StrUtil.isBlank(optionId)) { + return Result.error("福利项目和套餐不能为空"); + } + WelfareProjectSubjectOption subjectOption = dao().fetch(WelfareProjectSubjectOption.class, + Cnd.where("id", "=", optionId).and("welfareId", "=", projectId)); + if (subjectOption == null) { + return Result.error("福利套餐不存在或不属于当前福利项目"); + } if (Lang.isEmpty(file)) { return Result.error("上传的文件不能为空"); } @@ -226,7 +254,7 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme ImportParams importParams = new ImportParams(); excelList = ExcelImportUtil.importExcel(file.getFile(), CourierNumberExcelMode.class, importParams); } catch (Exception e) { - e.printStackTrace(); + log.warn("读取快递单号导入文件失败", e); return Result.error("读取不到数据,请检查excel文件格式"); } @@ -236,7 +264,7 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme try { if (excelList.stream().anyMatch(v -> StrUtil.isBlank(v.getLoginName()))) { - return Result.error("工号和快递单号不能为空"); + return Result.error("工号不能为空"); } List filterExcelList = excelList.stream().collect(Collectors.collectingAndThen( @@ -249,6 +277,9 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme Sql loginNameSql = Sqls.create("select id,loginname from sys_user where loginname in (@loginNameList)"); loginNameSql.setParam("loginNameList", loginNameList); List loginNameMaps = listMap(loginNameSql); + if (loginNameMaps.isEmpty()) { + return Result.error("Excel中的工号均未匹配到系统用户"); + } List userIds = loginNameMaps.stream().map(v -> v.getString("id")).collect(Collectors.toList()); @@ -291,12 +322,13 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme welfareCourierNumbers.add(courierNumber); } } - insert(welfareCourierNumbers); + if (!welfareCourierNumbers.isEmpty()) { + insert(welfareCourierNumbers); + } return Result.success("导入成功"); } catch (Exception e) { - - e.printStackTrace(); + log.error("导入快递单号失败,projectId={}, optionId={}", projectId, optionId, e); return Result.error("导入快递单号失败"); } @@ -685,12 +717,10 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme t2.loginname, t2.username, t2.sex, - DATE_FORMAT(t2.birthday, '%Y-%m-%d') AS birthday, - threeUnit.name AS threeUnitName + DATE_FORMAT(t2.birthday, '%Y-%m-%d') AS birthday FROM `welfare_list` t1 LEFT JOIN sys_user t2 ON t2.id = t1.userId - LEFT JOIN sys_unit threeUnit ON threeUnit.id = t2.threeUnitId $condition """); Cnd cnd = Cnd.NEW(); @@ -735,7 +765,6 @@ public class WelfareListServiceImpl extends BaseServiceImpl impleme entities.add(new ExcelExportEntity("在职状态", "userState", 20)); entities.add(new ExcelExportEntity("所属工会", "welfareUnionName", 20)); entities.add(new ExcelExportEntity("所属单位", "welfareUnitName", 20)); - entities.add(new ExcelExportEntity("三级单位", "threeUnitName", 20)); entities.add(new ExcelExportEntity("备注", "remark", 20)); // 设置导出参数 diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java index 6ca8f31..419d599 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSelectionSituationServiceImpl.java @@ -169,6 +169,8 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl> safeList = list.stream().map(nutMap -> { - Map map = new HashMap<>(nutMap); - return map; - }).toList(); - - // 分组 - Map>> listMap = safeList.stream() - .collect(Collectors.groupingBy(n -> (String) n.get("welfareUnionName"))); + // 保留 NutMap 数据结构,避免 EasyPOI 在 Java 17 下反射普通 HashMap 导致工作表创建失败。 + Map> listMap = list.stream() + .collect(Collectors.groupingBy(n -> n.getString("welfareUnionName"))); // 构建 Excel 列 List entities = new ArrayList<>(); @@ -342,16 +349,16 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl { ExcelExportService service = new ExcelExportService(); ExportParams exportParams = new ExportParams(); exportParams.setSheetName(k); - exportParams.setType(ExcelType.HSSF); + exportParams.setType(ExcelType.XSSF); service.createSheetForMap(workbook, exportParams, entities, v); }); - CommonDownloadUtil.download("领取表.xls", workbook, response); + CommonDownloadUtil.download("领取表.xlsx", workbook, response); } /** diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSingleServiceImpl.java b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSingleServiceImpl.java index 512a8db..c1af776 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSingleServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareSingleServiceImpl.java @@ -4,11 +4,14 @@ 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.afterturn.easypoi.excel.export.ExcelExportService; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.base.utils.CommonDownloadUtil; +import com.budwk.app.sys.models.Sys_file; +import com.budwk.app.sys.utils.SysFileMinIoUtil; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.welfare.mode.WelfareExportEntityTc; @@ -31,9 +34,7 @@ import org.nutz.lang.util.NutMap; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; /** @@ -159,9 +160,10 @@ public class WelfareSingleServiceImpl extends BaseServiceImpl implements Welfare u.unitname AS unitName, u.unionname AS unionName, u.mobile, - IF(LOCATE('undefined',wpus.receiveAddress)>0,NULL,wpus.receiveAddress) as receiveAddress, - GROUP_CONCAT(DISTINCT wpso.optionName ,'(',wpus.selectNum,'份)') optionName, - wpus.courierNumber + IF(LOCATE('undefined',wpus.receiveAddress)>0,NULL,wpus.receiveAddress) as receiveAddress, + GROUP_CONCAT(DISTINCT wpso.optionName ,'(',wpus.selectNum,'份)') optionName, + wpus.courierNumber, + MAX(wpus.userSign) userSign FROM welfare_project_user_selection wpus LEFT JOIN `vw_user` u ON u.id = wpus.selectUserId @@ -183,6 +185,8 @@ public class WelfareSingleServiceImpl extends BaseServiceImpl implements Welfare @Override public Workbook exportReceiveDetail(String projectId, String unionId) { + // V4 原横向套餐签领表逻辑保留,当前按 V3 的“每个套餐一个工作表”格式执行。 + if (false) { Sql optionSql = Sqls.create(""" SELECT w2.id, w2.optionName @@ -282,9 +286,148 @@ public class WelfareSingleServiceImpl extends BaseServiceImpl implements Welfare Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, dataList); return workbook; + } + return exportReceiveDetailV3(projectId, unionId); } + /** + * 生成参考项目格式的分工会福利签领表:一个工作表展示人员、套餐份数和签字,并在末行汇总套餐份数。 + * + * @param projectId 福利项目ID + * @param unionId 分工会ID + * @return XLSX工作簿 + */ + private Workbook exportReceiveDetailV3(String projectId, String unionId) { + Sql optionSql = Sqls.create(""" + SELECT id, optionName + FROM welfare_project_subject_option + WHERE welfareId = @projectId + ORDER BY optionSort, id + """); + optionSql.setParam("projectId", projectId); + List options = listMap(optionSql); + + Sql sql = Sqls.create(""" + SELECT + wl.userId, + u.username AS userName, + u.loginname AS loginName, + wl.welfareUnitName AS unitName, + wpus.userSign, + wpus.selectOptionId, + wpus.selectNum + FROM welfare_list wl + LEFT JOIN sys_user u ON u.id = wl.userId + LEFT JOIN welfare_project_user_selection wpus ON wpus.selectUserId = wl.userId + AND wpus.welfareId = wl.projectId + WHERE wl.projectId = @projectId AND wl.welfareUnionId = @unionId + ORDER BY wl.welfareUnitName, u.loginname, wpus.selectOptionId + """); + sql.setParam("projectId", projectId); + sql.setParam("unionId", unionId); + List rows = listMap(sql); + rows.forEach(this::fillUserSign); + + Map> userRows = rows.stream().collect(Collectors.groupingBy( + row -> row.getString("userId"), LinkedHashMap::new, Collectors.toList())); + List exportRows = new ArrayList<>(); + int index = 1; + for (List selectedRows : userRows.values()) { + NutMap userRow = selectedRows.get(0); + NutMap exportRow = NutMap.NEW(); + exportRow.put("no", index++); + exportRow.put("userName", userRow.getString("userName")); + exportRow.put("loginName", userRow.getString("loginName")); + exportRow.put("unitName", userRow.getString("unitName")); + exportRow.put("qzBytes", selectedRows.stream() + .map(row -> row.get("qzBytes")) + .filter(Objects::nonNull) + .findFirst() + .orElse(null)); + for (NutMap option : options) { + int selectNum = selectedRows.stream() + .filter(row -> option.getString("id").equals(row.getString("selectOptionId"))) + .mapToInt(row -> row.getInt("selectNum", 0)) + .sum(); + exportRow.put(option.getString("id"), selectNum); + } + exportRows.add(exportRow); + } + + NutMap totalRow = NutMap.NEW(); + totalRow.put("no", "合计"); + for (NutMap option : options) { + int total = rows.stream() + .filter(row -> option.getString("id").equals(row.getString("selectOptionId"))) + .mapToInt(row -> row.getInt("selectNum", 0)) + .sum(); + totalRow.put(option.getString("id"), total); + } + exportRows.add(totalRow); + + List entities = new ArrayList<>(); + entities.add(new ExcelExportEntity("序号", "no", 10)); + entities.add(new ExcelExportEntity("姓名", "userName", 15)); + entities.add(new ExcelExportEntity("工号", "loginName", 18)); + ExcelExportEntity unitEntity = new ExcelExportEntity("所在单位", "unitName", 25); + unitEntity.setWrap(true); + entities.add(unitEntity); + for (NutMap option : options) { + entities.add(new ExcelExportEntity(option.getString("optionName"), option.getString("id"), 12)); + } + ExcelExportEntity signEntity = new ExcelExportEntity("签字", "qzBytes", 20); + signEntity.setType(2); + signEntity.setExportImageType(2); + entities.add(signEntity); + + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + return ExcelExportUtil.exportExcel(exportParams, entities, exportRows); + } + + /** 从 V4 文件存储读取签名图片,历史文件缺失时保留空白签字格。 */ + private void fillUserSign(NutMap row) { + String userSign = row.getString("userSign"); + if (StrUtil.isBlank(userSign)) { + return; + } + try { + Sys_file file = dao().fetch(Sys_file.class, Cnd.where(Sys_file::getDownloadPath, "=", userSign)); + if (file != null) { + row.put("qzBytes", SysFileMinIoUtil.getFileBytes(file.getBucket(), file.getStoragePath())); + } + } catch (Exception ignored) { + // 单个签名文件异常不能影响整份签领表导出。 + } + } + + /** 身份证仅显示前段信息,保持 V3 签领表的脱敏规则。 */ + private String maskIdCard(String idCard) { + if (StrUtil.isBlank(idCard) || idCard.length() <= 4) { + return idCard; + } + return idCard.substring(0, idCard.length() - 4) + "****"; + } + + /** 将历史收货信息拆为 V3 名单所需的收货人、联系方式和收货地址字段。 */ + private void fillV3ReceiveAddress(NutMap row) { + String receiveAddress = row.getString("receiveAddress"); + if (StrUtil.isBlank(receiveAddress)) { + return; + } + Map values = new HashMap<>(); + for (String pair : receiveAddress.split("[,,]")) { + String[] keyValue = pair.split("[::]", 2); + if (keyValue.length == 2) { + values.put(keyValue[0].trim(), keyValue[1].trim()); + } + } + row.put("recipient", values.get("收件人")); + row.put("phone", values.get("联系方式")); + row.put("address", values.get("收货地址")); + } + @Override public Object unclaimedData(String projectId, String unionId, Integer pageNumber, Integer pageSize) { @@ -333,6 +476,7 @@ public class WelfareSingleServiceImpl extends BaseServiceImpl implements Welfare } List list2 = list.stream().filter(v -> Strings.isNotBlank(v.getString("unionName"))).collect(Collectors.toList()); + list2.forEach(this::fillV3ReceiveAddress); List welfareExportEntityTcList = list2.stream() .map(map -> JSONUtil.toBean(Json.toJson(map), WelfareExportEntityTc.class)) @@ -340,15 +484,15 @@ public class WelfareSingleServiceImpl extends BaseServiceImpl implements Welfare welfareExportEntityTcList.forEach(v -> { if (StrUtil.isNotBlank(v.getUserSign())) { - ByteArrayOutputStream signOs = new ByteArrayOutputStream(); -// ftpService.download(v.getUserSign(), signOs); - byte[] imageBytes = signOs.toByteArray(); - if (Lang.isNotEmpty(imageBytes)) { - v.setQzBytes(imageBytes); - } else { - v.setQzBytes(null); + try { + Sys_file file = dao().fetch(Sys_file.class, + Cnd.where(Sys_file::getDownloadPath, "=", v.getUserSign())); + if (file != null) { + v.setQzBytes(SysFileMinIoUtil.getFileBytes(file.getBucket(), file.getStoragePath())); + } + } catch (Exception ignored) { + // 签名文件缺失时保留空白,不中断名单导出。 } - } }); ExportParams exportParams = new ExportParams(); diff --git a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareStatisticsServiceImpl.java b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareStatisticsServiceImpl.java index 476f5a8..925f7be 100644 --- a/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareStatisticsServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/welfare/service/impl/WelfareStatisticsServiceImpl.java @@ -17,6 +17,7 @@ import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.welfare.model.WelfareProject; import com.budwk.app.zhgh.welfare.model.WelfareProjectSubjectOption; +import com.budwk.app.zhgh.welfare.service.WelfareSingleService; import com.budwk.app.zhgh.welfare.service.WelfareStatisticsService; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Workbook; @@ -32,14 +33,15 @@ import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.lang.util.NutMap; import javax.servlet.http.HttpServletResponse; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; @IocBean(args = {"refer:dao"}) @Slf4j public class WelfareStatisticsServiceImpl extends BaseServiceImpl implements WelfareStatisticsService { + @org.nutz.ioc.loader.annotation.Inject + private WelfareSingleService welfareSingleService; + public WelfareStatisticsServiceImpl(Dao dao) { super(dao); } @@ -65,7 +67,7 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl welfareOptions = dao().query(WelfareProjectSubjectOption.class, Cnd.where(WelfareProjectSubjectOption::getWelfareId, "=", projectId)); + List welfareOptions = getNaturalSortedOptions(projectId); for (WelfareProjectSubjectOption welfareOption : welfareOptions) { dynamicTableColumns.add(NutMap.NEW().addv("label", welfareOption.getOptionName()).addv("prop", welfareOption.getId())); @@ -176,6 +178,8 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl exportEntities = new ArrayList<>(); @@ -434,6 +448,8 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl welfareOptions = dao().query(WelfareProjectSubjectOption.class, Cnd.where(WelfareProjectSubjectOption::getWelfareId, "=", projectId)); + List welfareOptions = getNaturalSortedOptions(projectId); for (WelfareProjectSubjectOption welfareOption : welfareOptions) { ExcelExportEntity entity = new ExcelExportEntity(welfareOption.getOptionName(), welfareOption.getId(), 20); entity.setType(10); @@ -543,4 +559,150 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl entities = new ArrayList<>(); + entities.add(new ExcelExportEntity("工号", "loginName", 20)); + entities.add(new ExcelExportEntity("姓名", "userName", 20)); + entities.add(new ExcelExportEntity("电话号码", "mobile", 20)); + entities.add(new ExcelExportEntity("在职状态", "userState", 20)); + entities.add(new ExcelExportEntity("人员类型", "personType", 20)); + entities.add(new ExcelExportEntity("所在分工会", "welfareUnionName", 30)); + entities.add(new ExcelExportEntity("所在单位", "welfareUnitName", 50)); + entities.add(new ExcelExportEntity("选择份数", "selectNum", 10)); + entities.add(new ExcelExportEntity("收货人", "recipient", 15)); + entities.add(new ExcelExportEntity("联系方式", "phone", 20)); + entities.add(new ExcelExportEntity("收货地址", "address", 60)); + + Sql sql = Sqls.create(""" + SELECT + u.loginname AS loginName, + u.username AS userName, + wpus.mobile, + wl.userState, + wl.personType, + wl.welfareUnionName, + wl.welfareUnitName, + wpus.receiveAddress, + wpus.selectNum, + wpus.selectOptionId + FROM welfare_project_user_selection wpus + LEFT JOIN welfare_list wl ON wl.userId = wpus.selectUserId AND wl.projectId = wpus.welfareId + LEFT JOIN sys_user u ON u.id = wpus.selectUserId + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("wpus.welfareId", "=", projectId); + cnd.and("wpus.selectNum", "!=", 0); + cnd.andEX("wl.welfareUnionId", "=", resolveExportUnionId(unionId)); + cnd.asc("wl.welfareUnionName").asc("wl.welfareUnitName").asc("u.loginname"); + sql.setCondition(cnd); + List rows = listMap(sql); + rows.forEach(this::fillV3ReceiveAddress); + + try (Workbook workbook = new XSSFWorkbook()) { + for (WelfareProjectSubjectOption option : getNaturalSortedOptions(projectId)) { + List optionRows = rows.stream() + .filter(row -> option.getId().equals(row.getString("selectOptionId"))) + // EasyPOI 生成工作表时会移除已处理行,必须传入可修改列表。 + .collect(Collectors.toCollection(ArrayList::new)); + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + exportParams.setTitle(option.getOptionName() + "选择情况"); + exportParams.setSheetName(option.getOptionName()); + new ExcelExportService().createSheetForMap(workbook, exportParams, entities, optionRows); + } + CommonDownloadUtil.download("按品牌导出选择情况表.xlsx", workbook, response); + } catch (Exception e) { + log.error("按品牌导出Excel失败", e); + } + } + + /** 分工会角色只能导出本人工会数据,校级角色可按页面筛选导出。 */ + private String resolveExportUnionId(String unionId) { + if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ADMIN.name()) + || AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WENTI_SPORTS.name())) { + return SecurityUtil.getUnionId(); + } + return unionId; + } + + /** 将历史收货信息拆为 V3 导出所需的收货人、联系方式和收货地址三列。 */ + private void fillV3ReceiveAddress(NutMap row) { + String receiveAddress = row.getString("receiveAddress"); + if (StrUtil.isBlank(receiveAddress)) { + return; + } + Map values = new HashMap<>(); + for (String pair : receiveAddress.split("[,,]")) { + String[] keyValue = pair.split("[::]", 2); + if (keyValue.length == 2) { + values.put(keyValue[0].trim(), keyValue[1].trim()); + } + } + row.put("recipient", values.get("收件人")); + row.put("phone", values.get("联系方式")); + row.put("address", values.get("收货地址")); + } + + /** + * 按套餐名称进行自然排序,数字片段按数值比较、英文字母忽略大小写比较,避免“套餐10”排在“套餐2”之前。 + */ + private List getNaturalSortedOptions(String projectId) { + List options = dao().query(WelfareProjectSubjectOption.class, + Cnd.where(WelfareProjectSubjectOption::getWelfareId, "=", projectId)); + options.sort(Comparator.comparing(WelfareProjectSubjectOption::getOptionName, this::compareOptionNames) + .thenComparing(option -> option.getOptionSort() == null ? Integer.MAX_VALUE : option.getOptionSort()) + .thenComparing(WelfareProjectSubjectOption::getId)); + return options; + } + + private int compareOptionNames(String left, String right) { + String leftName = StrUtil.blankToDefault(left, ""); + String rightName = StrUtil.blankToDefault(right, ""); + int leftIndex = 0; + int rightIndex = 0; + while (leftIndex < leftName.length() && rightIndex < rightName.length()) { + char leftChar = leftName.charAt(leftIndex); + char rightChar = rightName.charAt(rightIndex); + if (Character.isDigit(leftChar) && Character.isDigit(rightChar)) { + int leftEnd = leftIndex; + int rightEnd = rightIndex; + while (leftEnd < leftName.length() && Character.isDigit(leftName.charAt(leftEnd))) { + leftEnd++; + } + while (rightEnd < rightName.length() && Character.isDigit(rightName.charAt(rightEnd))) { + rightEnd++; + } + String leftNumber = leftName.substring(leftIndex, leftEnd).replaceFirst("^0+(?!$)", ""); + String rightNumber = rightName.substring(rightIndex, rightEnd).replaceFirst("^0+(?!$)", ""); + int numberCompare = Integer.compare(leftNumber.length(), rightNumber.length()); + if (numberCompare != 0) { + return numberCompare; + } + numberCompare = leftNumber.compareTo(rightNumber); + if (numberCompare != 0) { + return numberCompare; + } + leftIndex = leftEnd; + rightIndex = rightEnd; + continue; + } + int charCompare = String.valueOf(leftChar).compareToIgnoreCase(String.valueOf(rightChar)); + if (charCompare != 0) { + return charCompare; + } + leftIndex++; + rightIndex++; + } + return Integer.compare(leftName.length(), rightName.length()); + } } diff --git a/src/main/resources/views/platform/zhgh/welfare/projectMange/index.html b/src/main/resources/views/platform/zhgh/welfare/projectMange/index.html index 7f18c35..482a3dc 100644 --- a/src/main/resources/views/platform/zhgh/welfare/projectMange/index.html +++ b/src/main/resources/views/platform/zhgh/welfare/projectMange/index.html @@ -373,7 +373,6 @@ layout("/layouts/platform.html"){ formRules: { name: [{ required: true, message: "必填", trigger: ["blur", "change"] }], festival: [{ required: true, message: "必选", trigger: ["blur", "change"] }], - provideTime: [{ required: true, message: "必选", trigger: ["blur", "change"] }], choiceTime: [{ required: true, message: "必选", trigger: ["blur", "change"] }], provideAddress: [{ required: true, message: "必选", trigger: ["blur", "change"] }], provideMode: [{ required: true, message: "必选", trigger: ["blur", "change"] }], diff --git a/src/main/resources/views/platform/zhgh/welfare/selectionSituation/index.html b/src/main/resources/views/platform/zhgh/welfare/selectionSituation/index.html index 871cf11..0dd7c97 100644 --- a/src/main/resources/views/platform/zhgh/welfare/selectionSituation/index.html +++ b/src/main/resources/views/platform/zhgh/welfare/selectionSituation/index.html @@ -29,8 +29,8 @@ layout("/layouts/platform.html"){ - - + + @@ -66,10 +66,33 @@ layout("/layouts/platform.html"){ - - - + + + + + + + + + + + + + @@ -185,10 +208,13 @@ layout("/layouts/platform.html"){ pageForm: { searchName: "username", year: new Date().getFullYear().toString(), - isSelect: null + isSelect: null, + personTypes: [], + userStates: [] }, unionOptions: [], unitOptions: [], + aidFundMemberUserTypeOptions: [], projectOptions: [], tableColumns: [ { prop: "loginName", label: "工号" }, @@ -401,6 +427,9 @@ layout("/layouts/platform.html"){ this.getWelfareList() this.unionOptions = await this.$businessTool.listUnion() this.unitOptions = await this.$businessTool.listUnit() + // 人员分类下拉仅在当前页面排除离退休人员,不影响全局字典及其他页面。 + this.$set(this, "aidFundMemberUserTypeOptions", (await this.$businessTool.getDictOptions("AIDFUND_MEMBER_USER_TYPE")) + .filter((item) => item.code !== "离退休人员")) } }) diff --git a/src/main/resources/views/platform/zhgh/welfare/statistics/index.html b/src/main/resources/views/platform/zhgh/welfare/statistics/index.html index 0208572..ed61b40 100644 --- a/src/main/resources/views/platform/zhgh/welfare/statistics/index.html +++ b/src/main/resources/views/platform/zhgh/welfare/statistics/index.html @@ -33,15 +33,21 @@ layout("/layouts/platform.html"){ - 按福利选项导出 + + 提醒未选择人员 + 按品牌导出 + 导出已选名单 + 导出未选名单 {{row.unSelectedNum}} + + + @@ -113,7 +125,7 @@ layout("/layouts/platform.html"){ remindUnSelectedPlaceholder() { this.$message.info("提醒未选择人员功能待开发") }, - // 导出汇总表 + // V4 原汇总表导出方法保留,页面已改用 V3 风格的导出入口。 exportSummary() { this.$downLoad("/platform/welfare/statistics/exportSummary", { projectId: this.pageForm.projectId @@ -129,7 +141,7 @@ layout("/layouts/platform.html"){ this.$refs.unSelectedUserRef.onOpen(this.pageForm.projectId, row) }, - // 按福利选项导出 + // V4 原按福利选项导出方法保留,页面已改用 V3 的按品牌导出入口。 exportByWelfareOptions() { this.$downLoad("/platform/welfare/statistics/exportByWelfareOptions", { projectId: this.pageForm.projectId, @@ -137,6 +149,38 @@ layout("/layouts/platform.html"){ }) }, + // V3 按品牌导出:按套餐拆分工作表,并传递当前分工会筛选范围。 + exportByWelfareOptionsV3() { + this.$downLoad("/platform/welfare/statistics/exportByWelfareOptions", { + projectId: this.pageForm.projectId, + unionId: this.pageForm.unionId + }) + }, + + // V3 已选、未选名单导出:未选择分工会时由后台导出当前数据权限内的全部人员。 + exportReceiveDetailByUnionId(flag) { + this.$downLoad("/platform/welfare/statistics/exportReceiveDetailByUnionId", { + projectId: this.pageForm.projectId, + unionId: this.pageForm.unionId, + flag: flag + }) + }, + + // V3 单个分工会福利签领表导出。 + exportReceiveDetailByUnionIdWord(row) { + this.$downLoad("/platform/welfare/statistics/exportReceiveDetailByUnionIdWord", { + projectId: this.pageForm.projectId, + unionId: row.id + }) + }, + + // V3 全部分工会福利签领表导出,后台生成 ZIP 文件。 + allExportReceiveDetailByUnionIdWord() { + this.$downLoad("/platform/welfare/statistics/allExportReceiveDetailByUnionIdWord", { + projectId: this.pageForm.projectId + }) + }, + async pageData() { this.tableLoading = true const resp = await this.$axios.post("/platform/welfare/statistics/pageData", this.pageForm) diff --git a/src/main/resources/views/platform/zhgh/welfare/statistics/selectedUser.js b/src/main/resources/views/platform/zhgh/welfare/statistics/selectedUser.js index 3fe3029..6603251 100644 --- a/src/main/resources/views/platform/zhgh/welfare/statistics/selectedUser.js +++ b/src/main/resources/views/platform/zhgh/welfare/statistics/selectedUser.js @@ -6,7 +6,7 @@ const selectedUser = { - @@ -76,18 +76,6 @@ const addUser = { - - - - - - - { - if (res.code === 0) { - this.$set(this, "threeUnitOptions", res.data || []) - } else { - this.$message.error(res.msg || "三级单位查询失败") - } - }) - .catch(() => { - this.$message.error("三级单位查询失败,请稍后重试") - }) - }, - pageData() { this.tableLoading = true this.$axios diff --git a/src/main/resources/views/platform/zhgh/welfare/welfareUser/index.html b/src/main/resources/views/platform/zhgh/welfare/welfareUser/index.html index ac5be5b..b4499c0 100644 --- a/src/main/resources/views/platform/zhgh/welfare/welfareUser/index.html +++ b/src/main/resources/views/platform/zhgh/welfare/welfareUser/index.html @@ -25,7 +25,7 @@ layout("/layouts/platform.html"){ - + @@ -133,11 +133,6 @@ layout("/layouts/platform.html"){ - - - - @@ -148,6 +143,35 @@ layout("/layouts/platform.html"){ + + + + + 导出报销表 + + + 导入快递单号 + 删除人员 @@ -254,6 +278,8 @@ layout("/layouts/platform.html"){ width="700px" :extra_params="{projectId:pageForm.projectId}" > + +