From 64dbcba3c67fe05074f4f731beefdfd513d2d47b Mon Sep 17 00:00:00 2001 From: zhouhefeng Date: Mon, 20 Jul 2026 14:36:55 +0800 Subject: [PATCH] commit --- ...EnrollmentRegistrationApplyController.java | 2 + ...rollmentRegistrationSummaryController.java | 17 +- .../model/EnrollmentRegistration.java | 31 +- .../controller/CondolenceApplyController.java | 47 +- .../controller/CondolenceReadController.java | 185 +++-- .../condolence/model/Condolence.java | 28 + .../condolence/service/CondolenceService.java | 67 ++ .../service/impl/CondolenceServiceImpl.java | 535 +++++++++++++- .../enrollmentRegistration/apply/index.html | 250 ++++--- .../enrollmentRegistration/info.js | 33 +- .../staffbenefit/condolence/apply/index.html | 514 +++++++------ .../staffbenefit/condolence/common/info.js | 50 +- .../staffbenefit/condolence/read/index.html | 79 +- .../enrollmentRegistration/apply/index.html | 321 +++++---- .../enrollmentRegistration/info.js | 47 +- .../staffbenefit/condolence/apply/index.html | 680 ++++++++---------- .../staffbenefit/condolence/common/info.js | 46 +- 17 files changed, 1925 insertions(+), 1007 deletions(-) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationApplyController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationApplyController.java index afb35c0..c4a8d9a 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationApplyController.java @@ -92,6 +92,7 @@ public class EnrollmentRegistrationApplyController { @SaCheckPermission(value = {"enrollmentRegistration.apply", "h5.enrollmentRegistration.apply"}, mode = SaMode.OR) @ApiOperation("保存申请") @SLog(tag = "子女入学管理-子女入学登记", msg = "保存子女入学登记") + @Aop(TransAop.READ_COMMITTED) public Result save(@Param("data") EnrollmentRegistration enrollmentRegistration) { enrollmentRegistration.setApplyTime(DateUtil.now()); baseService.insertOrUpdate(enrollmentRegistration); @@ -117,6 +118,7 @@ public class EnrollmentRegistrationApplyController { @SaCheckPermission(value = {"enrollmentRegistration.apply", "h5.enrollmentRegistration.apply"}, mode = SaMode.OR) @ApiOperation("提交申请") @SLog(tag = "子女入学管理-子女入学登记", msg = "提交子女入学登记") + @Aop(TransAop.READ_COMMITTED) public Result submit(@Param("data") EnrollmentRegistration enrollmentRegistration) { if (StrUtil.isBlank(enrollmentRegistration.getId())) { enrollmentRegistration.setApplyTime(DateUtil.now()); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationSummaryController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationSummaryController.java index 67ff1fe..2f313cd 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationSummaryController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/controller/EnrollmentRegistrationSummaryController.java @@ -160,9 +160,22 @@ public class EnrollmentRegistrationSummaryController { List huKouFiles = Json.fromJsonAsList(JSONObject.class, item.getString("huKouFiles")); List birthCertificateFiles = item.getList("birthCertificateFiles", JSONObject.class); + List qualificationCertificateFiles = item.getList("qualificationCertificateFiles", JSONObject.class); + List employmentContractFiles = item.getList("employmentContractFiles", JSONObject.class); List mergedList = new ArrayList<>(); - mergedList.addAll(huKouFiles); - mergedList.addAll(birthCertificateFiles); + // 历史登记可能缺少新增材料,逐类判空后合并,避免导出旧数据时中断整个压缩包。 + if (huKouFiles != null) { + mergedList.addAll(huKouFiles); + } + if (birthCertificateFiles != null) { + mergedList.addAll(birthCertificateFiles); + } + if (qualificationCertificateFiles != null) { + mergedList.addAll(qualificationCertificateFiles); + } + if (employmentContractFiles != null) { + mergedList.addAll(employmentContractFiles); + } for (JSONObject file : mergedList) { String fileName = file.getStr("name"); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/model/EnrollmentRegistration.java b/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/model/EnrollmentRegistration.java index d5a16e8..a82cb77 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/model/EnrollmentRegistration.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/enrollmentRegistration/model/EnrollmentRegistration.java @@ -91,7 +91,7 @@ public class EnrollmentRegistration extends BaseModel implements Serializable { @Column @ColDefine(type = ColType.VARCHAR,width = 30) - @Comment("出身年月") + @Comment("出生日期") private String childrenBirthday; @Column @@ -111,14 +111,29 @@ public class EnrollmentRegistration extends BaseModel implements Serializable { @Column @ColDefine(type = ColType.VARCHAR,width = 50) - @Comment("拟报就读学校") + @Comment("拟选区域") private String childrenPlanSchool; + @Column + @ColDefine(type = ColType.VARCHAR,width = 100) + @Comment("拟选学校") + private String childrenPlanSchoolName; + @Column @ColDefine(type = ColType.VARCHAR,width = 50) - @Comment("子女户口所在地") + @Comment("户籍所在地") private String childrenHuKouAddress; + @Column + @ColDefine(type = ColType.VARCHAR,width = 100) + @Comment("职务、职称") + private String positionTitle; + + @Column + @ColDefine(type = ColType.VARCHAR,width = 30) + @Comment("用工方式") + private String employmentType; + @Column @ColDefine(type = ColType.VARCHAR,width = 50) @Comment("备注") @@ -139,6 +154,16 @@ public class EnrollmentRegistration extends BaseModel implements Serializable { @Comment("子女出生证照片") private List birthCertificateFiles; + @Column + @ColDefine(type = ColType.MYSQL_JSON) + @Comment("学历学位证或教师资格证") + private List qualificationCertificateFiles; + + @Column + @ColDefine(type = ColType.MYSQL_JSON) + @Comment("聘用合同复印件") + private List employmentContractFiles; + } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/controller/CondolenceApplyController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/controller/CondolenceApplyController.java index f43681a..a14d4f3 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/controller/CondolenceApplyController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/controller/CondolenceApplyController.java @@ -26,7 +26,6 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.nutz.aop.interceptor.ioc.TransAop; 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; @@ -52,8 +51,6 @@ import java.util.List; @Ok("json:full") public class CondolenceApplyController { - @Inject - private Dao dao; @Inject private FlowEngine flowEngine; @Inject @@ -71,13 +68,43 @@ public class CondolenceApplyController { @SaCheckPermission("h5.condolence.apply") public void h5Index() {} + /** + * 查询慰问申报截止日期。 + * + * @return Result,data 为 yyyy-MM-dd 格式的截止日期字符串;未配置时为空字符串 + */ + @At + @ApiOperation("查询慰问申报截止日期") + @SaCheckPermission(value = {"condolence.apply", "h5.condolence.apply"}, mode = SaMode.OR) + public Result getApplyEndDate() { + return Result.success().addData(condolenceService.getApplyEndDate()); + } + + /** + * 保存慰问申报截止日期,服务层会再次校验当前用户必须为系统管理员。 + * + * @param endDate 截止日期,格式为 yyyy-MM-dd + * @return 保存成功结果 + */ + @At + @ApiOperation("保存慰问申报截止日期") + @SaCheckPermission("condolence.apply") + @SLog(tag = "职工慰问系统-慰问申请", msg = "设置申报截止日期:${endDate}") + @Aop(TransAop.READ_COMMITTED) + public Result saveApplyEndDate(@Param("endDate") String endDate) { + condolenceService.saveApplyEndDate(endDate); + return Result.success("设置成功"); + } + @At @ApiOperation("保存申请") @SaCheckPermission(value = {"condolence.apply", "h5.condolence.apply"}, mode = SaMode.OR) @SLog(tag = "职工慰问系统-慰问申请", msg = "保存申请") + @Aop(TransAop.READ_COMMITTED) public Result save(@Param("data") Condolence condolence) { + condolenceService.checkApplyDeadline(condolence.getId()); if(StrUtil.isBlank(condolence.getId())) condolence.setCreateTime(DateUtil.now()); - dao.insertOrUpdate(condolence); + condolenceService.insertOrUpdate(condolence); return Result.success(); } @@ -87,8 +114,9 @@ public class CondolenceApplyController { @SLog(tag = "职工慰问系统-慰问申请", msg = "提交申请") @Aop(TransAop.READ_COMMITTED) public Result submit(@Param("data") Condolence condolence) { + condolenceService.checkApplyDeadline(condolence.getId()); if(StrUtil.isBlank(condolence.getId())) condolence.setCreateTime(DateUtil.now()); - dao.insertOrUpdate(condolence); + condolenceService.insertOrUpdate(condolence); // 开启流程实例 Dict args = Dict.create(); args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode()); @@ -108,7 +136,7 @@ public class CondolenceApplyController { @Aop(TransAop.READ_COMMITTED) @SaCheckPermission(value = {"condolence.apply", "h5.condolence.apply"}, mode = SaMode.OR) public Result submitAgain(@Param("data") Condolence condolence, @Param("taskId") Long taskId) { - dao.insertOrUpdate(condolence); + condolenceService.insertOrUpdate(condolence); Dict dict = Dict.create(); dict.set(FlowConst.PROCESS_TASK_ID_KEY, taskId); @@ -117,6 +145,12 @@ public class CondolenceApplyController { return Result.success(); } + /** + * 查询慰问申请可选择的补贴对象或收款人。 + * + * @param keyword 姓名或工号关键字;为空时按照当前用户角色范围返回人员 + * @return Result,data 为人员列表,包含人员 ID、姓名、工号、性别、出生年月、联系电话、单位和工会信息 + */ @At @ApiOperation("查询用户") @SaCheckPermission(value = {"condolence.apply", "h5.condolence.apply"}, mode = SaMode.OR) @@ -127,6 +161,7 @@ public class CondolenceApplyController { username as userName, loginname as loginName, sex, + birthday, mobile, technicalTitle, IFNULL(unitname, '暂无') as unitName, diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/controller/CondolenceReadController.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/controller/CondolenceReadController.java index 8c75efe..c388fc0 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/controller/CondolenceReadController.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/controller/CondolenceReadController.java @@ -1,30 +1,20 @@ package com.budwk.app.zhgh.staffbenefit.condolence.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.constant.RoleConstant; +import com.budwk.app.base.exception.BaseException; 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.base.utils.PageUtil; -import com.budwk.app.flow.entity.ProcessInstance; -import com.budwk.app.flow.enums.ProcessTaskStateEnum; -import com.budwk.app.sys.param.SysMsgSummaryPageForm; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; -import com.budwk.app.zhgh.staffbenefit.condolence.model.Condolence; import com.budwk.app.zhgh.staffbenefit.condolence.service.CondolenceService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; -import org.apache.poi.ss.usermodel.Workbook; 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; @@ -36,16 +26,9 @@ import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Param; import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import java.util.ArrayList; -import java.util.List; /** - * @ClassName CondolenceReadController - * @Author JyuHsin - * @Date 2025/7/29 16:25 - * @Version 1.0 - * @Description TODO + * 职工慰问阅览。 */ @Slf4j @IocBean @@ -63,22 +46,40 @@ public class CondolenceReadController { public void index() { } + /** + * 按月份范围及页面筛选条件分页查询慰问记录。 + * + * @param pageForm 分页、排序及姓名或工号关键字参数 + * @param startMonth 开始月份,格式为 yyyy-MM;为空时不限制开始月份 + * @param endMonth 结束月份,格式为 yyyy-MM;为空时不限制结束月份 + * @param type 慰问类型 ID + * @param unionId 申请人所属工会 ID + * @param unitId 申请人所属单位 ID + * @return Result,data 为包含 list、totalCount 等分页字段的 Pagination 数据 + */ @At @ApiOperation("分页查询") @SaCheckPermission("condolence.read") public Result pageData(PageForm pageForm, - @Param(value = "year") Integer year, + @Param(value = "startMonth") String startMonth, + @Param(value = "endMonth") String endMonth, @Param(value = "type") String type, @Param(value = "unionId") String unionId, @Param(value = "unitId") String unitId) { + // 当前节点只取进行中的任务;流程结束后没有活动任务,按实例状态显示“已完结”或“已拒绝”。 Sql sql = Sqls.create(""" SELECT info.*, ins.id AS instanceId, type.name as typeName, + COALESCE(NULLIF(info.typeCode, ''), type.code) AS exportTypeCode, ins.state instanceState, ins.processDefineId instanceProcessDefineId, - t.displayName taskName + CASE + WHEN ins.state = 20 THEN '已完结' + WHEN ins.state = 45 THEN '已拒绝' + ELSE t.displayName + END AS taskName FROM condolence info LEFT JOIN wf_process_instance ins ON ins.businessNo = info.id @@ -87,15 +88,17 @@ public class CondolenceReadController { processInstanceId, displayName, createdAt, - ROW_NUMBER() OVER (PARTITION BY processInstanceId ORDER BY createdAt DESC) AS rn + ROW_NUMBER() OVER (PARTITION BY processInstanceId ORDER BY createdAt DESC, id DESC) AS rn FROM wf_process_task + WHERE taskState = 10 ) t ON t.processInstanceId = ins.id AND t.rn = 1 LEFT JOIN condolence_type type ON info.type = type.id $condition """); Cnd cnd = Cnd.NEW(); cnd.andEX("info.type", "=", type); - cnd.andEX("YEAR(info.createTime)", "=", year); + cnd.andEX("DATE_FORMAT(info.createTime,'%Y-%m')", ">=", startMonth); + cnd.andEX("DATE_FORMAT(info.createTime,'%Y-%m')", "<=", endMonth); cnd.andEX("info.applyUnionId", "=", unionId); cnd.andEX("info.applyUnitId", "=", unitId); @@ -120,58 +123,108 @@ public class CondolenceReadController { return Result.success(pageVO); } + /** + * 导出申报人汇总 Excel。 + * + * @param pageForm 页面查询参数,searchKeyword 为慰问对象姓名或工号 + * @param startMonth 开始月份,格式为 yyyy-MM + * @param endMonth 结束月份,格式为 yyyy-MM + * @param type 慰问类型 ID + * @param unionId 申请人所属工会 ID + * @param unitId 申请人所属单位 ID + * @param response 文件下载响应,内容为 xlsx + */ @At @Ok("void") @SaCheckPermission("condolence.read") - @ApiOperation("导出职工慰问汇总表") + @ApiOperation("导出申报人汇总") public void onExport(PageForm pageForm, - @Param(value = "year") Integer year, + @Param(value = "startMonth") String startMonth, + @Param(value = "endMonth") String endMonth, @Param(value = "type") String type, @Param(value = "unionId") String unionId, @Param(value = "unitId") String unitId, HttpServletResponse response) { - //查询审核通过的数据 - Sql sql = Sqls.create(""" - select - con.*, - type.name as typeName - from - condolence con - left join wf_process_instance ins on ins.businessNo = con.id - left join condolence_type type on type.id = con.type - $condition - """); - Cnd cnd = Cnd.NEW(); - cnd.and(ProcessInstance::getState, "=", ProcessTaskStateEnum.FINISHED.getCode()); - cnd.andEX("con.type", "=", type); - cnd.andEX("YEAR(info.createTime)", "=", year); - cnd.andEX("con.applyUnionId", "=", unionId); - cnd.andEX("con.applyUnitId", "=", unitId); - if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { - SqlExpressionGroup seg = new SqlExpressionGroup(); - seg.or("info.helpUserName", "like", "%" + pageForm.getSearchKeyword() + "%"); - seg.or("info.helpLoginName", "like", "%" + pageForm.getSearchKeyword() + "%"); - cnd.and(seg); - } - if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { - cnd.and("con.applyUnionId", "=", SecurityUtil.getUnionId()); - } - sql.setCondition(cnd); - List list = condolenceService.listMap(sql); + validateExportMonthRange(startMonth, endMonth); + condolenceService.exportSummary(pageForm, startMonth, endMonth, type, unionId, unitId, response); + } - List exportEntities = new ArrayList<>(); - exportEntities.add(new ExcelExportEntity("慰问对象", "helpUserName", 20)); - exportEntities.add(new ExcelExportEntity("慰问对象工号", "helpLoginName", 20)); - exportEntities.add(new ExcelExportEntity("申请人", "applyUserName", 20)); - exportEntities.add(new ExcelExportEntity("所在工会", "helpUnionName", 20)); - exportEntities.add(new ExcelExportEntity("所在单位", "helpUnitName", 20)); - exportEntities.add(new ExcelExportEntity("慰问类型", "typeName", 20)); - exportEntities.add(new ExcelExportEntity("拟补贴金额", "money", 20)); - exportEntities.add(new ExcelExportEntity("申请理由", "remark", 20)); + /** + * 导出单条工会领条 Word。 + * + * @param id 慰问申请 ID + * @param response 文件下载响应,内容为 docx + */ + @At + @Ok("void") + @SaCheckPermission("condolence.read") + @ApiOperation("导出工会领条") + public void onExportReceipt(@Param("id") String id, HttpServletResponse response) { + if (StrUtil.isBlank(id)) { + throw new BaseException("慰问申请ID不能为空"); + } + condolenceService.exportReceipt(id, response); + } - ExportParams exportParams = new ExportParams(); - exportParams.setType(ExcelType.XSSF); - Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list); - CommonDownloadUtil.download("申报人汇总.xlsx", workbook, response); + /** + * 导出生育慰问补贴登记表 Word。 + * + * @param id 慰问申请 ID,且慰问类型必须为生育慰问或生育双胎慰问 + * @param response 文件下载响应,内容为 docx + */ + @At + @Ok("void") + @SaCheckPermission("condolence.read") + @ApiOperation("导出补贴登记表") + public void onExportSubsidyRegistration(@Param("id") String id, HttpServletResponse response) { + if (StrUtil.isBlank(id)) { + throw new BaseException("慰问申请ID不能为空"); + } + condolenceService.exportSubsidyRegistration(id, response); + } + + /** + * 按当前页面筛选条件导出慰问资料压缩包。 + * + * @param pageForm 页面查询参数,searchKeyword 为慰问对象姓名或工号 + * @param startMonth 开始月份,格式为 yyyy-MM + * @param endMonth 结束月份,格式为 yyyy-MM + * @param type 慰问类型 ID + * @param unionId 申请人所属工会 ID + * @param unitId 申请人所属单位 ID + * @param response 文件下载响应,内容为 zip + */ + @At + @Ok("void") + @SaCheckPermission("condolence.read") + @ApiOperation("导出慰问资料压缩包") + public void onExportZip(PageForm pageForm, + @Param(value = "startMonth") String startMonth, + @Param(value = "endMonth") String endMonth, + @Param(value = "type") String type, + @Param(value = "unionId") String unionId, + @Param(value = "unitId") String unitId, + HttpServletResponse response) { + validateExportMonthRange(startMonth, endMonth); + condolenceService.exportZip(pageForm, startMonth, endMonth, type, unionId, unitId, response); + } + + /** + * 校验批量导出的月份范围,避免缺少条件时误导出全部数据。 + * + * @param startMonth 开始月份,格式为 yyyy-MM + * @param endMonth 结束月份,格式为 yyyy-MM + */ + private void validateExportMonthRange(String startMonth, String endMonth) { + if (StrUtil.isBlank(startMonth) || StrUtil.isBlank(endMonth)) { + throw new BaseException("请选择需要导出的月份"); + } + if (!startMonth.matches("\\d{4}-(0[1-9]|1[0-2])") + || !endMonth.matches("\\d{4}-(0[1-9]|1[0-2])")) { + throw new BaseException("月份格式不正确"); + } + if (startMonth.compareTo(endMonth) > 0) { + throw new BaseException("开始月份不能晚于结束月份"); + } } } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/model/Condolence.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/model/Condolence.java index 8d6ce49..71afe2c 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/model/Condolence.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/model/Condolence.java @@ -100,6 +100,21 @@ public class Condolence extends BaseModel { @ColDefine(type = ColType.VARCHAR, width = 100) private String helpUnitName; + @Column + @Comment("补助人性别") + @ColDefine(type = ColType.VARCHAR, width = 10) + private String sex; + + @Column + @Comment("补助人联系电话") + @ColDefine(type = ColType.VARCHAR, width = 30) + private String mobile; + + @Column + @Comment("补助人出生年月") + @ColDefine(type = ColType.VARCHAR, width = 30) + private String birthday; + @Column @Comment("类型") @ColDefine(type = ColType.VARCHAR, width = 20) @@ -180,6 +195,11 @@ public class Condolence extends BaseModel { @ColDefine(type = ColType.MYSQL_JSON) private List files; + @Column + @Comment("纸质申请表") + @ColDefine(type = ColType.MYSQL_JSON) + private List applyFiles; + @Comment("签字") @ColDefine(type = ColType.VARCHAR, width = 255) @Column(hump = true) @@ -293,4 +313,12 @@ public class Condolence extends BaseModel { @ColDefine(type = ColType.INT) private Integer thisYearHospitalizationNum; + /** + * 旧系统慰问申请单据号,仅用于保留历史数据的原始编号。 + */ + @Column + @Comment("旧系统单据号") + @ColDefine(type = ColType.INT) + private Integer legacyDocumentNo; + } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/service/CondolenceService.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/service/CondolenceService.java index 37865c7..d849a07 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/service/CondolenceService.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/service/CondolenceService.java @@ -1,8 +1,11 @@ package com.budwk.app.zhgh.staffbenefit.condolence.service; import com.budwk.app.base.service.BaseService; +import com.budwk.app.base.param.PageForm; import com.budwk.app.zhgh.staffbenefit.condolence.model.Condolence; +import javax.servlet.http.HttpServletResponse; + /** * @ClassName CondolenceService * @Author JyuHsin @@ -11,4 +14,68 @@ import com.budwk.app.zhgh.staffbenefit.condolence.model.Condolence; * @Description TODO */ public interface CondolenceService extends BaseService { + /** + * 查询慰问申报截止日期。 + * + * @return 截止日期,格式为 yyyy-MM-dd;未配置时返回空字符串 + */ + String getApplyEndDate(); + + /** + * 保存慰问申报截止日期,仅允许系统管理员操作。 + * + * @param endDate 截止日期,格式为 yyyy-MM-dd + */ + void saveApplyEndDate(String endDate); + + /** + * 校验新慰问申请是否已超过申报截止日期。 + * + * @param condolenceId 慰问申请 ID;为空表示新申请,已有 ID 的草稿或退回申请不受截止日期限制 + */ + void checkApplyDeadline(String condolenceId); + + /** + * 导出审核完成的慰问申报人汇总表。 + * + * @param pageForm 页面查询参数,searchKeyword 为慰问对象姓名或工号 + * @param startMonth 开始月份,格式为 yyyy-MM + * @param endMonth 结束月份,格式为 yyyy-MM + * @param type 慰问类型 ID + * @param unionId 申请人所属工会 ID + * @param unitId 申请人所属单位 ID + * @param response HTTP 文件下载响应 + */ + void exportSummary(PageForm pageForm, String startMonth, String endMonth, String type, String unionId, String unitId, + HttpServletResponse response); + + /** + * 导出单条审核完成记录的工会领条 Word。 + * + * @param id 慰问申请 ID + * @param response HTTP 文件下载响应 + */ + void exportReceipt(String id, HttpServletResponse response); + + /** + * 导出单条审核完成的生育慰问补贴登记表 Word。 + * + * @param id 慰问申请 ID + * @param response HTTP 文件下载响应 + */ + void exportSubsidyRegistration(String id, HttpServletResponse response); + + /** + * 按页面查询条件批量生成慰问资料压缩包。 + * + * @param pageForm 页面查询参数,searchKeyword 为慰问对象姓名或工号 + * @param startMonth 开始月份,格式为 yyyy-MM + * @param endMonth 结束月份,格式为 yyyy-MM + * @param type 慰问类型 ID + * @param unionId 申请人所属工会 ID + * @param unitId 申请人所属单位 ID + * @param response HTTP 文件下载响应 + */ + void exportZip(PageForm pageForm, String startMonth, String endMonth, String type, String unionId, String unitId, + HttpServletResponse response); } diff --git a/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/service/impl/CondolenceServiceImpl.java b/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/service/impl/CondolenceServiceImpl.java index 2b6eece..5eb2be5 100644 --- a/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/service/impl/CondolenceServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/staffbenefit/condolence/service/impl/CondolenceServiceImpl.java @@ -1,24 +1,549 @@ package com.budwk.app.zhgh.staffbenefit.condolence.service.impl; +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.date.DateUtil; +import cn.hutool.core.lang.Dict; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.budwk.app.base.constant.RoleConstant; +import com.budwk.app.base.exception.BaseException; +import com.budwk.app.base.param.PageForm; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.base.utils.CommonDownloadUtil; +import com.budwk.app.base.utils.MoneyUtil; +import com.budwk.app.base.utils.SysOfficeTemplateUtil; +import com.budwk.app.flow.engine.FlowEngine; +import com.budwk.app.flow.entity.ProcessTask; +import com.budwk.app.flow.enums.ProcessInstanceStateEnum; +import com.budwk.app.flow.vo.ProcessTaskVO; +import com.budwk.app.sys.models.Sys_config; +import com.budwk.app.sys.models.Sys_file; +import com.budwk.app.sys.services.SysConfigService; +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.web.commons.base.Globals; import com.budwk.app.zhgh.staffbenefit.condolence.model.Condolence; import com.budwk.app.zhgh.staffbenefit.condolence.service.CondolenceService; +import com.deepoove.poi.XWPFTemplate; +import com.deepoove.poi.data.PictureRenderData; +import com.deepoove.poi.data.PictureType; +import com.deepoove.poi.data.Pictures; import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Workbook; +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.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.aop.interceptor.ioc.TransAop; +import org.nutz.lang.util.NutMap; + +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** - * @ClassName CondolenceServiceImpl - * @Author JyuHsin - * @Date 2025/7/29 11:22 - * @Version 1.0 - * @Description TODO + * 职工慰问业务服务。 */ @Slf4j @IocBean(args = {"refer:dao"}) public class CondolenceServiceImpl extends BaseServiceImpl implements CondolenceService { + private static final String RECEIPT_TEMPLATE_CODE = "condolence_lt"; + private static final String SUBSIDY_TEMPLATE_CODE = "zgww_must_visit"; + private static final String APPLY_END_DATE_CONFIG_KEY = "CondolenceApplyEndDate"; + private static final List BIRTH_TYPE_CODES = List.of("A", "B"); + + @Inject + private FlowEngine flowEngine; + @Inject + private SysOfficeTemplateUtil sysOfficeTemplateUtil; + @Inject + private SysConfigService sysConfigService; + public CondolenceServiceImpl(Dao dao) { super(dao); } + + @Override + public String getApplyEndDate() { + Sys_config config = sysConfigService.fetch(APPLY_END_DATE_CONFIG_KEY); + return config == null ? "" : StrUtil.blankToDefault(config.getConfigValue(), ""); + } + + /** + * 保存慰问申报截止日期。参数必须是 yyyy-MM-dd,且仅 SYSADMIN 可以修改。 + * + * @param endDate 截止日期,格式为 yyyy-MM-dd + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public void saveApplyEndDate(String endDate) { + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name())) { + throw new BaseException("仅系统管理员可以设置慰问申报时间"); + } + parseApplyEndDate(endDate); + Sys_config config = sysConfigService.fetch(APPLY_END_DATE_CONFIG_KEY); + if (config == null) { + config = new Sys_config(); + config.setConfigKey(APPLY_END_DATE_CONFIG_KEY); + config.setConfigValue(endDate); + config.setNote("慰问申报截止日期"); + sysConfigService.insert(config); + return; + } + config.setConfigValue(endDate); + sysConfigService.update(config); + } + + @Override + public void checkApplyDeadline(String condolenceId) { + // 与旧系统保持一致:已有申请可以继续保存或重新提交,仅限制新申请。 + if (StrUtil.isNotBlank(condolenceId)) { + return; + } + String endDate = getApplyEndDate(); + if (StrUtil.isBlank(endDate)) { + return; + } + LocalDate deadline = parseApplyEndDate(endDate); + if (!LocalDate.now().isBefore(deadline)) { + throw new BaseException("本次申报已截止请关注下次申报"); + } + } + + /** + * 校验并解析慰问申报截止日期。 + * + * @param endDate 截止日期,必须为 yyyy-MM-dd + * @return 解析后的日期 + */ + private LocalDate parseApplyEndDate(String endDate) { + if (StrUtil.isBlank(endDate)) { + throw new BaseException("申报截止日期不能为空"); + } + try { + return LocalDate.parse(endDate); + } catch (DateTimeParseException e) { + throw new BaseException("申报截止日期格式不正确,应为yyyy-MM-dd"); + } + } + + @Override + public void exportSummary(PageForm pageForm, String startMonth, String endMonth, String type, String unionId, + String unitId, + HttpServletResponse response) { + List list = queryFinishedList(pageForm, startMonth, endMonth, type, unionId, unitId); + List exportEntities = new ArrayList<>(); + exportEntities.add(new ExcelExportEntity("慰问对象", "helpUserName", 20)); + exportEntities.add(new ExcelExportEntity("慰问对象工号", "helpLoginName", 20)); + exportEntities.add(new ExcelExportEntity("申请人", "applyUserName", 20)); + exportEntities.add(new ExcelExportEntity("所在工会", "helpUnionName", 20)); + exportEntities.add(new ExcelExportEntity("所在单位", "helpUnitName", 20)); + exportEntities.add(new ExcelExportEntity("慰问类型", "typeName", 20)); + exportEntities.add(new ExcelExportEntity("拟补贴金额", "money", 20)); + exportEntities.add(new ExcelExportEntity("申请理由", "remark", 20)); + + // Excel 导出统一使用 XSSF,避免旧版 xls 行数和格式限制。 + ExportParams exportParams = new ExportParams(); + exportParams.setType(ExcelType.XSSF); + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list); + CommonDownloadUtil.download("申报人汇总.xlsx", workbook, response); + } + + @Override + public void exportReceipt(String id, HttpServletResponse response) { + NutMap record = findExportRecord(id); + if (!ProcessInstanceStateEnum.FINISHED.getCode().equals(record.getInt("instanceState"))) { + throw new BaseException("仅流程已完成的慰问申请可以导出工会领条"); + } + if (isBirthType(record.getString("typeCode"))) { + throw new BaseException("生育慰问请导出补贴登记表"); + } + + byte[] docx = buildDocx(record, false, RECEIPT_TEMPLATE_CODE); + String fileName = Globals.AppName + "工会领条_" + safeFileName(record.getString("helpUserName")) + ".docx"; + CommonDownloadUtil.download(fileName, docx, response); + } + + @Override + public void exportSubsidyRegistration(String id, HttpServletResponse response) { + NutMap record = findExportRecord(id); + if (!ProcessInstanceStateEnum.FINISHED.getCode().equals(record.getInt("instanceState"))) { + throw new BaseException("仅流程已完成的慰问申请可以导出补贴登记表"); + } + if (!isBirthType(record.getString("typeCode"))) { + throw new BaseException("仅生育慰问可以导出补贴登记表"); + } + + byte[] docx = buildDocx(record, true, SUBSIDY_TEMPLATE_CODE); + String fileName = Globals.AppName + "教职工生育补贴登记表_" + + safeFileName(record.getString("helpUserName")) + ".docx"; + CommonDownloadUtil.download(fileName, docx, response); + } + + @Override + public void exportZip(PageForm pageForm, String startMonth, String endMonth, String type, String unionId, + String unitId, + HttpServletResponse response) { + List list = queryFinishedList(pageForm, startMonth, endMonth, type, unionId, unitId); + if (list.isEmpty()) { + throw new BaseException("当前条件下没有已完成的慰问申请可导出"); + } + + String zipName = startMonth + "至" + endMonth + "慰问补贴.zip"; + response.setContentType("application/zip;charset=UTF-8"); + response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode(zipName)); + response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); + + // 直接向响应流写 ZIP,不在服务器生成临时目录,避免并发导出时残留文件。 + try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) { + for (NutMap row : list) { + NutMap record = findExportRecord(row.getString("id")); + boolean birthType = isBirthType(record.getString("typeCode")); + String folderName = safeFileName(record.getString("helpUserName") + "-" + + record.getString("typeName") + "-" + record.getString("id")); + String documentName = birthType ? Globals.AppName + "教职工生育补贴登记表.docx" + : Globals.AppName + "工会领条.docx"; + zipOutputStream.putNextEntry(new ZipEntry(folderName + "/" + safeFileName(documentName))); + String templateCode = birthType ? SUBSIDY_TEMPLATE_CODE : RECEIPT_TEMPLATE_CODE; + zipOutputStream.write(buildDocx(record, birthType, templateCode)); + zipOutputStream.closeEntry(); + } + zipOutputStream.finish(); + } catch (IOException e) { + log.error("职工慰问压缩包导出失败:{}", e.getMessage(), e); + throw new BaseException("职工慰问压缩包导出失败:{}", e.getMessage()); + } + } + + /** + * 查询符合页面筛选条件且 wf 流程已完成的慰问记录。 + * + * @param pageForm 页面分页和关键字参数,仅使用 searchKeyword + * @param startMonth 开始月份,格式为 yyyy-MM + * @param endMonth 结束月份,格式为 yyyy-MM + * @param type 慰问类型 ID + * @param unionId 申请人所属工会 ID + * @param unitId 申请人所属单位 ID + * @return 导出记录列表,每项包含慰问信息、类型名称和 wf 实例信息 + */ + private List queryFinishedList(PageForm pageForm, String startMonth, String endMonth, String type, + String unionId, String unitId) { + Sql sql = Sqls.create(""" + SELECT + con.*, + type.name AS typeName, + type.code AS currentTypeCode, + ins.id AS instanceId, + ins.state AS instanceState + FROM condolence con + LEFT JOIN wf_process_instance ins ON ins.businessNo = con.id + LEFT JOIN condolence_type type ON type.id = con.type + $condition + """); + Cnd cnd = Cnd.NEW(); + cnd.and("ins.state", "=", ProcessInstanceStateEnum.FINISHED.getCode()); + cnd.andEX("con.type", "=", type); + cnd.andEX("DATE_FORMAT(con.createTime,'%Y-%m')", ">=", startMonth); + cnd.andEX("DATE_FORMAT(con.createTime,'%Y-%m')", "<=", endMonth); + cnd.andEX("con.applyUnionId", "=", unionId); + cnd.andEX("con.applyUnitId", "=", unitId); + if (pageForm != null && StrUtil.isNotBlank(pageForm.getSearchKeyword())) { + SqlExpressionGroup group = new SqlExpressionGroup(); + group.or("con.helpUserName", "like", "%" + pageForm.getSearchKeyword() + "%"); + group.or("con.helpLoginName", "like", "%" + pageForm.getSearchKeyword() + "%"); + cnd.and(group); + } + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) { + cnd.and("con.applyUnionId", "=", SecurityUtil.getUnionId()); + } + cnd.desc("con.createTime"); + sql.setCondition(cnd); + return listMap(sql); + } + + /** + * 查询生成 Word 所需的完整记录。 + * + * @param id 慰问申请 ID + * @return 慰问信息、类型信息和 wf 实例信息 + */ + private NutMap findExportRecord(String id) { + Sql sql = Sqls.create(""" + SELECT + con.*, + type.name AS typeName, + type.code AS currentTypeCode, + helpUser.idCard AS exportIdCard, + helpUser.homeAddress AS exportHomeAddress, + COALESCE(NULLIF(con.sex, ''), helpUser.sex) AS exportSex, + COALESCE(NULLIF(con.mobile, ''), helpUser.mobile) AS exportMobile, + TIMESTAMPDIFF( + YEAR, + COALESCE(STR_TO_DATE(NULLIF(con.birthday, ''), '%Y-%m-%d'), helpUser.birthday), + CURDATE() + ) AS exportAge, + ins.id AS instanceId, + ins.state AS instanceState + FROM condolence con + LEFT JOIN wf_process_instance ins ON ins.businessNo = con.id + LEFT JOIN condolence_type type ON type.id = con.type + LEFT JOIN vw_user helpUser ON helpUser.id = con.helpUserId + WHERE con.id = @id + """).setParam("id", id); + sql.setCallback(Sqls.callback.map()); + dao().execute(sql); + NutMap record = (NutMap) sql.getResult(); + if (record == null) { + throw new BaseException("慰问申请不存在"); + } + // 历史数据可能未保存 typeCode,以类型表当前编码作为兼容值。 + if (StrUtil.isBlank(record.getString("typeCode"))) { + record.put("typeCode", record.getString("currentTypeCode")); + } + // 非校级管理员只能导出本工会数据,避免通过手工拼接 ID 越权下载。 + if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()) + && !StrUtil.equals(record.getString("applyUnionId"), SecurityUtil.getUnionId())) { + throw new BaseException("无权导出该慰问申请"); + } + return record; + } + + /** + * 组装模板数据并生成 Word 字节数组。 + * + * @param record 慰问信息,必须包含 instanceId、typeCode 和 files + * @param includeAttachmentPictures 是否把图片附件组装到模板 picFiles 列表 + * @param templateCode 系统 Office 模板编码,工会领条为 condolence_lt,补贴登记表为 zgww_must_visit + * @return docx 文件字节数组 + */ + private byte[] buildDocx(NutMap record, boolean includeAttachmentPictures, String templateCode) { + prepareTemplateData(record, includeAttachmentPictures); + try (InputStream templateStream = sysOfficeTemplateUtil.getTemplate(templateCode); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + XWPFTemplate.compile(templateStream).render(record).writeAndClose(outputStream); + return outputStream.toByteArray(); + } catch (Exception e) { + log.error("职工慰问 Word 导出失败,申请ID:{},错误信息:{}", record.getString("id"), e.getMessage(), e); + throw new BaseException("职工慰问 Word 导出失败:{}", e.getMessage()); + } + } + + /** + * 填充模板所需的金额大写、签名、审批意见和附件图片。 + * + * @param record 慰问记录 + * @param includeAttachmentPictures 是否读取 MinIO 图片附件 + */ + private void prepareTemplateData(NutMap record, boolean includeAttachmentPictures) { + record.put("schoolName", Globals.AppName); + record.put("be_username", record.getString("helpUserName")); + record.put("be_idcard", record.getString("exportIdCard")); + record.put("be_sex", record.getString("exportSex")); + record.put("age", record.get("exportAge") == null ? "" : record.get("exportAge")); + record.put("be_loginname", record.getString("helpLoginName")); + record.put("be_unionname", record.getString("helpUnionName")); + record.put("be_unitname", record.getString("helpUnitName")); + record.put("unitname", record.getString("helpUnitName")); + record.put("jtzz", record.getString("exportHomeAddress")); + record.put("mobile", record.getString("exportMobile")); + // 当前业务数据未保存配偶所在单位,模板字段保留为空,避免误用其他单位信息。 + record.put("poszdw", ""); + record.put("typename", record.getString("typeName")); + record.put("payee_username", record.getString("payUserName")); + record.put("payee_loginname", record.getString("payLoginName")); + record.put("payee_bank_number", record.getString("bankCardNumber")); + record.put("manager_name", StrUtil.blankToDefault(record.getString("handlerUserName"), + record.getString("applyUserName"))); + record.put("apply_time", record.getString("createTime")); + try { + String moneyUpper = record.get("money") == null ? "" : MoneyUtil.toRMBUpper(record.get("money").toString()); + record.put("moneyBig", moneyUpper); + record.put("money_big", moneyUpper); + } catch (Exception e) { + log.warn("慰问金额转大写失败,申请ID:{},金额:{}", record.getString("id"), record.get("money")); + record.put("moneyBig", ""); + record.put("money_big", ""); + } + + if (StrUtil.isNotBlank(record.getString("signature"))) { + PictureRenderData applicantSign = sysOfficeTemplateUtil.createPictureRenderData(record.getString("signature")); + record.put("applicantSign", applicantSign); + record.put("ms", applicantSign); + } + fillApprovalData(record); + if (includeAttachmentPictures) { + record.put("picFiles", buildAttachmentPictures(record)); + } + } + + /** + * 将 wf 已办任务转换为模板审批节点数据。 + * + * @param record 慰问记录,instanceId 为 wf 流程实例 ID + */ + private void fillApprovalData(NutMap record) { + Long instanceId = record.getLong("instanceId"); + if (instanceId == null) { + return; + } + List> approvals = new ArrayList<>(); + List doneTasks = flowEngine.processTaskService().getDoneTaskList(instanceId, null); + for (ProcessTask doneTask : doneTasks) { + ProcessTaskVO task = flowEngine.processTaskService().findById(doneTask.getId()); + if ("提交申请".equals(task.getDisplayName())) { + continue; + } + Dict taskFormData = task.getTaskFormData(); + Map approval = new HashMap<>(); + approval.put("nodeName", task.getDisplayName()); + approval.put("user", taskFormData.getStr("userName")); + approval.put("opinion", taskFormData.getStr("opinion")); + approval.put("date", task.getFinishTime() == null ? "" : DateUtil.format(task.getFinishTime(), "yyyy年MM月dd日")); + approval.put("time", task.getFinishTime()); + if (StrUtil.isNotBlank(taskFormData.getStr("sign"))) { + approval.put("sign", sysOfficeTemplateUtil.createPictureRenderData(taskFormData.getStr("sign"))); + } + approvals.add(approval); + bindApprovalNode(record, task.getDisplayName(), approval); + } + record.put("approvals", approvals); + } + + /** + * 同时提供新版和旧版模板常用的审批节点键,保证系统模板平滑迁移。 + * + * @param record 模板数据 + * @param nodeName wf 节点显示名称 + * @param approval 审批人、意见、日期和签名 + */ + private void bindApprovalNode(NutMap record, String nodeName, Map approval) { + if (StrUtil.contains(nodeName, "分工会")) { + record.put("fgh", approval); + record.put("ucr", approval.get("sign")); + record.put("ucrTime", approval.get("date")); + record.put("ucrOpinion", approval.get("opinion")); + } else if (StrUtil.contains(nodeName, "校工会")) { + record.put("xgh", approval); + record.put("vcr", approval.get("sign")); + record.put("vcrTime", approval.get("date")); + record.put("vcrOpinion", approval.get("opinion")); + } else if (StrUtil.containsAny(nodeName, "校领导", "学校领导", "主席")) { + record.put("xld", approval); + record.put("ecr", approval.get("sign")); + record.put("ecrTime", approval.get("date")); + record.put("ecrOpinion", approval.get("opinion")); + } + } + + /** + * 将上传组件保存的附件 JSON 转换为 poi-tl 图片列表。 + * + * @param record 慰问记录,files 为上传组件 complete_result 数组 + * @return 图片及原文件名列表,供模板循环渲染 + */ + private List> buildAttachmentPictures(NutMap record) { + List> pictures = new ArrayList<>(); + Object filesValue = record.get("files"); + if (filesValue == null) { + return pictures; + } + List files; + try { + String filesJson = filesValue instanceof String ? filesValue.toString() : JSONUtil.toJsonStr(filesValue); + files = JSONUtil.toList(filesJson, JSONObject.class); + } catch (Exception e) { + log.warn("慰问附件数据格式错误,申请ID:{}", record.getString("id")); + return pictures; + } + for (JSONObject file : files) { + String fileUrl = extractFileUrl(file); + String fileId = extractFileId(fileUrl); + if (StrUtil.isBlank(fileId)) { + continue; + } + Sys_file sysFile = dao().fetch(Sys_file.class, fileId); + if (sysFile == null) { + continue; + } + byte[] bytes = SysFileMinIoUtil.getFileBytes(sysFile.getBucket(), sysFile.getStoragePath()); + if (bytes == null || bytes.length == 0) { + continue; + } + try { + PictureType pictureType = PictureType.suggestFileType(bytes); + PictureRenderData picture = Pictures.ofBytes(bytes, pictureType).size(100, 100).create(); + Map pictureData = new HashMap<>(); + pictureData.put("file", picture); + pictureData.put("fileName", StrUtil.blankToDefault(file.getStr("name"), sysFile.getName())); + pictures.add(pictureData); + } catch (Exception e) { + // 非图片附件不参与 Word 图片循环,附件本身仍保留在系统文件中。 + log.debug("慰问附件不是可渲染图片,文件ID:{}", fileId); + } + } + return pictures; + } + + /** + * 从 complete_result 附件对象中读取下载地址。 + * + * @param file 上传组件附件对象 + * @return 下载地址或文件 ID + */ + private String extractFileUrl(JSONObject file) { + JSONObject response = file.getJSONObject("response"); + if (response != null && StrUtil.isNotBlank(response.getStr("data"))) { + return response.getStr("data"); + } + return file.getStr("url"); + } + + /** + * 从下载地址中截取 sys_file 主键。 + * + * @param fileUrl 下载地址或文件 ID + * @return sys_file 主键 + */ + private String extractFileId(String fileUrl) { + if (StrUtil.isBlank(fileUrl)) { + return null; + } + String fileId = fileUrl.contains("=") ? StrUtil.subAfter(fileUrl, "=", true) : fileUrl; + return StrUtil.subBefore(fileId, "&", false); + } + + private boolean isBirthType(String typeCode) { + return BIRTH_TYPE_CODES.contains(typeCode); + } + + /** + * 清理 ZIP 路径和下载文件名中的 Windows 非法字符及路径片段。 + * + * @param fileName 原文件名 + * @return 可安全用于 ZIP 条目的文件名 + */ + private String safeFileName(String fileName) { + String safeName = StrUtil.blankToDefault(fileName, "未命名").replaceAll("[\\\\/:*?\"<>|]", "_"); + return safeName.replace("..", "_"); + } } diff --git a/src/main/resources/views/platform/zhgh/dayofficework/enrollmentRegistration/apply/index.html b/src/main/resources/views/platform/zhgh/dayofficework/enrollmentRegistration/apply/index.html index 3512d4e..535aadb 100644 --- a/src/main/resources/views/platform/zhgh/dayofficework/enrollmentRegistration/apply/index.html +++ b/src/main/resources/views/platform/zhgh/dayofficework/enrollmentRegistration/apply/index.html @@ -4,11 +4,11 @@ layout("/layouts/platform.html"){
- + - - + + @@ -20,39 +20,6 @@ layout("/layouts/platform.html"){ - - {{formData.userName}} - - - - {{formData.loginName}} - - - - - - - - - - {{formData.unitName}} - - - - - - - - - - - @@ -73,6 +40,19 @@ layout("/layouts/platform.html"){ + + + + + + + @@ -82,31 +62,27 @@ layout("/layouts/platform.html"){ - - - - - - - + - + - + + + + + + + - - - + - + + + {{formData.userName}} + - - + {{formData.loginName}} + + + + - + - + + + + + + + + + {{formData.unitName}} + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + @@ -153,33 +195,32 @@ layout("/layouts/platform.html"){ - - 保存 - 提交 - 提交 - - + +
diff --git a/src/main/resources/views/platform/zhgh/staffbenefit/condolence/common/info.js b/src/main/resources/views/platform/zhgh/staffbenefit/condolence/common/info.js index 79252bf..bd2988d 100644 --- a/src/main/resources/views/platform/zhgh/staffbenefit/condolence/common/info.js +++ b/src/main/resources/views/platform/zhgh/staffbenefit/condolence/common/info.js @@ -5,40 +5,34 @@ const condolenceInfo = { 申请信息 点击查看流程图 - - {{ viewData.applyUserName }} - {{ viewData.applyLoginName }} - {{ viewData.applyUnitName }} - {{ viewData.applyUnionName }} - {{ viewData.helpUserName }} - - {{ viewData.certifierUserName }} - {{ viewData.typeName }} - {{ viewData.money }} - {{ viewData.occurTime }} - {{ viewData.bankCardNumber }} - {{ viewData.bankUserName }} - {{ viewData.bankOfDeposit }} - - + + {{ viewData.applyUserName }} + {{ viewData.createTime }} + {{ viewData.helpUserName }} + {{ viewData.sex }} + {{ viewData.mobile }} + {{ viewData.birthday }} + {{ viewData.typeName }} + {{ viewData.money }} 元 + + {{ viewData.payUserName }} + + + {{ viewData.bankCardNumber }} + +
{{ viewData.remark }}
- + + + 暂无纸质申请表 + + 暂无附件 - - - 暂无签字 -
- +