From 635c18767e7acdb76759f0dbd0246c3947f338d6 Mon Sep 17 00:00:00 2001 From: dk <1260500659@qq.com> Date: Sat, 13 Jun 2026 12:46:55 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=B7=A5=E4=BD=9C=E6=8A=A5=E5=91=8A):=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B7=A5=E4=BD=9C=E6=8A=A5=E5=91=8A=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E7=9A=84=E8=8B=A5=E5=B9=B2=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E5=8C=85=E8=BF=87=E5=AF=BC=E5=87=BA=E6=97=B6word=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E9=87=8C=E6=8D=A2=E8=A1=8C=E7=AC=A6=E5=8F=98=E6=88=90?= =?UTF-8?q?=E7=A9=BA=E6=A0=BC=E7=AC=A6=E7=9A=84=E9=97=AE=E9=A2=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application-local.yaml | 4 +- rdms-project/rdms-project-boot/pom.xml | 12 +- .../constant/ProjectObjectConstants.java | 5 + .../OvertimeApplicationController.java | 18 + .../OvertimeApplicationBatchActionReqVO.java | 23 + .../OvertimeApplicationBatchResultRespVO.java | 37 + .../overtime/OvertimeApplicationService.java | 6 + .../OvertimeApplicationServiceImpl.java | 127 +++ .../common/WorkReportCommonService.java | 31 +- .../WorkReportDefaultDraftService.java | 13 +- .../WorkReportContentExportService.java | 758 ++++++++++++------ .../src/main/resources/application-local.yaml | 8 +- .../work-report/weekly-report-template.docx | Bin 24346 -> 24511 bytes .../src/main/resources/application-local.yaml | 8 +- 14 files changed, 791 insertions(+), 259 deletions(-) create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationBatchActionReqVO.java create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationBatchResultRespVO.java diff --git a/rdms-gateway/src/main/resources/application-local.yaml b/rdms-gateway/src/main/resources/application-local.yaml index c1e4b08..811726f 100644 --- a/rdms-gateway/src/main/resources/application-local.yaml +++ b/rdms-gateway/src/main/resources/application-local.yaml @@ -6,10 +6,10 @@ spring: username: # Nacos 账号 password: # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: 1924bcfb-4eab-4c58-9003-4a37d5fc2949 # 命名空间。这里使用 dev 开发环境 + namespace: 0cd9c1b2-56ba-4e1d-a23b-f951392c46bf # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP config: # 【注册中心】配置项 - namespace: 1924bcfb-4eab-4c58-9003-4a37d5fc2949 # 命名空间。这里使用 dev 开发环境 + namespace: 0cd9c1b2-56ba-4e1d-a23b-f951392c46bf # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP #################### 监控相关配置 #################### diff --git a/rdms-project/rdms-project-boot/pom.xml b/rdms-project/rdms-project-boot/pom.xml index e24d952..e6354e2 100644 --- a/rdms-project/rdms-project-boot/pom.xml +++ b/rdms-project/rdms-project-boot/pom.xml @@ -66,9 +66,15 @@ - org.apache.poi - poi-ooxml - 5.4.1 + commons-io + commons-io + 2.22.0 + + + + org.docx4j + docx4j-JAXB-ReferenceImpl + 11.5.14 diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/ProjectObjectConstants.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/ProjectObjectConstants.java index b2539fd..5c47b1c 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/ProjectObjectConstants.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/ProjectObjectConstants.java @@ -20,6 +20,11 @@ public final class ProjectObjectConstants { */ public static final String MANAGER_ROLE_CODE = "project_manager"; + /** + * 项目技术负责人对象角色编码。 + */ + public static final String TECHNICAL_OWNER_ROLE_CODE = "technical_owner"; + /** * 项目游客对象角色编码。创建人未成为项目成员时,用于返回只读上下文菜单。 */ diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/OvertimeApplicationController.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/OvertimeApplicationController.java index 19dc43a..bdeac0d 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/OvertimeApplicationController.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/OvertimeApplicationController.java @@ -6,6 +6,8 @@ import com.njcn.rdms.framework.common.pojo.PageResult; import com.njcn.rdms.framework.excel.core.util.ExcelUtils; import com.njcn.rdms.module.project.constant.OvertimeApplicationConstants; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationApprovalRecordRespVO; +import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationBatchActionReqVO; +import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationBatchResultRespVO; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationExportVO; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO; @@ -108,6 +110,22 @@ public class OvertimeApplicationController { return success(true); } + @PostMapping("/batch-approve") + @Operation(summary = "批量审核通过加班申请") + @PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_APPROVE + "')") + public CommonResult batchApprove( + @Valid @RequestBody OvertimeApplicationBatchActionReqVO reqVO) { + return success(overtimeApplicationService.batchApprove(reqVO)); + } + + @PostMapping("/batch-reject") + @Operation(summary = "批量审核退回加班申请") + @PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_APPROVE + "')") + public CommonResult batchReject( + @Valid @RequestBody OvertimeApplicationBatchActionReqVO reqVO) { + return success(overtimeApplicationService.batchReject(reqVO)); + } + @DeleteMapping("/{id}") @Operation(summary = "删除已退回的加班申请") @PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_DELETE + "')") diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationBatchActionReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationBatchActionReqVO.java new file mode 100644 index 0000000..e049a1b --- /dev/null +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationBatchActionReqVO.java @@ -0,0 +1,23 @@ +package com.njcn.rdms.module.project.controller.admin.overtime.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 加班申请批量审批 Request VO") +@Data +public class OvertimeApplicationBatchActionReqVO { + + @Schema(description = "加班申请编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]") + @NotEmpty(message = "加班申请编号列表不能为空") + private List ids; + + @Schema(description = "审批意见。是否必填以状态机配置为准;当前退回必填", + example = "已确认,同意加班") + @Size(max = 1000, message = "审批意见长度不能超过 1000 个字符") + private String reason; + +} diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationBatchResultRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationBatchResultRespVO.java new file mode 100644 index 0000000..ff32849 --- /dev/null +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationBatchResultRespVO.java @@ -0,0 +1,37 @@ +package com.njcn.rdms.module.project.controller.admin.overtime.vo; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Schema(description = "管理后台 - 加班申请批量审批结果 Response VO") +@Data +public class OvertimeApplicationBatchResultRespVO { + + @Schema(description = "成功处理的数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + private Integer successCount; + + @Schema(description = "失败的数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer failCount; + + @Schema(description = "失败的加班申请编号列表") + private List failItems = new ArrayList<>(); + + @Data + @Schema(description = "批量审批失败项") + public static class FailItem { + + @Schema(description = "加班申请编号", example = "3") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + @Schema(description = "失败原因", example = "当前状态不允许该操作") + private String reason; + + } + +} diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/overtime/OvertimeApplicationService.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/overtime/OvertimeApplicationService.java index c67cc10..4c56288 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/overtime/OvertimeApplicationService.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/overtime/OvertimeApplicationService.java @@ -2,6 +2,8 @@ package com.njcn.rdms.module.project.service.overtime; import com.njcn.rdms.framework.common.pojo.PageResult; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationApprovalRecordRespVO; +import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationBatchActionReqVO; +import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationBatchResultRespVO; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationExportVO; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO; @@ -22,6 +24,10 @@ public interface OvertimeApplicationService { void reject(Long id, OvertimeApplicationStatusActionReqVO reqVO); + OvertimeApplicationBatchResultRespVO batchApprove(OvertimeApplicationBatchActionReqVO reqVO); + + OvertimeApplicationBatchResultRespVO batchReject(OvertimeApplicationBatchActionReqVO reqVO); + void deleteApplication(Long id); OvertimeApplicationRespVO getApplication(Long id); diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/overtime/OvertimeApplicationServiceImpl.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/overtime/OvertimeApplicationServiceImpl.java index 6b165b1..34cc379 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/overtime/OvertimeApplicationServiceImpl.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/overtime/OvertimeApplicationServiceImpl.java @@ -5,9 +5,12 @@ import com.njcn.rdms.framework.common.pojo.PageParam; import com.njcn.rdms.framework.common.pojo.PageResult; import com.njcn.rdms.framework.common.util.json.JsonUtils; import com.njcn.rdms.framework.common.util.object.BeanUtils; +import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX; import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils; import com.njcn.rdms.module.project.constant.OvertimeApplicationConstants; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationApprovalRecordRespVO; +import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationBatchActionReqVO; +import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationBatchResultRespVO; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationExportVO; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO; import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO; @@ -37,6 +40,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -137,6 +141,16 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic processApprovalAction(id, OvertimeApplicationConstants.ACTION_REJECT, reqVO); } + @Override + public OvertimeApplicationBatchResultRespVO batchApprove(OvertimeApplicationBatchActionReqVO reqVO) { + return processBatchApprovalAction(reqVO, OvertimeApplicationConstants.ACTION_APPROVE); + } + + @Override + public OvertimeApplicationBatchResultRespVO batchReject(OvertimeApplicationBatchActionReqVO reqVO) { + return processBatchApprovalAction(reqVO, OvertimeApplicationConstants.ACTION_REJECT); + } + @Override @Transactional(rollbackFor = Exception.class) public void deleteApplication(Long id) { @@ -229,6 +243,119 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic writeAuditLog(after, actionCode, fromStatus, transition.getToStatusCode(), null, reason, null); } + private OvertimeApplicationBatchResultRespVO processBatchApprovalAction( + OvertimeApplicationBatchActionReqVO reqVO, String actionCode) { + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + String loginUserName = defaultText(SecurityFrameworkUtils.getLoginUserNickname()); + String reason = normalizeNullableText(reqVO.getReason()); + + // 1. 校验状态机流转(统一校验一次,批量场景下 fromStatus 相同) + ObjectStatusTransitionDO transition = validateTransition(OvertimeApplicationConstants.STATUS_PENDING, actionCode, reason); + LocalDateTime approvalTime = LocalDateTime.now(); + + // 2. 批量查询所有申请 + List applications = overtimeApplicationMapper.selectBatchIds(reqVO.getIds()); + Map appMap = applications.stream() + .collect(Collectors.toMap(OvertimeApplicationDO::getId, a -> a)); + + // 3. 分类:可处理的 vs 需跳过的 + List validList = new ArrayList<>(); + OvertimeApplicationBatchResultRespVO result = new OvertimeApplicationBatchResultRespVO(); + for (Long id : reqVO.getIds()) { + OvertimeApplicationDO app = appMap.get(id); + if (app == null) { + addFailItem(result, id, "加班申请不存在"); + } else if (!Objects.equals(app.getApproverId(), loginUserId)) { + addFailItem(result, id, "您不是该申请的审批人"); + } else if (!Objects.equals(app.getStatusCode(), OvertimeApplicationConstants.STATUS_PENDING)) { + addFailItem(result, id, "当前状态不允许该操作"); + } else { + validList.add(app); + } + } + + if (validList.isEmpty()) { + result.setSuccessCount(0); + result.setFailCount(result.getFailItems().size()); + return result; + } + + // 4. 批量更新申请状态 + List validIds = validList.stream().map(OvertimeApplicationDO::getId).collect(Collectors.toList()); + OvertimeApplicationDO update = new OvertimeApplicationDO(); + update.setStatusCode(transition.getToStatusCode()); + update.setApprovalComment(reason); + update.setApprovalTime(approvalTime); + overtimeApplicationMapper.update(update, new LambdaQueryWrapperX() + .in(OvertimeApplicationDO::getId, validIds) + .eq(OvertimeApplicationDO::getApproverId, loginUserId) + .eq(OvertimeApplicationDO::getStatusCode, OvertimeApplicationConstants.STATUS_PENDING)); + + // 5. 批量写状态日志 + List statusLogs = new ArrayList<>(); + for (OvertimeApplicationDO original : validList) { + OvertimeApplicationStatusLogDO log = new OvertimeApplicationStatusLogDO(); + log.setApplicationId(original.getId()); + log.setActionType(actionCode); + log.setFromStatus(OvertimeApplicationConstants.STATUS_PENDING); + log.setToStatus(transition.getToStatusCode()); + log.setReason(reason); + log.setOperatorUserId(loginUserId); + log.setOperatorName(loginUserName); + log.setApplicantNameSnapshot(original.getApplicantName()); + log.setOvertimeDateSnapshot(original.getOvertimeDate()); + log.setOvertimeDurationSnapshot(original.getOvertimeDuration()); + log.setRemark(buildSnapshotRemark(original)); + statusLogs.add(log); + } + overtimeApplicationStatusLogMapper.insertBatch(statusLogs); + + // 6. 批量写审批记录(需要 statusLogId,逐条设置后批量插入) + List approvalRecords = new ArrayList<>(); + for (int i = 0; i < validList.size(); i++) { + OvertimeApplicationDO original = validList.get(i); + OvertimeApplicationStatusLogDO statusLog = statusLogs.get(i); + OvertimeApplicationApprovalRecordDO record = new OvertimeApplicationApprovalRecordDO(); + record.setOvertimeApplicationId(original.getId()); + record.setStatusLogId(statusLog.getId()); + record.setApprovalRound(overtimeApplicationApprovalRecordMapper.countByApplicationId(original.getId()) + 1); + record.setConclusion(transition.getToStatusCode()); + record.setOpinion(reason); + record.setAuditorUserId(loginUserId); + record.setAuditorName(loginUserName); + approvalRecords.add(record); + } + overtimeApplicationApprovalRecordMapper.insertBatch(approvalRecords); + + // 7. 批量写审计日志 + List auditLogs = new ArrayList<>(); + for (OvertimeApplicationDO original : validList) { + BizAuditLogDO auditLog = new BizAuditLogDO(); + auditLog.setBizType(OvertimeApplicationConstants.BIZ_TYPE); + auditLog.setBizId(original.getId()); + auditLog.setActionType(actionCode); + auditLog.setFromStatus(OvertimeApplicationConstants.STATUS_PENDING); + auditLog.setToStatus(transition.getToStatusCode()); + auditLog.setReason(reason); + auditLog.setOperatorUserId(loginUserId); + auditLog.setOperatorName(loginUserName); + auditLog.setRemark(buildSnapshotRemark(original)); + auditLogs.add(auditLog); + } + bizAuditLogMapper.insertBatch(auditLogs); + + result.setSuccessCount(validList.size()); + result.setFailCount(result.getFailItems().size()); + return result; + } + + private void addFailItem(OvertimeApplicationBatchResultRespVO result, Long id, String reason) { + OvertimeApplicationBatchResultRespVO.FailItem failItem = new OvertimeApplicationBatchResultRespVO.FailItem(); + failItem.setId(id); + failItem.setReason(reason); + result.getFailItems().add(failItem); + } + private OvertimeApplicationDO validateApplicationExists(Long id) { OvertimeApplicationDO application = overtimeApplicationMapper.selectById(id); if (application == null) { diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/common/WorkReportCommonService.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/common/WorkReportCommonService.java index 1d8fa6b..9189a36 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/common/WorkReportCommonService.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/common/WorkReportCommonService.java @@ -52,6 +52,8 @@ import com.njcn.rdms.module.system.api.dept.DeptApi; import com.njcn.rdms.module.system.api.dept.PostApi; import com.njcn.rdms.module.system.api.dept.dto.DeptRespDTO; import com.njcn.rdms.module.system.api.dept.dto.PostRespDTO; +import com.njcn.rdms.module.system.api.permission.ObjectPermissionApi; +import com.njcn.rdms.module.system.api.permission.dto.ObjectRoleRespDTO; import com.njcn.rdms.module.system.api.user.AdminUserApi; import com.njcn.rdms.module.system.api.user.UserManagementRelationApi; import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO; @@ -117,6 +119,8 @@ public class WorkReportCommonService { @Resource private UserManagementRelationApi userManagementRelationApi; @Resource + private ObjectPermissionApi objectPermissionApi; + @Resource private ProjectMapper projectMapper; @Resource private UserObjectRoleMapper userObjectRoleMapper; @@ -443,6 +447,7 @@ public class WorkReportCommonService { respVO.setProjectName(project.getProjectName()); respVO.setProjectOwnerId(profile.userId()); respVO.setProjectOwnerName(profile.userName()); + respVO.setTechnicalOwnerName(resolveProjectTechnicalOwnerName(projectId)); respVO.setProjectMemberSnapshot(BeanUtils.toBean(buildProjectMemberSnapshot(projectId), WorkReportMemberSnapshotRespVO.class)); respVO.setSupervisorUserId(profile.directManagerId()); @@ -870,6 +875,29 @@ public class WorkReportCommonService { return user; } + public String resolveProjectTechnicalOwnerName(Long projectId) { + if (projectId == null) { + return null; + } + ObjectRoleRespDTO technicalOwnerRole = objectPermissionApi.getObjectRoleByCode( + ProjectObjectConstants.TECHNICAL_OWNER_ROLE_CODE, + com.njcn.rdms.module.project.constant.ObjectRoleConstants.ROLE_SCOPE_OBJECT, + ProjectObjectConstants.OBJECT_TYPE).getCheckedData(); + if (technicalOwnerRole == null || technicalOwnerRole.getId() == null) { + throw exception(ErrorCodeConstants.PROJECT_INTERNAL_ROLE_NOT_CONFIGURED, + ProjectObjectConstants.TECHNICAL_OWNER_ROLE_CODE); + } + for (UserObjectRoleDO member : userObjectRoleMapper.selectListByObject(ProjectObjectConstants.OBJECT_TYPE, projectId)) { + if (member == null || !Objects.equals(member.getStatus(), 0) + || !Objects.equals(member.getRoleId(), technicalOwnerRole.getId()) + || member.getUserId() == null) { + continue; + } + return defaultText(loadUser(member.getUserId()).getNickname()); + } + return null; + } + private ProjectDO validateProjectExists(Long projectId) { ProjectDO project = projectMapper.selectById(projectId); if (project == null) { @@ -941,6 +969,7 @@ public class WorkReportCommonService { target.setProjectName(project.getProjectName()); target.setProjectOwnerId(profile.userId()); target.setProjectOwnerName(profile.userName()); + target.setTechnicalOwnerName(resolveProjectTechnicalOwnerName(project.getId())); target.setProjectMemberSnapshot(buildProjectMemberSnapshot(project.getId())); target.setSupervisorUserId(profile.directManagerId()); target.setSupervisorName(profile.directManagerName()); @@ -1358,4 +1387,4 @@ public class WorkReportCommonService { private record CurrentUserProfile(Long userId, String userName, String deptName, String postName, Long directManagerId, String directManagerName) { } -} +} \ No newline at end of file diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/defaultdraft/WorkReportDefaultDraftService.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/defaultdraft/WorkReportDefaultDraftService.java index be9848e..3bab8ba 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/defaultdraft/WorkReportDefaultDraftService.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/defaultdraft/WorkReportDefaultDraftService.java @@ -33,6 +33,7 @@ import com.njcn.rdms.module.project.dal.mysql.project.task.ProjectTaskMapper; import com.njcn.rdms.module.project.dal.mysql.project.task.TaskWorklogMapper; import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper; import com.njcn.rdms.module.project.enums.ErrorCodeConstants; +import com.njcn.rdms.module.project.service.workreport.common.WorkReportCommonService; import com.njcn.rdms.module.system.api.dept.DeptApi; import com.njcn.rdms.module.system.api.dept.PostApi; import com.njcn.rdms.module.system.api.dept.dto.DeptRespDTO; @@ -91,6 +92,8 @@ public class WorkReportDefaultDraftService { private PersonalItemMapper personalItemMapper; @Resource private ProjectExecutionMapper projectExecutionMapper; + @Resource + private WorkReportCommonService workReportCommonService; public WeeklyReportRespVO previewWeeklyDefaultDraft(WeeklyReportDefaultDraftReqVO reqVO) { validatePeriod(reqVO.getPeriodStartDate(), reqVO.getPeriodEndDate()); @@ -155,7 +158,7 @@ public class WorkReportDefaultDraftService { respVO.setProjectName(project.getProjectName()); respVO.setProjectOwnerId(profile.userId()); respVO.setProjectOwnerName(profile.userName()); - respVO.setTechnicalOwnerName(resolveProjectManagerName(project.getManagerUserId())); + respVO.setTechnicalOwnerName(workReportCommonService.resolveProjectTechnicalOwnerName(project.getId())); respVO.setProjectMemberSnapshot(BeanUtils.toBean(buildProjectMemberSnapshot(projectId), WorkReportMemberSnapshotRespVO.class)); respVO.setSupervisorUserId(profile.directManagerId()); @@ -525,14 +528,6 @@ public class WorkReportDefaultDraftService { return user; } - private String resolveProjectManagerName(Long managerUserId) { - if (managerUserId == null) { - return null; - } - AdminUserRespDTO manager = loadUser(managerUserId); - return safeText(manager.getNickname()); - } - private List buildProjectMemberSnapshot(Long projectId) { List members = userObjectRoleMapper.selectListByObject(ProjectObjectConstants.OBJECT_TYPE, projectId); Set userIds = new LinkedHashSet<>(); diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/export/WorkReportContentExportService.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/export/WorkReportContentExportService.java index cd44aac..0a526d9 100644 --- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/export/WorkReportContentExportService.java +++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/export/WorkReportContentExportService.java @@ -21,9 +21,12 @@ import com.njcn.rdms.module.project.enums.ProjectDictTypeConstants; import com.njcn.rdms.module.project.service.workreport.common.WorkReportCommonService; import com.njcn.rdms.module.system.api.dict.DictDataApi; import jakarta.annotation.Resource; -import org.apache.poi.xwpf.usermodel.*; -import org.apache.xmlbeans.XmlException; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow; +import org.docx4j.XmlUtils; +import org.docx4j.jaxb.Context; +import org.docx4j.model.table.TblFactory; +import org.docx4j.openpackaging.exceptions.Docx4JException; +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.wml.*; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @@ -32,6 +35,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; +import java.math.BigInteger; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -52,6 +56,10 @@ public class WorkReportContentExportService { private static final String PROJECT_TEMPLATE_PATH = "templates/work-report/project-report-template.docx"; private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd"); private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm"); + private static final ObjectFactory WML_FACTORY = Context.getWmlObjectFactory(); + private static final BigInteger DEFAULT_FONT_SIZE = BigInteger.valueOf(18); + private static final BigInteger TITLE_FONT_SIZE = BigInteger.valueOf(36); + private static final BigInteger SECTION_TITLE_FONT_SIZE = BigInteger.valueOf(24); @Resource private WorkReportCommonService workReportCommonService; @@ -174,20 +182,21 @@ public class WorkReportContentExportService { } private DocumentContent buildWeeklyTemplateDocument(WeeklyReportRespVO report, ClassPathResource templateResource) { - try (InputStream inputStream = templateResource.getInputStream(); - XWPFDocument document = new XWPFDocument(inputStream)) { + try (InputStream inputStream = templateResource.getInputStream()) { + WordprocessingMLPackage document = WordprocessingMLPackage.load(inputStream); replacePlaceholders(document, buildWeeklyTemplateData(report)); renderPersonalReviewTable(document, report.getReviewItems()); renderPersonalPlanTable(document, report.getPlanItems()); return new DocumentContent(buildFilename("个人周报", report.getReporterName(), report.getPeriodLabel()), toBytes(document)); - } catch (IOException e) { + } catch (IOException | Docx4JException e) { throw new IllegalStateException("生成周报模板导出文件失败", e); } } private DocumentContent buildWeeklyFallbackDocument(WeeklyReportRespVO report) { - try (XWPFDocument document = new XWPFDocument()) { + try { + WordprocessingMLPackage document = WordprocessingMLPackage.createPackage(); addTitle(document, "个人周报"); addParagraph(document, "填报周期:" + formatPeriod(report), false); addPersonalBaseTable(document, report.getReporterName(), report.getReporterDeptName(), @@ -207,7 +216,7 @@ public class WorkReportContentExportService { addPersonalPlanTable(document, report.getPlanItems(), false); return new DocumentContent(buildFilename("个人周报", report.getReporterName(), report.getPeriodLabel()), toBytes(document)); - } catch (IOException e) { + } catch (Docx4JException | IOException e) { throw new IllegalStateException("生成周报导出文件失败", e); } } @@ -228,42 +237,37 @@ public class WorkReportContentExportService { return data; } - private void replacePlaceholders(XWPFDocument document, Map data) { - for (XWPFParagraph paragraph : document.getParagraphs()) { + private void replacePlaceholders(WordprocessingMLPackage document, Map data) { + replacePlaceholdersInObject(document.getMainDocumentPart(), data); + } + + private void replacePlaceholdersInObject(Object root, Map data) { + for (P paragraph : findAllParagraphs(root)) { replaceParagraphPlaceholders(paragraph, data); } - for (XWPFTable table : document.getTables()) { - for (XWPFTableRow row : table.getRows()) { - for (XWPFTableCell cell : row.getTableCells()) { - for (XWPFParagraph paragraph : cell.getParagraphs()) { - replaceParagraphPlaceholders(paragraph, data); - } - } - } - } } - private void replaceParagraphPlaceholders(XWPFParagraph paragraph, Map data) { - List runs = paragraph.getRuns(); - if (runs == null || runs.isEmpty()) { + private void replaceParagraphPlaceholders(P paragraph, Map data) { + List textNodes = findAllTextNodes(paragraph); + if (textNodes.isEmpty()) { return; } - String sourceText = paragraph.getText(); - if (!StringUtils.hasText(sourceText) || !sourceText.contains("{{")) { + String source = textNodes.stream() + .map(Text::getValue) + .filter(Objects::nonNull) + .collect(java.util.stream.Collectors.joining()); + if (!StringUtils.hasText(source) || !source.contains("{{")) { return; } - String replacedText = sourceText; + String replaced = source; for (Map.Entry entry : data.entrySet()) { - replacedText = replacedText.replace("{{" + entry.getKey() + "}}", entry.getValue()); - } - XWPFRun firstRun = runs.get(0); - applyRunText(firstRun, replacedText); - for (int i = runs.size() - 1; i >= 1; i--) { - paragraph.removeRun(i); + replaced = replaced.replace("{{" + entry.getKey() + "}}", entry.getValue()); } + rewriteParagraphText(paragraph, replaced, true); + applyParagraphFormatting(paragraph, replaced, false); } - private void renderPersonalReviewTable(XWPFDocument document, List items) { + private void renderPersonalReviewTable(WordprocessingMLPackage document, List items) { TemplateRowLocation location = findTemplateRowByPlaceholders(document, List.of( "{{itemNumber}}", "{{itemTitleWithHours}}", "{{contentText}}", "{{reflectionText}}")); if (location == null) { @@ -276,12 +280,12 @@ public class WorkReportContentExportService { "contentText", buildStructuredReviewText(item, false), "reflectionText", text(item.getReflectionText())))) .toList(); - List renderedRows = renderRowsByTemplateRow(location, rows, List.of( + List renderedRows = renderRowsByTemplateRow(location, rows, List.of( "itemNumber", "itemTitleWithHours", "contentText", "reflectionText")); rewriteReviewRows(renderedRows, items, false); } - private void renderPersonalPlanTable(XWPFDocument document, List items) { + private void renderPersonalPlanTable(WordprocessingMLPackage document, List items) { TemplateRowLocation location = findTemplateRowByPlaceholders(document, List.of( "{{itemNumber}}", "{{itemTitle}}", "{{targetText}}", "{{supportNeed}}")); if (location == null) { @@ -294,16 +298,20 @@ public class WorkReportContentExportService { "targetText", buildStructuredPlanText(item, false), "supportNeed", text(item.getSupportNeed())))) .toList(); - List renderedRows = renderRowsByTemplateRow(location, rows, List.of( + List renderedRows = renderRowsByTemplateRow(location, rows, List.of( "itemNumber", "itemTitle", "targetText", "supportNeed")); rewritePlanRows(renderedRows, items, false); } - private TemplateRowLocation findTemplateRowByPlaceholders(XWPFDocument document, List placeholders) { - for (XWPFTable table : document.getTables()) { - for (int i = 0; i < table.getRows().size(); i++) { - XWPFTableRow row = table.getRow(i); - String rowXml = row.getCtRow().xmlText(); + private TemplateRowLocation findTemplateRowByPlaceholders(WordprocessingMLPackage document, List placeholders) { + for (Tbl table : findAllTables(document.getMainDocumentPart())) { + List rows = table.getContent(); + for (int i = 0; i < rows.size(); i++) { + Object rowObject = XmlUtils.unwrap(rows.get(i)); + if (!(rowObject instanceof Tr row)) { + continue; + } + String rowXml = XmlUtils.marshaltoString(row, true, true, Context.jc, "", "", Tr.class); if (placeholders.stream().allMatch(rowXml::contains)) { return new TemplateRowLocation(table, i); } @@ -312,26 +320,26 @@ public class WorkReportContentExportService { return null; } - private List renderRowsByTemplateRow(TemplateRowLocation location, - List> dataRows, List placeholderNames) { - XWPFTable table = location.table(); + private List renderRowsByTemplateRow(TemplateRowLocation location, + List> dataRows, List placeholderNames) { + Tbl table = location.table(); int templateRowIndex = location.rowIndex(); - int dataStartIndex = templateRowIndex; int dataEndIndex = findPlaceholderBlockEndRowIndex(table, templateRowIndex, placeholderNames); - String templateRowXml = table.getRow(templateRowIndex).getCtRow().xmlText(); - for (int i = dataEndIndex - 1; i >= dataStartIndex; i--) { - table.removeRow(i); + Tr templateRow = tableRow(table, templateRowIndex); + for (int i = dataEndIndex - 1; i >= templateRowIndex; i--) { + table.getContent().remove(i); } List> rows = dataRows == null || dataRows.isEmpty() ? List.of(emptyPlaceholderValues(placeholderNames)) : dataRows; - int insertIndex = dataStartIndex; - List insertedRows = new ArrayList<>(rows.size()); + int insertIndex = templateRowIndex; + List insertedRows = new ArrayList<>(rows.size()); for (Map rowData : rows) { - XWPFTableRow row = new XWPFTableRow(parseRenderedRow(templateRowXml, rowData), table); - table.addRow(row, insertIndex++); - insertedRows.add(table.getRow(insertIndex - 1)); + Tr row = (Tr) XmlUtils.deepCopy(templateRow); + replacePlaceholdersInObject(row, rowData); + table.getContent().add(insertIndex++, row); + insertedRows.add(row); } return insertedRows; } @@ -352,53 +360,78 @@ public class WorkReportContentExportService { return result; } - private CTRow parseRenderedRow(String templateRowXml, Map values) { - String renderedXml = templateRowXml; - for (Map.Entry entry : values.entrySet()) { - renderedXml = renderedXml.replace(entry.getKey(), escapeXmlText(entry.getValue())); - } - try { - return CTRow.Factory.parse(renderedXml); - } catch (XmlException e) { - throw new IllegalStateException("渲染周报模板明细行失败", e); - } - } - - private String escapeXmlText(String value) { - return text(value) - .replace("&", "&") - .replace("<", "<") - .replace(">", ">"); - } - - private int findPlaceholderBlockEndRowIndex(XWPFTable table, int templateRowIndex, List placeholderNames) { + private int findPlaceholderBlockEndRowIndex(Tbl table, int templateRowIndex, List placeholderNames) { List placeholderTokens = placeholderNames.stream() .map(name -> "{{" + name + "}}") .toList(); - for (int i = templateRowIndex; i < table.getRows().size(); i++) { - String rowXml = table.getRow(i).getCtRow().xmlText(); + for (int i = templateRowIndex; i < table.getContent().size(); i++) { + Object rowObject = XmlUtils.unwrap(table.getContent().get(i)); + if (!(rowObject instanceof Tr row)) { + return i; + } + String rowXml = marshalTr(row); boolean containsPlaceholder = placeholderTokens.stream().anyMatch(rowXml::contains); if (!containsPlaceholder) { return i; } } - return table.getNumberOfRows(); + return table.getContent().size(); } - private void applyRunText(XWPFRun run, String value) { - String[] lines = text(value).split("\\R", -1); - run.setText("", 0); - for (int i = 0; i < lines.length; i++) { - if (i == 0) { - run.setText(lines[i], 0); - } else { - run.addBreak(); - run.setText(lines[i]); + private String marshalTr(Tr row) { + return XmlUtils.marshaltoString(row, true, true, Context.jc, "", "", Tr.class); + } + + private Tr tableRow(Tbl table, int rowIndex) { + return (Tr) XmlUtils.unwrap(table.getContent().get(rowIndex)); + } + + private List findAllTextNodes(Object root) { + List result = new ArrayList<>(); + new org.docx4j.TraversalUtil(root, new org.docx4j.TraversalUtil.CallbackImpl() { + @Override + public List apply(Object o) { + Object value = XmlUtils.unwrap(o); + if (value instanceof Text textNode) { + result.add(textNode); + } + return null; } - } + }); + return result; } - private record TemplateRowLocation(XWPFTable table, int rowIndex) { + private List findAllParagraphs(Object root) { + List result = new ArrayList<>(); + new org.docx4j.TraversalUtil(root, new org.docx4j.TraversalUtil.CallbackImpl() { + @Override + public List apply(Object o) { + Object value = XmlUtils.unwrap(o); + if (value instanceof P paragraph) { + result.add(paragraph); + } + return null; + } + }); + return result; + } + + private List findAllTables(Object root) { + List result = new ArrayList<>(); + new org.docx4j.TraversalUtil(root, new org.docx4j.TraversalUtil.CallbackImpl() { + @Override + public List apply(Object o) { + Object value = XmlUtils.unwrap(o); + if (value instanceof Tbl table) { + result.add(table); + } + return null; + } + }); + return result; + } + + private record TemplateRowLocation(Tbl table, int rowIndex) { } private DocumentContent buildMonthlyDocument(MonthlyReportRespVO report, @@ -413,22 +446,23 @@ public class WorkReportContentExportService { private DocumentContent buildMonthlyTemplateDocument(MonthlyReportRespVO report, MonthlyReportApprovalRecordRespVO approvalRecord, ClassPathResource templateResource) { - try (InputStream inputStream = templateResource.getInputStream(); - XWPFDocument document = new XWPFDocument(inputStream)) { + try (InputStream inputStream = templateResource.getInputStream()) { + WordprocessingMLPackage document = WordprocessingMLPackage.load(inputStream); replacePlaceholders(document, buildMonthlyTemplateData(report, approvalRecord)); renderMonthlyReviewTable(document, report.getReviewItems()); renderMonthlyFeedbackTables(document, approvalRecord); renderMonthlyPlanTable(document, report.getPlanItems()); return new DocumentContent(buildFilename("个人月报", report.getReporterName(), report.getPeriodLabel()), toBytes(document)); - } catch (IOException e) { + } catch (IOException | Docx4JException e) { throw new IllegalStateException("生成月报模板导出文件失败", e); } } private DocumentContent buildMonthlyFallbackDocument(MonthlyReportRespVO report, MonthlyReportApprovalRecordRespVO approvalRecord) { - try (XWPFDocument document = new XWPFDocument()) { + try { + WordprocessingMLPackage document = WordprocessingMLPackage.createPackage(); addTitle(document, "灿能电力绩效反馈面谈表"); addPersonalBaseTable(document, report.getReporterName(), report.getReporterDeptName(), report.getReporterPostName(), report.getSupervisorName()); @@ -455,7 +489,7 @@ public class WorkReportContentExportService { + " 日期:" + formatDate(approvalRecord == null ? null : approvalRecord.getSupervisorSignedDate()), false); return new DocumentContent(buildFilename("个人月报", report.getReporterName(), report.getPeriodLabel()), toBytes(document)); - } catch (IOException e) { + } catch (Docx4JException | IOException e) { throw new IllegalStateException("生成月报导出文件失败", e); } } @@ -470,18 +504,24 @@ public class WorkReportContentExportService { private DocumentContent buildProjectTemplateDocument(ProjectReportRespVO report, ClassPathResource templateResource) { - try (InputStream inputStream = templateResource.getInputStream(); - XWPFDocument document = new XWPFDocument(inputStream)) { - replacePlaceholders(document, buildProjectTemplateData(report)); + try (InputStream inputStream = templateResource.getInputStream()) { + WordprocessingMLPackage document = WordprocessingMLPackage.load(inputStream); + Map templateData = buildProjectTemplateData(report); + List currentItemLines = splitTextLinesPreserveBlank(templateData.remove("currentItems")); + List nextItemLines = splitTextLinesPreserveBlank(templateData.remove("nextItems")); + replacePlaceholders(document, templateData); + renderTemplateCellParagraphsByPlaceholder(document, "{{currentItems}}", currentItemLines); + renderTemplateCellParagraphsByPlaceholder(document, "{{nextItems}}", nextItemLines); return new DocumentContent(buildFilename("项目半月报", report.getProjectName(), report.getPeriodLabel()), toBytes(document)); - } catch (IOException e) { + } catch (IOException | Docx4JException e) { throw new IllegalStateException("生成项目半月报模板导出文件失败", e); } } private DocumentContent buildProjectFallbackDocument(ProjectReportRespVO report) { - try (XWPFDocument document = new XWPFDocument()) { + try { + WordprocessingMLPackage document = WordprocessingMLPackage.createPackage(); addTitle(document, "研发工作情况简报"); addParagraph(document, "研发工作情况简报(" + formatPeriod(report) + "):", true); addSectionTitle(document, "一、" + text(report.getProjectName())); @@ -504,11 +544,10 @@ public class WorkReportContentExportService { row("项目问题", report.getProjectProblems()))); return new DocumentContent(buildFilename("项目半月报", report.getProjectName(), report.getPeriodLabel()), toBytes(document)); - } catch (IOException e) { + } catch (Docx4JException | IOException e) { throw new IllegalStateException("生成项目半月报导出文件失败", e); } } - private Map buildMonthlyTemplateData(MonthlyReportRespVO report, MonthlyReportApprovalRecordRespVO approvalRecord) { Map data = new HashMap<>(); @@ -549,7 +588,30 @@ public class WorkReportContentExportService { return data; } - private void renderMonthlyReviewTable(XWPFDocument document, List items) { + private void renderTemplateCellParagraphsByPlaceholder(WordprocessingMLPackage document, + String placeholder, List lines) { + if (!StringUtils.hasText(placeholder)) { + return; + } + for (Tbl table : findAllTables(document.getMainDocumentPart())) { + for (Object rowObject : table.getContent()) { + Object rowValue = XmlUtils.unwrap(rowObject); + if (!(rowValue instanceof Tr row)) { + continue; + } + for (Tc cell : rowCells(row)) { + P templateParagraph = findParagraphByPlaceholder(cell, placeholder); + if (templateParagraph == null) { + continue; + } + setTemplateCellParagraphs(cell, templateParagraph, lines, false); + return; + } + } + } + } + + private void renderMonthlyReviewTable(WordprocessingMLPackage document, List items) { TemplateRowLocation location = findTemplateRowByPlaceholders(document, List.of( "{{itemNumber}}", "{{itemTitleWithHours}}", "{{contentText}}", "{{reflectionText}}")); if (location == null) { @@ -562,12 +624,12 @@ public class WorkReportContentExportService { "contentText", buildStructuredReviewText(item, false), "reflectionText", text(item.getReflectionText())))) .toList(); - List renderedRows = renderRowsByTemplateRow(location, rows, List.of( + List renderedRows = renderRowsByTemplateRow(location, rows, List.of( "itemNumber", "itemTitleWithHours", "contentText", "reflectionText")); rewriteReviewRows(renderedRows, items, false); } - private void renderMonthlyPlanTable(XWPFDocument document, List items) { + private void renderMonthlyPlanTable(WordprocessingMLPackage document, List items) { TemplateRowLocation location = findTemplateRowByPlaceholders(document, List.of( "{{itemNumber}}", "{{itemTitle}}", "{{targetText}}", "{{supportNeed}}")); if (location == null) { @@ -580,12 +642,12 @@ public class WorkReportContentExportService { "targetText", buildStructuredPlanText(item, false), "supportNeed", text(item.getSupportNeed())))) .toList(); - List renderedRows = renderRowsByTemplateRow(location, rows, List.of( + List renderedRows = renderRowsByTemplateRow(location, rows, List.of( "itemNumber", "itemTitle", "targetText", "supportNeed")); rewritePlanRows(renderedRows, items, false); } - private void renderMonthlyFeedbackTables(XWPFDocument document, MonthlyReportApprovalRecordRespVO approvalRecord) { + private void renderMonthlyFeedbackTables(WordprocessingMLPackage document, MonthlyReportApprovalRecordRespVO approvalRecord) { TemplateRowLocation strengthLocation = findTemplateRowByPlaceholders(document, List.of( "优势", "{{strengthDesc}}", "{{strengthExample}}", "{{improvementSuggestion}}")); if (strengthLocation != null) { @@ -606,30 +668,34 @@ public class WorkReportContentExportService { } private void renderSingleTemplateRow(TemplateRowLocation location, Map values) { - XWPFTableRow row = location.table().getRow(location.rowIndex()); - CTRow renderedRow = parseRenderedRow(row.getCtRow().xmlText(), values); - row.getCtRow().set(renderedRow); + Tr renderedRow = (Tr) XmlUtils.deepCopy(tableRow(location.table(), location.rowIndex())); + replacePlaceholdersInObject(renderedRow, values); + location.table().getContent().set(location.rowIndex(), renderedRow); } - private void rewriteReviewRows(List rows, List items, + private void rewriteReviewRows(List rows, List items, boolean includeDetail) { List safeItems = safeList(items); for (int i = 0; i < rows.size() && i < safeItems.size(); i++) { PersonalReportReviewItemRespVO item = safeItems.get(i); - XWPFTableRow row = rows.get(i); - setStructuredReviewCell(row.getCell(2), item, includeDetail); - setCellText(row.getCell(3), text(item.getReflectionText()), false); + List cells = rowCells(rows.get(i)); + setCenteredCellText(cells.get(0), formatNumber(item.getItemNumber()), false); + setCenteredCellText(cells.get(1), appendHours(item.getItemTitle(), item.getWorkHours()), false); + setStructuredReviewCell(cells.get(2), item, includeDetail); + setCellText(cells.get(3), text(item.getReflectionText()), false); } } - private void rewritePlanRows(List rows, List items, + private void rewritePlanRows(List rows, List items, boolean includeDetail) { List safeItems = safeList(items); for (int i = 0; i < rows.size() && i < safeItems.size(); i++) { PersonalReportPlanItemRespVO item = safeItems.get(i); - XWPFTableRow row = rows.get(i); - setStructuredPlanCell(row.getCell(2), item, includeDetail); - setCellText(row.getCell(3), text(item.getSupportNeed()), false); + List cells = rowCells(rows.get(i)); + setCenteredCellText(cells.get(0), formatNumber(item.getItemNumber()), false); + setCenteredCellText(cells.get(1), text(item.getItemTitle()), false); + setStructuredPlanCell(cells.get(2), item, includeDetail); + setCellText(cells.get(3), text(item.getSupportNeed()), false); } } @@ -643,7 +709,7 @@ public class WorkReportContentExportService { item == null ? null : item.getTargetText(), false, includeDetail); } - private void setStructuredReviewCell(XWPFTableCell cell, PersonalReportReviewItemRespVO item, + private void setStructuredReviewCell(Tc cell, PersonalReportReviewItemRespVO item, boolean includeDetail) { setStructuredCellTextForTemplate(cell, item == null ? null : item.getContentJson(), @@ -652,7 +718,7 @@ public class WorkReportContentExportService { includeDetail); } - private void setStructuredPlanCell(XWPFTableCell cell, PersonalReportPlanItemRespVO item, + private void setStructuredPlanCell(Tc cell, PersonalReportPlanItemRespVO item, boolean includeDetail) { setStructuredCellTextForTemplate(cell, item == null ? null : item.getTargetJson(), @@ -661,18 +727,17 @@ public class WorkReportContentExportService { includeDetail); } - private void setStructuredCellTextForTemplate(XWPFTableCell cell, Object jsonValue, + private void setStructuredCellTextForTemplate(Tc cell, Object jsonValue, String fallbackText, boolean includeHours, boolean includeDetail) { List lines = buildStructuredParagraphsForTemplate(jsonValue, fallbackText, includeHours, includeDetail); unsetNoWrap(cell); if (lines.isEmpty()) { - setCellTextByTemplateParagraph(cell, normalizeLineBreaks(fallbackText), false); + setStructuredTemplateCellParagraphs(cell, splitTextLinesPreserveBlank(normalizeLineBreaks(fallbackText)), false); return; } - setCellTextByTemplateParagraph(cell, String.join("\n", lines), false); + setStructuredTemplateCellParagraphs(cell, lines, false); } - private List buildStructuredParagraphsForTemplate(Object jsonValue, String fallbackText, boolean includeHours, boolean includeDetail) { List sections = parseStructuredSections(jsonValue); @@ -687,19 +752,19 @@ public class WorkReportContentExportService { for (StructuredSectionData section : sections) { String category = normalizeSectionCategory(section.category()); if (StringUtils.hasText(category)) { - lines.add("# " + category + "\uFF1A"); + lines.add("#" + category + ":"); } List tasks = safeList(section.tasks()); for (int i = 0; i < tasks.size(); i++) { StructuredTaskData task = tasks.get(i); - String taskLine = (i + 1) + "\u3001 " + formatStructuredTaskForTemplate(task, includeHours, priorityLabelMap); + String taskLine = (i + 1) + "、" + formatStructuredTaskForTemplate(task, includeHours, priorityLabelMap); String detail = includeDetail ? text(task.detail()) : ""; if (StringUtils.hasText(detail)) { List detailLines = splitTextLines(detail); if (detailLines.isEmpty()) { - lines.add(taskLine + "\uFF1A"); + lines.add(taskLine + ":"); } else { - lines.add(taskLine + "\uFF1A" + detailLines.get(0)); + lines.add(taskLine + ":" + detailLines.get(0)); for (int j = 1; j < detailLines.size(); j++) { lines.add(detailLines.get(j)); } @@ -722,12 +787,12 @@ public class WorkReportContentExportService { metrics.add(resolvePriorityLabel(task.priority(), priorityLabelMap)); } if (task.progress() != null) { - metrics.add("\u8FDB\u5EA6 " + formatDecimal(task.progress()) + "%"); + metrics.add("进度 " + formatDecimal(task.progress()) + "%"); } if (includeHours && task.hours() != null) { metrics.add(formatDecimal(task.hours()) + "h"); } - return text(task.title()) + (metrics.isEmpty() ? "" : "\uFF08" + String.join(" / ", metrics) + "\uFF09"); + return text(task.title()) + (metrics.isEmpty() ? "" : "(" + String.join(" / ", metrics) + ")"); } private List parseStructuredSectionsForTemplate(String textValue) { @@ -846,7 +911,7 @@ public class WorkReportContentExportService { Map priorityLabelMap = loadPriorityLabelMap(); List lines = new ArrayList<>(); for (StructuredSectionData section : sections) { - lines.add("# " + normalizeSectionCategory(section.category()) + ":"); + lines.add("#" + normalizeSectionCategory(section.category()) + ":"); List tasks = safeList(section.tasks()); for (int i = 0; i < tasks.size(); i++) { StructuredTaskData task = tasks.get(i); @@ -870,7 +935,7 @@ public class WorkReportContentExportService { return String.join("\n", lines); } - private void setStructuredCellText(XWPFTableCell cell, Object jsonValue, String fallbackText, boolean includeHours) { + private void setStructuredCellText(Tc cell, Object jsonValue, String fallbackText, boolean includeHours) { List lines = buildStructuredLines(jsonValue, fallbackText, includeHours); if (lines.isEmpty()) { setCellText(cell, fallbackText, false); @@ -890,7 +955,7 @@ public class WorkReportContentExportService { Map priorityLabelMap = loadPriorityLabelMap(); List lines = new ArrayList<>(); for (StructuredSectionData section : sections) { - lines.add("# " + normalizeSectionCategory(section.category()) + ":"); + lines.add("#" + normalizeSectionCategory(section.category()) + ":"); List tasks = safeList(section.tasks()); for (int i = 0; i < tasks.size(); i++) { lines.add((i + 1) + "、" + formatStructuredTask(tasks.get(i), includeHours, priorityLabelMap)); @@ -1082,7 +1147,7 @@ public class WorkReportContentExportService { metrics.add(resolvePriorityLabel(task.priority(), priorityLabelMap)); } if (task.progress() != null) { - metrics.add("\u8FDB\u5EA6 " + formatDecimal(task.progress()) + "%"); + metrics.add("进度 " + formatDecimal(task.progress()) + "%"); } if (includeHours && task.hours() != null) { metrics.add(formatDecimal(task.hours()) + "h"); @@ -1291,31 +1356,22 @@ public class WorkReportContentExportService { return String.join("\n", lines); } - private void addTitle(XWPFDocument document, String title) { - XWPFParagraph paragraph = document.createParagraph(); - paragraph.setAlignment(ParagraphAlignment.CENTER); - XWPFRun run = paragraph.createRun(); - run.setBold(true); - run.setFontSize(18); - run.setText(title); + private void addTitle(WordprocessingMLPackage document, String title) { + P paragraph = createParagraph(title, true, TITLE_FONT_SIZE, JcEnumeration.CENTER); + document.getMainDocumentPart().addObject(paragraph); } - private void addSectionTitle(XWPFDocument document, String title) { - XWPFParagraph paragraph = document.createParagraph(); - XWPFRun run = paragraph.createRun(); - run.setBold(true); - run.setFontSize(12); - run.setText(title); + private void addSectionTitle(WordprocessingMLPackage document, String title) { + P paragraph = createParagraph(title, true, SECTION_TITLE_FONT_SIZE, null); + document.getMainDocumentPart().addObject(paragraph); } - private void addParagraph(XWPFDocument document, String text, boolean bold) { - XWPFParagraph paragraph = document.createParagraph(); - XWPFRun run = paragraph.createRun(); - run.setBold(bold); - run.setText(text(text)); + private void addParagraph(WordprocessingMLPackage document, String text, boolean bold) { + P paragraph = createParagraph(text, bold, DEFAULT_FONT_SIZE, null); + document.getMainDocumentPart().addObject(paragraph); } - private void addPersonalBaseTable(XWPFDocument document, String name, String deptName, + private void addPersonalBaseTable(WordprocessingMLPackage document, String name, String deptName, String postName, String managerName) { addKeyValueTable(document, List.of( row("姓名", name), @@ -1324,156 +1380,386 @@ public class WorkReportContentExportService { row("直接上级", managerName))); } - private void addPersonalReviewTable(XWPFDocument document, List items, + private void addPersonalReviewTable(WordprocessingMLPackage document, List items, boolean includeDetail) { - XWPFTable table = document.createTable(1, 4); + Tbl table = TblFactory.createTable(1, 4, 2000); setTableWidth(table); - setRowText(table.getRow(0), List.of("序号", "工作事项", "具体工作内容及成果描述", "工作感悟"), true); + setRowText(tableRow(table, 0), List.of("序号", "工作事项", "具体工作内容及成果描述", "工作感悟"), true); for (PersonalReportReviewItemRespVO item : safeList(items)) { - XWPFTableRow row = table.createRow(); - setCellText(row.getCell(0), formatNumber(item.getItemNumber()), false); - setCellText(row.getCell(1), appendHours(item.getItemTitle(), item.getWorkHours()), false); - setCellText(row.getCell(2), buildStructuredReviewText(item, includeDetail), false); - setCellText(row.getCell(3), item.getReflectionText(), false); + Tr row = addTableRow(table, 4); + List cells = rowCells(row); + setCellText(cells.get(0), formatNumber(item.getItemNumber()), false); + setCellText(cells.get(1), appendHours(item.getItemTitle(), item.getWorkHours()), false); + setCellText(cells.get(2), buildStructuredReviewText(item, includeDetail), false); + setCellText(cells.get(3), item.getReflectionText(), false); } + document.getMainDocumentPart().addObject(table); } - private void addPersonalPlanTable(XWPFDocument document, List items, + private void addPersonalPlanTable(WordprocessingMLPackage document, List items, boolean includeDetail) { - XWPFTable table = document.createTable(1, 4); + Tbl table = TblFactory.createTable(1, 4, 2000); setTableWidth(table); - setRowText(table.getRow(0), List.of("序号", "工作事项", "具体目标", "对他人协助的需求"), true); + setRowText(tableRow(table, 0), List.of("序号", "工作事项", "具体目标", "对他人协助的需求"), true); for (PersonalReportPlanItemRespVO item : safeList(items)) { - XWPFTableRow row = table.createRow(); - setCellText(row.getCell(0), formatNumber(item.getItemNumber()), false); - setCellText(row.getCell(1), item.getItemTitle(), false); - setCellText(row.getCell(2), buildStructuredPlanText(item, includeDetail), false); - setCellText(row.getCell(3), item.getSupportNeed(), false); + Tr row = addTableRow(table, 4); + List cells = rowCells(row); + setCellText(cells.get(0), formatNumber(item.getItemNumber()), false); + setCellText(cells.get(1), item.getItemTitle(), false); + setCellText(cells.get(2), buildStructuredPlanText(item, includeDetail), false); + setCellText(cells.get(3), item.getSupportNeed(), false); } + document.getMainDocumentPart().addObject(table); } - private void addFeedbackTable(XWPFDocument document, MonthlyReportApprovalRecordRespVO record) { - XWPFTable table = document.createTable(1, 4); + private void addFeedbackTable(WordprocessingMLPackage document, MonthlyReportApprovalRecordRespVO record) { + Tbl table = TblFactory.createTable(1, 4, 2000); setTableWidth(table); - setRowText(table.getRow(0), List.of("优势/劣势项", "优势/劣势描述", "行为事例", "改进提升建议"), true); - XWPFTableRow strengthRow = table.createRow(); + setRowText(tableRow(table, 0), List.of("优势/劣势项", "优势/劣势描述", "行为事例", "改进提升建议"), true); + Tr strengthRow = addTableRow(table, 4); setRowText(strengthRow, values("优势", record == null ? null : record.getStrengthDesc(), record == null ? null : record.getStrengthExample(), ""), false); - XWPFTableRow weaknessRow = table.createRow(); + Tr weaknessRow = addTableRow(table, 4); setRowText(weaknessRow, values("劣势", record == null ? null : record.getWeaknessDesc(), record == null ? null : record.getWeaknessExample(), record == null ? null : record.getImprovementSuggestion()), false); + document.getMainDocumentPart().addObject(table); } - private void addProjectItemTable(XWPFDocument document, List items, boolean showHours) { - XWPFTable table = document.createTable(1, showHours ? 4 : 3); + private void addProjectItemTable(WordprocessingMLPackage document, List items, boolean showHours) { + Tbl table = TblFactory.createTable(1, showHours ? 4 : 3, 2000); setTableWidth(table); - setRowText(table.getRow(0), showHours + setRowText(tableRow(table, 0), showHours ? List.of("工作内容", "工时", "优先级", "进度") : List.of("工作内容", "优先级", "进度"), true); for (ProjectReportItemRespVO item : safeList(items)) { - XWPFTableRow row = table.createRow(); + Tr row = addTableRow(table, showHours ? 4 : 3); setRowText(row, showHours ? values(item.getItemTitle(), formatDecimal(item.getWorkHours()), item.getPriorityCode(), formatProgress(item.getProgressRate())) : values(item.getItemTitle(), item.getPriorityCode(), formatProgress(item.getProgressRate())), false); } + document.getMainDocumentPart().addObject(table); } - private void addKeyValueTable(XWPFDocument document, List> rows) { - XWPFTable table = document.createTable(rows.size(), 2); + private void addKeyValueTable(WordprocessingMLPackage document, List> rows) { + Tbl table = TblFactory.createTable(rows.size(), 2, 4000); setTableWidth(table); for (int i = 0; i < rows.size(); i++) { - setRowText(table.getRow(i), rows.get(i), i == 0 && rows.size() == 1); + setRowText(tableRow(table, i), rows.get(i), i == 0 && rows.size() == 1); + } + document.getMainDocumentPart().addObject(table); + } + + private void setTableWidth(Tbl table) { + TblPr tblPr = table.getTblPr(); + if (tblPr == null) { + tblPr = WML_FACTORY.createTblPr(); + table.setTblPr(tblPr); + } + TblWidth width = tblPr.getTblW(); + if (width == null) { + width = WML_FACTORY.createTblWidth(); + tblPr.setTblW(width); + } + width.setType("pct"); + width.setW(BigInteger.valueOf(5000)); + } + + private void setRowText(Tr row, List values, boolean bold) { + List cells = rowCells(row); + for (int i = 0; i < values.size() && i < cells.size(); i++) { + setCellText(cells.get(i), values.get(i), bold); } } - private void setTableWidth(XWPFTable table) { - table.setWidth("100%"); + private void setCellText(Tc cell, String value, boolean bold) { + clearCellContent(cell); + cell.getContent().add(createParagraphWithBreaks(value, bold, true)); } - private void setRowText(XWPFTableRow row, List values, boolean bold) { - for (int i = 0; i < values.size(); i++) { - setCellText(row.getCell(i), values.get(i), bold); - } + private void setCenteredCellText(Tc cell, String value, boolean bold) { + setCellText(cell, value, bold); + applyCellAlignment(cell, JcEnumeration.CENTER, STVerticalJc.CENTER, false); } - private void setCellText(XWPFTableCell cell, String value, boolean bold) { - while (cell.getParagraphs().size() > 0) { - cell.removeParagraph(0); - } - XWPFParagraph paragraph = cell.addParagraph(); - XWPFRun run = paragraph.createRun(); - run.setBold(bold); - String[] lines = text(value).split("\\R", -1); - for (int i = 0; i < lines.length; i++) { - if (i > 0) { - run.addBreak(); - } - run.setText(lines[i]); - } - } - - private void setCellTextByTemplateParagraph(XWPFTableCell cell, String value, boolean bold) { + private void setStructuredTemplateCellParagraphs(Tc cell, List lines, boolean bold) { unsetNoWrap(cell); - XWPFParagraph paragraph; - if (cell.getParagraphs() == null || cell.getParagraphs().isEmpty()) { - paragraph = cell.addParagraph(); - } else { - paragraph = cell.getParagraphs().get(0); - for (int i = cell.getParagraphs().size() - 1; i >= 1; i--) { - cell.removeParagraph(i); - } + clearCellContent(cell); + List safeLines = (lines == null || lines.isEmpty()) ? List.of("") : lines; + for (String line : safeLines) { + P paragraph = createParagraph(line, bold, DEFAULT_FONT_SIZE, null); + applyParagraphFormatting(paragraph, line, false); + cell.getContent().add(paragraph); } - List runs = paragraph.getRuns(); - XWPFRun firstRun; - if (runs == null || runs.isEmpty()) { - firstRun = paragraph.createRun(); - } else { - firstRun = runs.get(0); - for (int i = runs.size() - 1; i >= 1; i--) { - paragraph.removeRun(i); - } - } - firstRun.setBold(bold); - applyRunText(firstRun, value); } - private void unsetNoWrap(XWPFTableCell cell) { - if (cell == null || cell.getCTTc() == null) { + private void applyCellAlignment(Tc cell, JcEnumeration horizontal, STVerticalJc vertical, boolean firstLineIndent) { + if (cell == null) { return; } - var tcPr = cell.getCTTc().getTcPr(); - if (tcPr != null && tcPr.isSetNoWrap()) { - tcPr.unsetNoWrap(); + TcPr tcPr = cell.getTcPr(); + if (tcPr == null) { + tcPr = WML_FACTORY.createTcPr(); + cell.setTcPr(tcPr); + } + if (vertical != null) { + CTVerticalJc verticalJc = WML_FACTORY.createCTVerticalJc(); + verticalJc.setVal(vertical); + tcPr.setVAlign(verticalJc); + } + for (P paragraph : findAllParagraphs(cell)) { + ensureParagraphFormatting(paragraph, horizontal, firstLineIndent); } } - private void setCellParagraphs(XWPFTableCell cell, List lines, boolean bold) { - while (cell.getParagraphs().size() > 0) { - cell.removeParagraph(0); + private void applyParagraphFormatting(P paragraph, String value, boolean firstLineIndent) { + ensureParagraphFormatting(paragraph, null, firstLineIndent); + PPr pPr = paragraph.getPPr(); + PPrBase.Spacing spacing = pPr.getSpacing(); + if (spacing == null) { + spacing = WML_FACTORY.createPPrBaseSpacing(); + pPr.setSpacing(spacing); } + spacing.setBefore(BigInteger.ZERO); + spacing.setAfter(BigInteger.ZERO); + if (!StringUtils.hasText(value)) { + spacing.setLineRule(STLineSpacingRule.AUTO); + } + } + + private void ensureParagraphFormatting(P paragraph, JcEnumeration alignment, boolean firstLineIndent) { + PPr pPr = paragraph.getPPr(); + if (pPr == null) { + pPr = WML_FACTORY.createPPr(); + paragraph.setPPr(pPr); + } + pPr.setNumPr(null); + if (alignment != null) { + Jc jc = pPr.getJc(); + if (jc == null) { + jc = WML_FACTORY.createJc(); + pPr.setJc(jc); + } + jc.setVal(alignment); + } + PPrBase.Ind ind = pPr.getInd(); + if (ind == null) { + ind = WML_FACTORY.createPPrBaseInd(); + pPr.setInd(ind); + } + ind.setLeft(BigInteger.ZERO); + ind.setLeftChars(BigInteger.ZERO); + ind.setRight(BigInteger.ZERO); + ind.setRightChars(BigInteger.ZERO); + ind.setStart(BigInteger.ZERO); + ind.setStartChars(BigInteger.ZERO); + ind.setEnd(BigInteger.ZERO); + ind.setEndChars(BigInteger.ZERO); + ind.setHanging(BigInteger.ZERO); + ind.setHangingChars(BigInteger.ZERO); + ind.setFirstLine(firstLineIndent ? BigInteger.valueOf(420) : BigInteger.ZERO); + ind.setFirstLineChars(BigInteger.ZERO); + } + + private void unsetNoWrap(Tc cell) { + if (cell == null) { + return; + } + TcPr tcPr = cell.getTcPr(); + if (tcPr != null) { + tcPr.setNoWrap(null); + } + } + + private void setCellParagraphs(Tc cell, List lines, boolean bold) { + clearCellContent(cell); List safeLines = lines == null || lines.isEmpty() ? List.of("") : lines; for (String line : safeLines) { - XWPFParagraph paragraph = cell.addParagraph(); - paragraph.setSpacingBefore(0); - paragraph.setSpacingAfter(0); - XWPFRun run = paragraph.createRun(); - run.setBold(bold); - run.setText(text(line)); + P paragraph = createParagraph(line, bold, DEFAULT_FONT_SIZE, null); + applyParagraphFormatting(paragraph, line, false); + cell.getContent().add(paragraph); } } - private byte[] toBytes(XWPFDocument document) throws IOException { + private byte[] toBytes(WordprocessingMLPackage document) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - document.write(outputStream); + try { + document.save(outputStream); + } catch (Docx4JException e) { + throw new IOException("save docx failed", e); + } return outputStream.toByteArray(); } + private P createParagraph(String value, boolean bold, BigInteger fontSize, JcEnumeration alignment) { + P paragraph = WML_FACTORY.createP(); + ensureParagraphFormatting(paragraph, alignment, false); + R run = createRun(value, bold, fontSize); + paragraph.getContent().add(run); + return paragraph; + } + + private P createParagraphWithBreaks(String value, boolean bold, boolean preserveEmptyLines) { + P paragraph = WML_FACTORY.createP(); + R run = WML_FACTORY.createR(); + run.setRPr(createRunProperties(bold, DEFAULT_FONT_SIZE)); + String[] lines = splitRawLines(value); + for (int i = 0; i < lines.length; i++) { + if (i > 0) { + run.getContent().add(WML_FACTORY.createBr()); + } + if (preserveEmptyLines || StringUtils.hasText(lines[i])) { + Text textNode = WML_FACTORY.createText(); + textNode.setValue(lines[i]); + if (lines[i].startsWith(" ") || lines[i].endsWith(" ")) { + textNode.setSpace("preserve"); + } + run.getContent().add(textNode); + } + } + paragraph.getContent().add(run); + applyParagraphFormatting(paragraph, value, false); + return paragraph; + } + + private void rewriteParagraphText(P paragraph, String value, boolean preserveEmptyLines) { + R firstRun = findFirstRun(paragraph); + RPr originalRunProperties = firstRun == null ? null : firstRun.getRPr(); + paragraph.getContent().clear(); + R run = WML_FACTORY.createR(); + if (originalRunProperties != null) { + run.setRPr((RPr) XmlUtils.deepCopy(originalRunProperties)); + } + String[] lines = splitRawLines(value); + for (int i = 0; i < lines.length; i++) { + if (i > 0) { + run.getContent().add(WML_FACTORY.createBr()); + } + if (preserveEmptyLines || StringUtils.hasText(lines[i])) { + Text textNode = WML_FACTORY.createText(); + textNode.setValue(lines[i]); + if (lines[i].startsWith(" ") || lines[i].endsWith(" ")) { + textNode.setSpace("preserve"); + } + run.getContent().add(textNode); + } + } + paragraph.getContent().add(run); + } + + private P findParagraphByPlaceholder(Tc cell, String placeholder) { + if (cell == null || !StringUtils.hasText(placeholder)) { + return null; + } + for (P paragraph : findAllParagraphs(cell)) { + String paragraphText = findAllTextNodes(paragraph).stream() + .map(Text::getValue) + .filter(Objects::nonNull) + .collect(java.util.stream.Collectors.joining()); + if (paragraphText.contains(placeholder)) { + return paragraph; + } + } + return null; + } + + private void setTemplateCellParagraphs(Tc cell, P templateParagraph, List lines, boolean bold) { + unsetNoWrap(cell); + clearCellContent(cell); + List safeLines = (lines == null || lines.isEmpty()) ? List.of("") : lines; + for (String line : safeLines) { + P paragraph = templateParagraph == null + ? createParagraph(line, bold, DEFAULT_FONT_SIZE, null) + : (P) XmlUtils.deepCopy(templateParagraph); + rewriteParagraphText(paragraph, line, true); + applyParagraphFormatting(paragraph, line, false); + cell.getContent().add(paragraph); + } + } + + private R findFirstRun(P paragraph) { + if (paragraph == null) { + return null; + } + for (Object content : paragraph.getContent()) { + Object value = XmlUtils.unwrap(content); + if (value instanceof R run) { + return run; + } + } + return null; + } + + private String[] splitRawLines(String value) { + String source = value == null ? "" : value; + source = source.replace("\r\n", "\n").replace('\r', '\n'); + return source.split("\\R", -1); + } + + private List splitTextLinesPreserveBlank(String value) { + String normalized = normalizeLineBreaks(text(value)); + if (!StringUtils.hasText(normalized)) { + return List.of(); + } + return Arrays.asList(normalized.split("\\R", -1)); + } + + private R createRun(String value, boolean bold, BigInteger fontSize) { + R run = WML_FACTORY.createR(); + run.setRPr(createRunProperties(bold, fontSize)); + Text textNode = WML_FACTORY.createText(); + textNode.setValue(text(value)); + run.getContent().add(textNode); + return run; + } + + private RPr createRunProperties(boolean bold, BigInteger fontSize) { + RPr rPr = WML_FACTORY.createRPr(); + if (bold) { + BooleanDefaultTrue b = WML_FACTORY.createBooleanDefaultTrue(); + b.setVal(Boolean.TRUE); + rPr.setB(b); + rPr.setBCs(b); + } + HpsMeasure size = WML_FACTORY.createHpsMeasure(); + size.setVal(fontSize); + rPr.setSz(size); + HpsMeasure sizeCs = WML_FACTORY.createHpsMeasure(); + sizeCs.setVal(fontSize); + rPr.setSzCs(sizeCs); + return rPr; + } + + private Tr addTableRow(Tbl table, int cellCount) { + Tr row = WML_FACTORY.createTr(); + for (int i = 0; i < cellCount; i++) { + Tc cell = WML_FACTORY.createTc(); + cell.getContent().add(WML_FACTORY.createP()); + row.getContent().add(cell); + } + table.getContent().add(row); + return row; + } + + private List rowCells(Tr row) { + List cells = new ArrayList<>(); + for (Object content : row.getContent()) { + Object value = XmlUtils.unwrap(content); + if (value instanceof Tc cell) { + cells.add(cell); + } + } + return cells; + } + + private void clearCellContent(Tc cell) { + cell.getContent().clear(); + } private String buildTravelText(List travelSegments) { if (travelSegments == null || travelSegments.isEmpty()) { return ""; diff --git a/rdms-project/rdms-project-boot/src/main/resources/application-local.yaml b/rdms-project/rdms-project-boot/src/main/resources/application-local.yaml index 4135ce4..f47cbf8 100644 --- a/rdms-project/rdms-project-boot/src/main/resources/application-local.yaml +++ b/rdms-project/rdms-project-boot/src/main/resources/application-local.yaml @@ -6,12 +6,12 @@ spring: username: # Nacos 账号 password: # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: 1924bcfb-4eab-4c58-9003-4a37d5fc2949 # 命名空间。这里使用 dev 开发环境 + namespace: 0cd9c1b2-56ba-4e1d-a23b-f951392c46bf # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # 服务实例的版本号,可用于灰度发布 config: # 【注册中心】配置项 - namespace: 1924bcfb-4eab-4c58-9003-4a37d5fc2949 # 命名空间。这里使用 dev 开发环境 + namespace: 0cd9c1b2-56ba-4e1d-a23b-f951392c46bf # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP #################### 数据库相关配置 #################### @@ -55,7 +55,7 @@ spring: primary: master datasource: master: - url: jdbc:mysql://192.168.1.22:13306/rdms_view?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://192.168.1.22:13306/rdms_v3?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 username: root password: njcnpqs @@ -63,7 +63,7 @@ spring: data: redis: host: 127.0.0.1 # 地址 - port: 16379 # 端口 + port: 6379 # 端口 database: 1 # 数据库索引 # password: njcnpqs # 密码,建议生产环境开启 diff --git a/rdms-project/rdms-project-boot/src/main/resources/templates/work-report/weekly-report-template.docx b/rdms-project/rdms-project-boot/src/main/resources/templates/work-report/weekly-report-template.docx index 97bb06c38e114f8d9ca63640ae2f115f564a60d4..0968abd68db5338410fa27e827e86b37b165d590 100644 GIT binary patch delta 10822 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 QKNph(Ul0c7SpWb40J4(IhX4Qo delta 10699 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
findAllParagraphs(Object root) { + List
result = new ArrayList<>(); + new org.docx4j.TraversalUtil(root, new org.docx4j.TraversalUtil.CallbackImpl() { + @Override + public List apply(Object o) { + Object value = XmlUtils.unwrap(o); + if (value instanceof P paragraph) { + result.add(paragraph); + } + return null; + } + }); + return result; + } + + private List findAllTables(Object root) { + List result = new ArrayList<>(); + new org.docx4j.TraversalUtil(root, new org.docx4j.TraversalUtil.CallbackImpl() { + @Override + public List apply(Object o) { + Object value = XmlUtils.unwrap(o); + if (value instanceof Tbl table) { + result.add(table); + } + return null; + } + }); + return result; + } + + private record TemplateRowLocation(Tbl table, int rowIndex) { } private DocumentContent buildMonthlyDocument(MonthlyReportRespVO report, @@ -413,22 +446,23 @@ public class WorkReportContentExportService { private DocumentContent buildMonthlyTemplateDocument(MonthlyReportRespVO report, MonthlyReportApprovalRecordRespVO approvalRecord, ClassPathResource templateResource) { - try (InputStream inputStream = templateResource.getInputStream(); - XWPFDocument document = new XWPFDocument(inputStream)) { + try (InputStream inputStream = templateResource.getInputStream()) { + WordprocessingMLPackage document = WordprocessingMLPackage.load(inputStream); replacePlaceholders(document, buildMonthlyTemplateData(report, approvalRecord)); renderMonthlyReviewTable(document, report.getReviewItems()); renderMonthlyFeedbackTables(document, approvalRecord); renderMonthlyPlanTable(document, report.getPlanItems()); return new DocumentContent(buildFilename("个人月报", report.getReporterName(), report.getPeriodLabel()), toBytes(document)); - } catch (IOException e) { + } catch (IOException | Docx4JException e) { throw new IllegalStateException("生成月报模板导出文件失败", e); } } private DocumentContent buildMonthlyFallbackDocument(MonthlyReportRespVO report, MonthlyReportApprovalRecordRespVO approvalRecord) { - try (XWPFDocument document = new XWPFDocument()) { + try { + WordprocessingMLPackage document = WordprocessingMLPackage.createPackage(); addTitle(document, "灿能电力绩效反馈面谈表"); addPersonalBaseTable(document, report.getReporterName(), report.getReporterDeptName(), report.getReporterPostName(), report.getSupervisorName()); @@ -455,7 +489,7 @@ public class WorkReportContentExportService { + " 日期:" + formatDate(approvalRecord == null ? null : approvalRecord.getSupervisorSignedDate()), false); return new DocumentContent(buildFilename("个人月报", report.getReporterName(), report.getPeriodLabel()), toBytes(document)); - } catch (IOException e) { + } catch (Docx4JException | IOException e) { throw new IllegalStateException("生成月报导出文件失败", e); } } @@ -470,18 +504,24 @@ public class WorkReportContentExportService { private DocumentContent buildProjectTemplateDocument(ProjectReportRespVO report, ClassPathResource templateResource) { - try (InputStream inputStream = templateResource.getInputStream(); - XWPFDocument document = new XWPFDocument(inputStream)) { - replacePlaceholders(document, buildProjectTemplateData(report)); + try (InputStream inputStream = templateResource.getInputStream()) { + WordprocessingMLPackage document = WordprocessingMLPackage.load(inputStream); + Map templateData = buildProjectTemplateData(report); + List currentItemLines = splitTextLinesPreserveBlank(templateData.remove("currentItems")); + List nextItemLines = splitTextLinesPreserveBlank(templateData.remove("nextItems")); + replacePlaceholders(document, templateData); + renderTemplateCellParagraphsByPlaceholder(document, "{{currentItems}}", currentItemLines); + renderTemplateCellParagraphsByPlaceholder(document, "{{nextItems}}", nextItemLines); return new DocumentContent(buildFilename("项目半月报", report.getProjectName(), report.getPeriodLabel()), toBytes(document)); - } catch (IOException e) { + } catch (IOException | Docx4JException e) { throw new IllegalStateException("生成项目半月报模板导出文件失败", e); } } private DocumentContent buildProjectFallbackDocument(ProjectReportRespVO report) { - try (XWPFDocument document = new XWPFDocument()) { + try { + WordprocessingMLPackage document = WordprocessingMLPackage.createPackage(); addTitle(document, "研发工作情况简报"); addParagraph(document, "研发工作情况简报(" + formatPeriod(report) + "):", true); addSectionTitle(document, "一、" + text(report.getProjectName())); @@ -504,11 +544,10 @@ public class WorkReportContentExportService { row("项目问题", report.getProjectProblems()))); return new DocumentContent(buildFilename("项目半月报", report.getProjectName(), report.getPeriodLabel()), toBytes(document)); - } catch (IOException e) { + } catch (Docx4JException | IOException e) { throw new IllegalStateException("生成项目半月报导出文件失败", e); } } - private Map buildMonthlyTemplateData(MonthlyReportRespVO report, MonthlyReportApprovalRecordRespVO approvalRecord) { Map data = new HashMap<>(); @@ -549,7 +588,30 @@ public class WorkReportContentExportService { return data; } - private void renderMonthlyReviewTable(XWPFDocument document, List items) { + private void renderTemplateCellParagraphsByPlaceholder(WordprocessingMLPackage document, + String placeholder, List lines) { + if (!StringUtils.hasText(placeholder)) { + return; + } + for (Tbl table : findAllTables(document.getMainDocumentPart())) { + for (Object rowObject : table.getContent()) { + Object rowValue = XmlUtils.unwrap(rowObject); + if (!(rowValue instanceof Tr row)) { + continue; + } + for (Tc cell : rowCells(row)) { + P templateParagraph = findParagraphByPlaceholder(cell, placeholder); + if (templateParagraph == null) { + continue; + } + setTemplateCellParagraphs(cell, templateParagraph, lines, false); + return; + } + } + } + } + + private void renderMonthlyReviewTable(WordprocessingMLPackage document, List items) { TemplateRowLocation location = findTemplateRowByPlaceholders(document, List.of( "{{itemNumber}}", "{{itemTitleWithHours}}", "{{contentText}}", "{{reflectionText}}")); if (location == null) { @@ -562,12 +624,12 @@ public class WorkReportContentExportService { "contentText", buildStructuredReviewText(item, false), "reflectionText", text(item.getReflectionText())))) .toList(); - List renderedRows = renderRowsByTemplateRow(location, rows, List.of( + List renderedRows = renderRowsByTemplateRow(location, rows, List.of( "itemNumber", "itemTitleWithHours", "contentText", "reflectionText")); rewriteReviewRows(renderedRows, items, false); } - private void renderMonthlyPlanTable(XWPFDocument document, List items) { + private void renderMonthlyPlanTable(WordprocessingMLPackage document, List items) { TemplateRowLocation location = findTemplateRowByPlaceholders(document, List.of( "{{itemNumber}}", "{{itemTitle}}", "{{targetText}}", "{{supportNeed}}")); if (location == null) { @@ -580,12 +642,12 @@ public class WorkReportContentExportService { "targetText", buildStructuredPlanText(item, false), "supportNeed", text(item.getSupportNeed())))) .toList(); - List renderedRows = renderRowsByTemplateRow(location, rows, List.of( + List renderedRows = renderRowsByTemplateRow(location, rows, List.of( "itemNumber", "itemTitle", "targetText", "supportNeed")); rewritePlanRows(renderedRows, items, false); } - private void renderMonthlyFeedbackTables(XWPFDocument document, MonthlyReportApprovalRecordRespVO approvalRecord) { + private void renderMonthlyFeedbackTables(WordprocessingMLPackage document, MonthlyReportApprovalRecordRespVO approvalRecord) { TemplateRowLocation strengthLocation = findTemplateRowByPlaceholders(document, List.of( "优势", "{{strengthDesc}}", "{{strengthExample}}", "{{improvementSuggestion}}")); if (strengthLocation != null) { @@ -606,30 +668,34 @@ public class WorkReportContentExportService { } private void renderSingleTemplateRow(TemplateRowLocation location, Map values) { - XWPFTableRow row = location.table().getRow(location.rowIndex()); - CTRow renderedRow = parseRenderedRow(row.getCtRow().xmlText(), values); - row.getCtRow().set(renderedRow); + Tr renderedRow = (Tr) XmlUtils.deepCopy(tableRow(location.table(), location.rowIndex())); + replacePlaceholdersInObject(renderedRow, values); + location.table().getContent().set(location.rowIndex(), renderedRow); } - private void rewriteReviewRows(List rows, List items, + private void rewriteReviewRows(List rows, List items, boolean includeDetail) { List safeItems = safeList(items); for (int i = 0; i < rows.size() && i < safeItems.size(); i++) { PersonalReportReviewItemRespVO item = safeItems.get(i); - XWPFTableRow row = rows.get(i); - setStructuredReviewCell(row.getCell(2), item, includeDetail); - setCellText(row.getCell(3), text(item.getReflectionText()), false); + List cells = rowCells(rows.get(i)); + setCenteredCellText(cells.get(0), formatNumber(item.getItemNumber()), false); + setCenteredCellText(cells.get(1), appendHours(item.getItemTitle(), item.getWorkHours()), false); + setStructuredReviewCell(cells.get(2), item, includeDetail); + setCellText(cells.get(3), text(item.getReflectionText()), false); } } - private void rewritePlanRows(List rows, List items, + private void rewritePlanRows(List rows, List items, boolean includeDetail) { List safeItems = safeList(items); for (int i = 0; i < rows.size() && i < safeItems.size(); i++) { PersonalReportPlanItemRespVO item = safeItems.get(i); - XWPFTableRow row = rows.get(i); - setStructuredPlanCell(row.getCell(2), item, includeDetail); - setCellText(row.getCell(3), text(item.getSupportNeed()), false); + List cells = rowCells(rows.get(i)); + setCenteredCellText(cells.get(0), formatNumber(item.getItemNumber()), false); + setCenteredCellText(cells.get(1), text(item.getItemTitle()), false); + setStructuredPlanCell(cells.get(2), item, includeDetail); + setCellText(cells.get(3), text(item.getSupportNeed()), false); } } @@ -643,7 +709,7 @@ public class WorkReportContentExportService { item == null ? null : item.getTargetText(), false, includeDetail); } - private void setStructuredReviewCell(XWPFTableCell cell, PersonalReportReviewItemRespVO item, + private void setStructuredReviewCell(Tc cell, PersonalReportReviewItemRespVO item, boolean includeDetail) { setStructuredCellTextForTemplate(cell, item == null ? null : item.getContentJson(), @@ -652,7 +718,7 @@ public class WorkReportContentExportService { includeDetail); } - private void setStructuredPlanCell(XWPFTableCell cell, PersonalReportPlanItemRespVO item, + private void setStructuredPlanCell(Tc cell, PersonalReportPlanItemRespVO item, boolean includeDetail) { setStructuredCellTextForTemplate(cell, item == null ? null : item.getTargetJson(), @@ -661,18 +727,17 @@ public class WorkReportContentExportService { includeDetail); } - private void setStructuredCellTextForTemplate(XWPFTableCell cell, Object jsonValue, + private void setStructuredCellTextForTemplate(Tc cell, Object jsonValue, String fallbackText, boolean includeHours, boolean includeDetail) { List lines = buildStructuredParagraphsForTemplate(jsonValue, fallbackText, includeHours, includeDetail); unsetNoWrap(cell); if (lines.isEmpty()) { - setCellTextByTemplateParagraph(cell, normalizeLineBreaks(fallbackText), false); + setStructuredTemplateCellParagraphs(cell, splitTextLinesPreserveBlank(normalizeLineBreaks(fallbackText)), false); return; } - setCellTextByTemplateParagraph(cell, String.join("\n", lines), false); + setStructuredTemplateCellParagraphs(cell, lines, false); } - private List buildStructuredParagraphsForTemplate(Object jsonValue, String fallbackText, boolean includeHours, boolean includeDetail) { List sections = parseStructuredSections(jsonValue); @@ -687,19 +752,19 @@ public class WorkReportContentExportService { for (StructuredSectionData section : sections) { String category = normalizeSectionCategory(section.category()); if (StringUtils.hasText(category)) { - lines.add("# " + category + "\uFF1A"); + lines.add("#" + category + ":"); } List tasks = safeList(section.tasks()); for (int i = 0; i < tasks.size(); i++) { StructuredTaskData task = tasks.get(i); - String taskLine = (i + 1) + "\u3001 " + formatStructuredTaskForTemplate(task, includeHours, priorityLabelMap); + String taskLine = (i + 1) + "、" + formatStructuredTaskForTemplate(task, includeHours, priorityLabelMap); String detail = includeDetail ? text(task.detail()) : ""; if (StringUtils.hasText(detail)) { List detailLines = splitTextLines(detail); if (detailLines.isEmpty()) { - lines.add(taskLine + "\uFF1A"); + lines.add(taskLine + ":"); } else { - lines.add(taskLine + "\uFF1A" + detailLines.get(0)); + lines.add(taskLine + ":" + detailLines.get(0)); for (int j = 1; j < detailLines.size(); j++) { lines.add(detailLines.get(j)); } @@ -722,12 +787,12 @@ public class WorkReportContentExportService { metrics.add(resolvePriorityLabel(task.priority(), priorityLabelMap)); } if (task.progress() != null) { - metrics.add("\u8FDB\u5EA6 " + formatDecimal(task.progress()) + "%"); + metrics.add("进度 " + formatDecimal(task.progress()) + "%"); } if (includeHours && task.hours() != null) { metrics.add(formatDecimal(task.hours()) + "h"); } - return text(task.title()) + (metrics.isEmpty() ? "" : "\uFF08" + String.join(" / ", metrics) + "\uFF09"); + return text(task.title()) + (metrics.isEmpty() ? "" : "(" + String.join(" / ", metrics) + ")"); } private List parseStructuredSectionsForTemplate(String textValue) { @@ -846,7 +911,7 @@ public class WorkReportContentExportService { Map priorityLabelMap = loadPriorityLabelMap(); List lines = new ArrayList<>(); for (StructuredSectionData section : sections) { - lines.add("# " + normalizeSectionCategory(section.category()) + ":"); + lines.add("#" + normalizeSectionCategory(section.category()) + ":"); List tasks = safeList(section.tasks()); for (int i = 0; i < tasks.size(); i++) { StructuredTaskData task = tasks.get(i); @@ -870,7 +935,7 @@ public class WorkReportContentExportService { return String.join("\n", lines); } - private void setStructuredCellText(XWPFTableCell cell, Object jsonValue, String fallbackText, boolean includeHours) { + private void setStructuredCellText(Tc cell, Object jsonValue, String fallbackText, boolean includeHours) { List lines = buildStructuredLines(jsonValue, fallbackText, includeHours); if (lines.isEmpty()) { setCellText(cell, fallbackText, false); @@ -890,7 +955,7 @@ public class WorkReportContentExportService { Map priorityLabelMap = loadPriorityLabelMap(); List lines = new ArrayList<>(); for (StructuredSectionData section : sections) { - lines.add("# " + normalizeSectionCategory(section.category()) + ":"); + lines.add("#" + normalizeSectionCategory(section.category()) + ":"); List tasks = safeList(section.tasks()); for (int i = 0; i < tasks.size(); i++) { lines.add((i + 1) + "、" + formatStructuredTask(tasks.get(i), includeHours, priorityLabelMap)); @@ -1082,7 +1147,7 @@ public class WorkReportContentExportService { metrics.add(resolvePriorityLabel(task.priority(), priorityLabelMap)); } if (task.progress() != null) { - metrics.add("\u8FDB\u5EA6 " + formatDecimal(task.progress()) + "%"); + metrics.add("进度 " + formatDecimal(task.progress()) + "%"); } if (includeHours && task.hours() != null) { metrics.add(formatDecimal(task.hours()) + "h"); @@ -1291,31 +1356,22 @@ public class WorkReportContentExportService { return String.join("\n", lines); } - private void addTitle(XWPFDocument document, String title) { - XWPFParagraph paragraph = document.createParagraph(); - paragraph.setAlignment(ParagraphAlignment.CENTER); - XWPFRun run = paragraph.createRun(); - run.setBold(true); - run.setFontSize(18); - run.setText(title); + private void addTitle(WordprocessingMLPackage document, String title) { + P paragraph = createParagraph(title, true, TITLE_FONT_SIZE, JcEnumeration.CENTER); + document.getMainDocumentPart().addObject(paragraph); } - private void addSectionTitle(XWPFDocument document, String title) { - XWPFParagraph paragraph = document.createParagraph(); - XWPFRun run = paragraph.createRun(); - run.setBold(true); - run.setFontSize(12); - run.setText(title); + private void addSectionTitle(WordprocessingMLPackage document, String title) { + P paragraph = createParagraph(title, true, SECTION_TITLE_FONT_SIZE, null); + document.getMainDocumentPart().addObject(paragraph); } - private void addParagraph(XWPFDocument document, String text, boolean bold) { - XWPFParagraph paragraph = document.createParagraph(); - XWPFRun run = paragraph.createRun(); - run.setBold(bold); - run.setText(text(text)); + private void addParagraph(WordprocessingMLPackage document, String text, boolean bold) { + P paragraph = createParagraph(text, bold, DEFAULT_FONT_SIZE, null); + document.getMainDocumentPart().addObject(paragraph); } - private void addPersonalBaseTable(XWPFDocument document, String name, String deptName, + private void addPersonalBaseTable(WordprocessingMLPackage document, String name, String deptName, String postName, String managerName) { addKeyValueTable(document, List.of( row("姓名", name), @@ -1324,156 +1380,386 @@ public class WorkReportContentExportService { row("直接上级", managerName))); } - private void addPersonalReviewTable(XWPFDocument document, List items, + private void addPersonalReviewTable(WordprocessingMLPackage document, List items, boolean includeDetail) { - XWPFTable table = document.createTable(1, 4); + Tbl table = TblFactory.createTable(1, 4, 2000); setTableWidth(table); - setRowText(table.getRow(0), List.of("序号", "工作事项", "具体工作内容及成果描述", "工作感悟"), true); + setRowText(tableRow(table, 0), List.of("序号", "工作事项", "具体工作内容及成果描述", "工作感悟"), true); for (PersonalReportReviewItemRespVO item : safeList(items)) { - XWPFTableRow row = table.createRow(); - setCellText(row.getCell(0), formatNumber(item.getItemNumber()), false); - setCellText(row.getCell(1), appendHours(item.getItemTitle(), item.getWorkHours()), false); - setCellText(row.getCell(2), buildStructuredReviewText(item, includeDetail), false); - setCellText(row.getCell(3), item.getReflectionText(), false); + Tr row = addTableRow(table, 4); + List cells = rowCells(row); + setCellText(cells.get(0), formatNumber(item.getItemNumber()), false); + setCellText(cells.get(1), appendHours(item.getItemTitle(), item.getWorkHours()), false); + setCellText(cells.get(2), buildStructuredReviewText(item, includeDetail), false); + setCellText(cells.get(3), item.getReflectionText(), false); } + document.getMainDocumentPart().addObject(table); } - private void addPersonalPlanTable(XWPFDocument document, List items, + private void addPersonalPlanTable(WordprocessingMLPackage document, List items, boolean includeDetail) { - XWPFTable table = document.createTable(1, 4); + Tbl table = TblFactory.createTable(1, 4, 2000); setTableWidth(table); - setRowText(table.getRow(0), List.of("序号", "工作事项", "具体目标", "对他人协助的需求"), true); + setRowText(tableRow(table, 0), List.of("序号", "工作事项", "具体目标", "对他人协助的需求"), true); for (PersonalReportPlanItemRespVO item : safeList(items)) { - XWPFTableRow row = table.createRow(); - setCellText(row.getCell(0), formatNumber(item.getItemNumber()), false); - setCellText(row.getCell(1), item.getItemTitle(), false); - setCellText(row.getCell(2), buildStructuredPlanText(item, includeDetail), false); - setCellText(row.getCell(3), item.getSupportNeed(), false); + Tr row = addTableRow(table, 4); + List cells = rowCells(row); + setCellText(cells.get(0), formatNumber(item.getItemNumber()), false); + setCellText(cells.get(1), item.getItemTitle(), false); + setCellText(cells.get(2), buildStructuredPlanText(item, includeDetail), false); + setCellText(cells.get(3), item.getSupportNeed(), false); } + document.getMainDocumentPart().addObject(table); } - private void addFeedbackTable(XWPFDocument document, MonthlyReportApprovalRecordRespVO record) { - XWPFTable table = document.createTable(1, 4); + private void addFeedbackTable(WordprocessingMLPackage document, MonthlyReportApprovalRecordRespVO record) { + Tbl table = TblFactory.createTable(1, 4, 2000); setTableWidth(table); - setRowText(table.getRow(0), List.of("优势/劣势项", "优势/劣势描述", "行为事例", "改进提升建议"), true); - XWPFTableRow strengthRow = table.createRow(); + setRowText(tableRow(table, 0), List.of("优势/劣势项", "优势/劣势描述", "行为事例", "改进提升建议"), true); + Tr strengthRow = addTableRow(table, 4); setRowText(strengthRow, values("优势", record == null ? null : record.getStrengthDesc(), record == null ? null : record.getStrengthExample(), ""), false); - XWPFTableRow weaknessRow = table.createRow(); + Tr weaknessRow = addTableRow(table, 4); setRowText(weaknessRow, values("劣势", record == null ? null : record.getWeaknessDesc(), record == null ? null : record.getWeaknessExample(), record == null ? null : record.getImprovementSuggestion()), false); + document.getMainDocumentPart().addObject(table); } - private void addProjectItemTable(XWPFDocument document, List items, boolean showHours) { - XWPFTable table = document.createTable(1, showHours ? 4 : 3); + private void addProjectItemTable(WordprocessingMLPackage document, List items, boolean showHours) { + Tbl table = TblFactory.createTable(1, showHours ? 4 : 3, 2000); setTableWidth(table); - setRowText(table.getRow(0), showHours + setRowText(tableRow(table, 0), showHours ? List.of("工作内容", "工时", "优先级", "进度") : List.of("工作内容", "优先级", "进度"), true); for (ProjectReportItemRespVO item : safeList(items)) { - XWPFTableRow row = table.createRow(); + Tr row = addTableRow(table, showHours ? 4 : 3); setRowText(row, showHours ? values(item.getItemTitle(), formatDecimal(item.getWorkHours()), item.getPriorityCode(), formatProgress(item.getProgressRate())) : values(item.getItemTitle(), item.getPriorityCode(), formatProgress(item.getProgressRate())), false); } + document.getMainDocumentPart().addObject(table); } - private void addKeyValueTable(XWPFDocument document, List> rows) { - XWPFTable table = document.createTable(rows.size(), 2); + private void addKeyValueTable(WordprocessingMLPackage document, List> rows) { + Tbl table = TblFactory.createTable(rows.size(), 2, 4000); setTableWidth(table); for (int i = 0; i < rows.size(); i++) { - setRowText(table.getRow(i), rows.get(i), i == 0 && rows.size() == 1); + setRowText(tableRow(table, i), rows.get(i), i == 0 && rows.size() == 1); + } + document.getMainDocumentPart().addObject(table); + } + + private void setTableWidth(Tbl table) { + TblPr tblPr = table.getTblPr(); + if (tblPr == null) { + tblPr = WML_FACTORY.createTblPr(); + table.setTblPr(tblPr); + } + TblWidth width = tblPr.getTblW(); + if (width == null) { + width = WML_FACTORY.createTblWidth(); + tblPr.setTblW(width); + } + width.setType("pct"); + width.setW(BigInteger.valueOf(5000)); + } + + private void setRowText(Tr row, List values, boolean bold) { + List cells = rowCells(row); + for (int i = 0; i < values.size() && i < cells.size(); i++) { + setCellText(cells.get(i), values.get(i), bold); } } - private void setTableWidth(XWPFTable table) { - table.setWidth("100%"); + private void setCellText(Tc cell, String value, boolean bold) { + clearCellContent(cell); + cell.getContent().add(createParagraphWithBreaks(value, bold, true)); } - private void setRowText(XWPFTableRow row, List values, boolean bold) { - for (int i = 0; i < values.size(); i++) { - setCellText(row.getCell(i), values.get(i), bold); - } + private void setCenteredCellText(Tc cell, String value, boolean bold) { + setCellText(cell, value, bold); + applyCellAlignment(cell, JcEnumeration.CENTER, STVerticalJc.CENTER, false); } - private void setCellText(XWPFTableCell cell, String value, boolean bold) { - while (cell.getParagraphs().size() > 0) { - cell.removeParagraph(0); - } - XWPFParagraph paragraph = cell.addParagraph(); - XWPFRun run = paragraph.createRun(); - run.setBold(bold); - String[] lines = text(value).split("\\R", -1); - for (int i = 0; i < lines.length; i++) { - if (i > 0) { - run.addBreak(); - } - run.setText(lines[i]); - } - } - - private void setCellTextByTemplateParagraph(XWPFTableCell cell, String value, boolean bold) { + private void setStructuredTemplateCellParagraphs(Tc cell, List lines, boolean bold) { unsetNoWrap(cell); - XWPFParagraph paragraph; - if (cell.getParagraphs() == null || cell.getParagraphs().isEmpty()) { - paragraph = cell.addParagraph(); - } else { - paragraph = cell.getParagraphs().get(0); - for (int i = cell.getParagraphs().size() - 1; i >= 1; i--) { - cell.removeParagraph(i); - } + clearCellContent(cell); + List safeLines = (lines == null || lines.isEmpty()) ? List.of("") : lines; + for (String line : safeLines) { + P paragraph = createParagraph(line, bold, DEFAULT_FONT_SIZE, null); + applyParagraphFormatting(paragraph, line, false); + cell.getContent().add(paragraph); } - List runs = paragraph.getRuns(); - XWPFRun firstRun; - if (runs == null || runs.isEmpty()) { - firstRun = paragraph.createRun(); - } else { - firstRun = runs.get(0); - for (int i = runs.size() - 1; i >= 1; i--) { - paragraph.removeRun(i); - } - } - firstRun.setBold(bold); - applyRunText(firstRun, value); } - private void unsetNoWrap(XWPFTableCell cell) { - if (cell == null || cell.getCTTc() == null) { + private void applyCellAlignment(Tc cell, JcEnumeration horizontal, STVerticalJc vertical, boolean firstLineIndent) { + if (cell == null) { return; } - var tcPr = cell.getCTTc().getTcPr(); - if (tcPr != null && tcPr.isSetNoWrap()) { - tcPr.unsetNoWrap(); + TcPr tcPr = cell.getTcPr(); + if (tcPr == null) { + tcPr = WML_FACTORY.createTcPr(); + cell.setTcPr(tcPr); + } + if (vertical != null) { + CTVerticalJc verticalJc = WML_FACTORY.createCTVerticalJc(); + verticalJc.setVal(vertical); + tcPr.setVAlign(verticalJc); + } + for (P paragraph : findAllParagraphs(cell)) { + ensureParagraphFormatting(paragraph, horizontal, firstLineIndent); } } - private void setCellParagraphs(XWPFTableCell cell, List lines, boolean bold) { - while (cell.getParagraphs().size() > 0) { - cell.removeParagraph(0); + private void applyParagraphFormatting(P paragraph, String value, boolean firstLineIndent) { + ensureParagraphFormatting(paragraph, null, firstLineIndent); + PPr pPr = paragraph.getPPr(); + PPrBase.Spacing spacing = pPr.getSpacing(); + if (spacing == null) { + spacing = WML_FACTORY.createPPrBaseSpacing(); + pPr.setSpacing(spacing); } + spacing.setBefore(BigInteger.ZERO); + spacing.setAfter(BigInteger.ZERO); + if (!StringUtils.hasText(value)) { + spacing.setLineRule(STLineSpacingRule.AUTO); + } + } + + private void ensureParagraphFormatting(P paragraph, JcEnumeration alignment, boolean firstLineIndent) { + PPr pPr = paragraph.getPPr(); + if (pPr == null) { + pPr = WML_FACTORY.createPPr(); + paragraph.setPPr(pPr); + } + pPr.setNumPr(null); + if (alignment != null) { + Jc jc = pPr.getJc(); + if (jc == null) { + jc = WML_FACTORY.createJc(); + pPr.setJc(jc); + } + jc.setVal(alignment); + } + PPrBase.Ind ind = pPr.getInd(); + if (ind == null) { + ind = WML_FACTORY.createPPrBaseInd(); + pPr.setInd(ind); + } + ind.setLeft(BigInteger.ZERO); + ind.setLeftChars(BigInteger.ZERO); + ind.setRight(BigInteger.ZERO); + ind.setRightChars(BigInteger.ZERO); + ind.setStart(BigInteger.ZERO); + ind.setStartChars(BigInteger.ZERO); + ind.setEnd(BigInteger.ZERO); + ind.setEndChars(BigInteger.ZERO); + ind.setHanging(BigInteger.ZERO); + ind.setHangingChars(BigInteger.ZERO); + ind.setFirstLine(firstLineIndent ? BigInteger.valueOf(420) : BigInteger.ZERO); + ind.setFirstLineChars(BigInteger.ZERO); + } + + private void unsetNoWrap(Tc cell) { + if (cell == null) { + return; + } + TcPr tcPr = cell.getTcPr(); + if (tcPr != null) { + tcPr.setNoWrap(null); + } + } + + private void setCellParagraphs(Tc cell, List lines, boolean bold) { + clearCellContent(cell); List safeLines = lines == null || lines.isEmpty() ? List.of("") : lines; for (String line : safeLines) { - XWPFParagraph paragraph = cell.addParagraph(); - paragraph.setSpacingBefore(0); - paragraph.setSpacingAfter(0); - XWPFRun run = paragraph.createRun(); - run.setBold(bold); - run.setText(text(line)); + P paragraph = createParagraph(line, bold, DEFAULT_FONT_SIZE, null); + applyParagraphFormatting(paragraph, line, false); + cell.getContent().add(paragraph); } } - private byte[] toBytes(XWPFDocument document) throws IOException { + private byte[] toBytes(WordprocessingMLPackage document) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - document.write(outputStream); + try { + document.save(outputStream); + } catch (Docx4JException e) { + throw new IOException("save docx failed", e); + } return outputStream.toByteArray(); } + private P createParagraph(String value, boolean bold, BigInteger fontSize, JcEnumeration alignment) { + P paragraph = WML_FACTORY.createP(); + ensureParagraphFormatting(paragraph, alignment, false); + R run = createRun(value, bold, fontSize); + paragraph.getContent().add(run); + return paragraph; + } + + private P createParagraphWithBreaks(String value, boolean bold, boolean preserveEmptyLines) { + P paragraph = WML_FACTORY.createP(); + R run = WML_FACTORY.createR(); + run.setRPr(createRunProperties(bold, DEFAULT_FONT_SIZE)); + String[] lines = splitRawLines(value); + for (int i = 0; i < lines.length; i++) { + if (i > 0) { + run.getContent().add(WML_FACTORY.createBr()); + } + if (preserveEmptyLines || StringUtils.hasText(lines[i])) { + Text textNode = WML_FACTORY.createText(); + textNode.setValue(lines[i]); + if (lines[i].startsWith(" ") || lines[i].endsWith(" ")) { + textNode.setSpace("preserve"); + } + run.getContent().add(textNode); + } + } + paragraph.getContent().add(run); + applyParagraphFormatting(paragraph, value, false); + return paragraph; + } + + private void rewriteParagraphText(P paragraph, String value, boolean preserveEmptyLines) { + R firstRun = findFirstRun(paragraph); + RPr originalRunProperties = firstRun == null ? null : firstRun.getRPr(); + paragraph.getContent().clear(); + R run = WML_FACTORY.createR(); + if (originalRunProperties != null) { + run.setRPr((RPr) XmlUtils.deepCopy(originalRunProperties)); + } + String[] lines = splitRawLines(value); + for (int i = 0; i < lines.length; i++) { + if (i > 0) { + run.getContent().add(WML_FACTORY.createBr()); + } + if (preserveEmptyLines || StringUtils.hasText(lines[i])) { + Text textNode = WML_FACTORY.createText(); + textNode.setValue(lines[i]); + if (lines[i].startsWith(" ") || lines[i].endsWith(" ")) { + textNode.setSpace("preserve"); + } + run.getContent().add(textNode); + } + } + paragraph.getContent().add(run); + } + + private P findParagraphByPlaceholder(Tc cell, String placeholder) { + if (cell == null || !StringUtils.hasText(placeholder)) { + return null; + } + for (P paragraph : findAllParagraphs(cell)) { + String paragraphText = findAllTextNodes(paragraph).stream() + .map(Text::getValue) + .filter(Objects::nonNull) + .collect(java.util.stream.Collectors.joining()); + if (paragraphText.contains(placeholder)) { + return paragraph; + } + } + return null; + } + + private void setTemplateCellParagraphs(Tc cell, P templateParagraph, List lines, boolean bold) { + unsetNoWrap(cell); + clearCellContent(cell); + List safeLines = (lines == null || lines.isEmpty()) ? List.of("") : lines; + for (String line : safeLines) { + P paragraph = templateParagraph == null + ? createParagraph(line, bold, DEFAULT_FONT_SIZE, null) + : (P) XmlUtils.deepCopy(templateParagraph); + rewriteParagraphText(paragraph, line, true); + applyParagraphFormatting(paragraph, line, false); + cell.getContent().add(paragraph); + } + } + + private R findFirstRun(P paragraph) { + if (paragraph == null) { + return null; + } + for (Object content : paragraph.getContent()) { + Object value = XmlUtils.unwrap(content); + if (value instanceof R run) { + return run; + } + } + return null; + } + + private String[] splitRawLines(String value) { + String source = value == null ? "" : value; + source = source.replace("\r\n", "\n").replace('\r', '\n'); + return source.split("\\R", -1); + } + + private List splitTextLinesPreserveBlank(String value) { + String normalized = normalizeLineBreaks(text(value)); + if (!StringUtils.hasText(normalized)) { + return List.of(); + } + return Arrays.asList(normalized.split("\\R", -1)); + } + + private R createRun(String value, boolean bold, BigInteger fontSize) { + R run = WML_FACTORY.createR(); + run.setRPr(createRunProperties(bold, fontSize)); + Text textNode = WML_FACTORY.createText(); + textNode.setValue(text(value)); + run.getContent().add(textNode); + return run; + } + + private RPr createRunProperties(boolean bold, BigInteger fontSize) { + RPr rPr = WML_FACTORY.createRPr(); + if (bold) { + BooleanDefaultTrue b = WML_FACTORY.createBooleanDefaultTrue(); + b.setVal(Boolean.TRUE); + rPr.setB(b); + rPr.setBCs(b); + } + HpsMeasure size = WML_FACTORY.createHpsMeasure(); + size.setVal(fontSize); + rPr.setSz(size); + HpsMeasure sizeCs = WML_FACTORY.createHpsMeasure(); + sizeCs.setVal(fontSize); + rPr.setSzCs(sizeCs); + return rPr; + } + + private Tr addTableRow(Tbl table, int cellCount) { + Tr row = WML_FACTORY.createTr(); + for (int i = 0; i < cellCount; i++) { + Tc cell = WML_FACTORY.createTc(); + cell.getContent().add(WML_FACTORY.createP()); + row.getContent().add(cell); + } + table.getContent().add(row); + return row; + } + + private List rowCells(Tr row) { + List cells = new ArrayList<>(); + for (Object content : row.getContent()) { + Object value = XmlUtils.unwrap(content); + if (value instanceof Tc cell) { + cells.add(cell); + } + } + return cells; + } + + private void clearCellContent(Tc cell) { + cell.getContent().clear(); + } private String buildTravelText(List travelSegments) { if (travelSegments == null || travelSegments.isEmpty()) { return ""; diff --git a/rdms-project/rdms-project-boot/src/main/resources/application-local.yaml b/rdms-project/rdms-project-boot/src/main/resources/application-local.yaml index 4135ce4..f47cbf8 100644 --- a/rdms-project/rdms-project-boot/src/main/resources/application-local.yaml +++ b/rdms-project/rdms-project-boot/src/main/resources/application-local.yaml @@ -6,12 +6,12 @@ spring: username: # Nacos 账号 password: # Nacos 密码 discovery: # 【配置中心】配置项 - namespace: 1924bcfb-4eab-4c58-9003-4a37d5fc2949 # 命名空间。这里使用 dev 开发环境 + namespace: 0cd9c1b2-56ba-4e1d-a23b-f951392c46bf # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: version: 1.0.0 # 服务实例的版本号,可用于灰度发布 config: # 【注册中心】配置项 - namespace: 1924bcfb-4eab-4c58-9003-4a37d5fc2949 # 命名空间。这里使用 dev 开发环境 + namespace: 0cd9c1b2-56ba-4e1d-a23b-f951392c46bf # 命名空间。这里使用 dev 开发环境 group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP #################### 数据库相关配置 #################### @@ -55,7 +55,7 @@ spring: primary: master datasource: master: - url: jdbc:mysql://192.168.1.22:13306/rdms_view?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://192.168.1.22:13306/rdms_v3?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 username: root password: njcnpqs @@ -63,7 +63,7 @@ spring: data: redis: host: 127.0.0.1 # 地址 - port: 16379 # 端口 + port: 6379 # 端口 database: 1 # 数据库索引 # password: njcnpqs # 密码,建议生产环境开启 diff --git a/rdms-project/rdms-project-boot/src/main/resources/templates/work-report/weekly-report-template.docx b/rdms-project/rdms-project-boot/src/main/resources/templates/work-report/weekly-report-template.docx index 97bb06c38e114f8d9ca63640ae2f115f564a60d4..0968abd68db5338410fa27e827e86b37b165d590 100644 GIT binary patch delta 10822 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 QKNph(Ul0c7SpWb40J4(IhX4Qo delta 10699 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
9e?Lrb$s*mH)BR; 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 QKNph(Ul0c7SpWb40J4(IhX4Qo delta 10699 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 QKNph(Ul0c7SpWb40J4(IhX4Qo delta 10699 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
e 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
%1^`ua}B?W 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
WT1}rSKyuwunIRcui4p#=);-_=QxK#||4Z zeW>A?!w!K)+lA#0+@ausN3~%+-95t)?6+G0j!X~gf1fRd+HS$bQG^)kUX!@-CCyb} z_$-Yv+6@xqN?QGDYkvp`bN2Lf(alP_2mlP6j#1c_7t0F&=q z7L#jQ8Un3UlZRT81&HX0H&c@yTT}r(lXF`@2?YfJ0C#V4WG|B~Un&CARg>sjHv;@s zlObF#8