From 33239700fdb0e7ce72c37baa0db8927dea5bb1fb Mon Sep 17 00:00:00 2001
From: dk <1260500659@qq.com>
Date: Thu, 11 Jun 2026 09:58:31 +0800
Subject: [PATCH] =?UTF-8?q?fix(=E5=8A=A0=E7=8F=AD=E7=94=B3=E8=AF=B7):=20?=
=?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=92=A4=E9=94=80=E7=9B=B8=E5=85=B3=E7=9A=84?=
=?UTF-8?q?=E7=8A=B6=E6=80=81=E5=92=8C=E5=8A=A8=E4=BD=9C=E3=80=82=20feat(?=
=?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=8A=A5=E5=91=8A):=20=E5=BC=80=E5=8F=91?=
=?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=8A=A5=E5=91=8A=E5=8A=9F=E8=83=BD=EF=BC=8C?=
=?UTF-8?q?=E5=8C=85=E5=90=AB=E5=AF=BC=E5=87=BA=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../project/enums/ErrorCodeConstants.java | 18 +-
rdms-project/rdms-project-boot/pom.xml | 6 +
.../OvertimeApplicationConstants.java | 2 -
.../project/constant/WorkReportConstants.java | 41 +
.../OvertimeApplicationController.java | 21 +-
...ertimeApplicationApprovalRecordRespVO.java | 29 +
.../OvertimeApplicationStatusActionReqVO.java | 2 +-
.../common/WorkReportExportResponseUtils.java | 20 +
.../common/WorkReportStatusController.java | 35 +
.../vo/PersonalReportPlanItemReqVO.java | 19 +
.../vo/PersonalReportPlanItemRespVO.java | 21 +
.../vo/PersonalReportReviewItemReqVO.java | 23 +
.../vo/PersonalReportReviewItemRespVO.java | 25 +
.../vo/WorkReportApprovalRecordRespVO.java | 27 +
.../common/vo/WorkReportBasePageReqVO.java | 32 +
.../vo/WorkReportMemberSnapshotRespVO.java | 13 +
.../vo/WorkReportStatusActionReqVO.java | 14 +
.../common/vo/WorkReportStatusDictRespVO.java | 27 +
.../common/vo/WorkReportStatusLogRespVO.java | 35 +
.../monthly/MonthlyReportController.java | 172 ++
.../vo/MonthlyReportApprovalRecordRespVO.java | 50 +
.../monthly/vo/MonthlyReportApproveReqVO.java | 57 +
.../vo/MonthlyReportContentExportReqVO.java | 19 +
.../vo/MonthlyReportDefaultDraftReqVO.java | 30 +
.../monthly/vo/MonthlyReportExportVO.java | 44 +
.../monthly/vo/MonthlyReportPageReqVO.java | 12 +
.../monthly/vo/MonthlyReportRespVO.java | 41 +
.../monthly/vo/MonthlyReportSaveReqVO.java | 40 +
.../project/ProjectReportController.java | 181 ++
.../vo/ProjectReportContentExportReqVO.java | 19 +
.../vo/ProjectReportDefaultDraftReqVO.java | 33 +
.../project/vo/ProjectReportExportVO.java | 47 +
.../project/vo/ProjectReportItemReqVO.java | 19 +
.../project/vo/ProjectReportItemRespVO.java | 21 +
...ProjectReportOwnerProjectOptionRespVO.java | 18 +
.../project/vo/ProjectReportPageReqVO.java | 18 +
.../project/vo/ProjectReportRespVO.java | 47 +
.../project/vo/ProjectReportSaveReqVO.java | 52 +
.../weekly/WeeklyReportController.java | 171 ++
.../vo/WeeklyReportContentExportReqVO.java | 19 +
.../vo/WeeklyReportDefaultDraftReqVO.java | 30 +
.../weekly/vo/WeeklyReportExportVO.java | 50 +
.../weekly/vo/WeeklyReportPageReqVO.java | 15 +
.../weekly/vo/WeeklyReportRespVO.java | 44 +
.../weekly/vo/WeeklyReportSaveReqVO.java | 46 +
.../vo/WeeklyReportTravelSegmentReqVO.java | 27 +
.../vo/WeeklyReportTravelSegmentRespVO.java | 24 +
.../OvertimeApplicationApprovalRecordDO.java | 36 +
.../common/PersonalReportPlanItemDO.java | 40 +
.../common/PersonalReportReviewItemDO.java | 45 +
.../common/WorkReportMemberSnapshotItem.java | 14 +
.../common/WorkReportStatusLogDO.java | 44 +
.../MonthlyReportApprovalRecordDO.java | 71 +
.../workreport/monthly/MonthlyReportDO.java | 64 +
.../ProjectReportApprovalRecordDO.java | 36 +
.../project/ProjectReportCurrentItemDO.java | 36 +
.../workreport/project/ProjectReportDO.java | 85 +
.../project/ProjectReportNextItemDO.java | 33 +
.../weekly/WeeklyReportApprovalRecordDO.java | 36 +
.../workreport/weekly/WeeklyReportDO.java | 69 +
.../weekly/WeeklyReportTravelSegmentDO.java | 36 +
...ertimeApplicationApprovalRecordMapper.java | 24 +
.../mysql/personal/PersonalItemMapper.java | 32 +
.../dal/mysql/project/ProjectMapper.java | 6 +
.../execution/ProjectExecutionMapper.java | 54 +-
.../mysql/project/task/ProjectTaskMapper.java | 68 +
.../mysql/project/task/TaskWorklogMapper.java | 26 +
.../common/PersonalReportPlanItemMapper.java | 26 +
.../PersonalReportReviewItemMapper.java | 26 +
.../common/WorkReportStatusLogMapper.java | 26 +
.../MonthlyReportApprovalRecordMapper.java | 29 +
.../monthly/MonthlyReportMapper.java | 123 ++
.../ProjectReportApprovalRecordMapper.java | 29 +
.../ProjectReportCurrentItemMapper.java | 23 +
.../project/ProjectReportMapper.java | 136 ++
.../project/ProjectReportNextItemMapper.java | 23 +
.../WeeklyReportApprovalRecordMapper.java | 29 +
.../workreport/weekly/WeeklyReportMapper.java | 124 ++
.../WeeklyReportTravelSegmentMapper.java | 24 +
.../rpc/config/RpcConfiguration.java | 9 +-
.../overtime/OvertimeApplicationService.java | 5 +-
.../OvertimeApplicationServiceImpl.java | 71 +-
.../common/WorkReportCommonService.java | 1354 ++++++++++++++
.../common/WorkReportStatusService.java | 10 +
.../common/WorkReportStatusServiceImpl.java | 19 +
.../WorkReportDefaultDraftService.java | 930 ++++++++++
.../WorkReportContentExportService.java | 1583 +++++++++++++++++
.../export/WorkReportExportFile.java | 4 +
.../monthly/MonthlyReportService.java | 45 +
.../monthly/MonthlyReportServiceImpl.java | 97 +
.../project/ProjectReportService.java | 47 +
.../project/ProjectReportServiceImpl.java | 106 ++
.../weekly/WeeklyReportService.java | 44 +
.../weekly/WeeklyReportServiceImpl.java | 96 +
.../work-report/monthly-report-template.docx | Bin 0 -> 25281 bytes
.../work-report/project-report-template.docx | Bin 0 -> 15071 bytes
.../work-report/weekly-report-template.docx | Bin 0 -> 24346 bytes
97 files changed, 7581 insertions(+), 68 deletions(-)
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/WorkReportConstants.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationApprovalRecordRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/WorkReportExportResponseUtils.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/WorkReportStatusController.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportPlanItemReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportPlanItemRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportReviewItemReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportReviewItemRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportApprovalRecordRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportBasePageReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportMemberSnapshotRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusActionReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusDictRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusLogRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/MonthlyReportController.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportApprovalRecordRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportApproveReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportContentExportReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportDefaultDraftReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportExportVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportPageReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportSaveReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/ProjectReportController.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportContentExportReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportDefaultDraftReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportExportVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportItemReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportItemRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportOwnerProjectOptionRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportPageReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportSaveReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/WeeklyReportController.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportContentExportReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportDefaultDraftReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportExportVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportPageReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportSaveReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportTravelSegmentReqVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportTravelSegmentRespVO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/overtime/OvertimeApplicationApprovalRecordDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/PersonalReportPlanItemDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/PersonalReportReviewItemDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/WorkReportMemberSnapshotItem.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/WorkReportStatusLogDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/monthly/MonthlyReportApprovalRecordDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/monthly/MonthlyReportDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportApprovalRecordDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportCurrentItemDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportNextItemDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportApprovalRecordDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportTravelSegmentDO.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/overtime/OvertimeApplicationApprovalRecordMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/PersonalReportPlanItemMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/PersonalReportReviewItemMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/WorkReportStatusLogMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/monthly/MonthlyReportApprovalRecordMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/monthly/MonthlyReportMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportApprovalRecordMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportCurrentItemMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportNextItemMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportApprovalRecordMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportTravelSegmentMapper.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/common/WorkReportCommonService.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/common/WorkReportStatusService.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/common/WorkReportStatusServiceImpl.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/defaultdraft/WorkReportDefaultDraftService.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/export/WorkReportContentExportService.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/export/WorkReportExportFile.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/monthly/MonthlyReportService.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/monthly/MonthlyReportServiceImpl.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/project/ProjectReportService.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/project/ProjectReportServiceImpl.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/weekly/WeeklyReportService.java
create mode 100644 rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/weekly/WeeklyReportServiceImpl.java
create mode 100644 rdms-project/rdms-project-boot/src/main/resources/templates/work-report/monthly-report-template.docx
create mode 100644 rdms-project/rdms-project-boot/src/main/resources/templates/work-report/project-report-template.docx
create mode 100644 rdms-project/rdms-project-boot/src/main/resources/templates/work-report/weekly-report-template.docx
diff --git a/rdms-project/rdms-project-api/src/main/java/com/njcn/rdms/module/project/enums/ErrorCodeConstants.java b/rdms-project/rdms-project-api/src/main/java/com/njcn/rdms/module/project/enums/ErrorCodeConstants.java
index e92172f..60b3096 100644
--- a/rdms-project/rdms-project-api/src/main/java/com/njcn/rdms/module/project/enums/ErrorCodeConstants.java
+++ b/rdms-project/rdms-project-api/src/main/java/com/njcn/rdms/module/project/enums/ErrorCodeConstants.java
@@ -240,5 +240,21 @@ public interface ErrorCodeConstants {
ErrorCode OVERTIME_APPLICATION_APPROVER_INVALID = new ErrorCode(1_008_009_008, "审核人不是有效系统用户");
ErrorCode OVERTIME_APPLICATION_APPROVER_SELF_FORBIDDEN = new ErrorCode(1_008_009_009, "审核人不能选择申请人本人");
ErrorCode OVERTIME_APPLICATION_READ_FORBIDDEN = new ErrorCode(1_008_009_010, "无权查看该加班申请");
- ErrorCode OVERTIME_APPLICATION_DELETE_ONLY_CANCELLED = new ErrorCode(1_008_009_011, "仅已撤销的加班申请允许删除");
+ ErrorCode OVERTIME_APPLICATION_DELETE_ONLY_REJECTED = new ErrorCode(1_008_009_011, "仅已退回的加班申请允许删除");
+ // ========== 工作报告 1_008_010_xxx ==========
+ ErrorCode WORK_REPORT_NOT_EXISTS = new ErrorCode(1_008_010_001, "工作报告不存在");
+ ErrorCode WORK_REPORT_STATUS_MODEL_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_008_010_002, "工作报告状态定义不存在或已停用");
+ ErrorCode WORK_REPORT_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_010_003, "当前工作报告状态不支持动作【{}】");
+ ErrorCode WORK_REPORT_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_010_004, "动作【{}】必须填写原因");
+ ErrorCode WORK_REPORT_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_010_005, "工作报告状态已发生变化,请刷新后重试");
+ ErrorCode WORK_REPORT_REPORTER_ONLY = new ErrorCode(1_008_010_006, "仅填报人可执行该操作");
+ ErrorCode WORK_REPORT_APPROVER_ONLY = new ErrorCode(1_008_010_007, "仅当前审核人可执行该操作");
+ ErrorCode WORK_REPORT_READ_FORBIDDEN = new ErrorCode(1_008_010_008, "无权查看该工作报告");
+ ErrorCode WORK_REPORT_DIRECT_MANAGER_NOT_EXISTS = new ErrorCode(1_008_010_009, "当前用户不存在生效中的直属上级,无法提交工作报告");
+ ErrorCode WORK_REPORT_PROJECT_NOT_EXISTS = new ErrorCode(1_008_010_010, "项目半月报对应的项目不存在");
+ ErrorCode WORK_REPORT_STATUS_NOT_ALLOW_EDIT = new ErrorCode(1_008_010_011, "当前工作报告状态不允许编辑");
+ ErrorCode WORK_REPORT_DELETE_NOT_ALLOWED = new ErrorCode(1_008_010_012, "当前工作报告状态不允许删除");
+ ErrorCode WORK_REPORT_PERIOD_DUPLICATE = new ErrorCode(1_008_010_013, "当前周期的工作报告已存在,请勿重复创建");
+ ErrorCode WORK_REPORT_APPROVAL_RECORD_NOT_EXISTS = new ErrorCode(1_008_010_014, "工作报告审核记录不存在");
+ ErrorCode WORK_REPORT_PROJECT_OWNER_ONLY = new ErrorCode(1_008_010_015, "仅项目负责人可创建或维护该项目的项目半月报");
}
diff --git a/rdms-project/rdms-project-boot/pom.xml b/rdms-project/rdms-project-boot/pom.xml
index b075fca..e24d952 100644
--- a/rdms-project/rdms-project-boot/pom.xml
+++ b/rdms-project/rdms-project-boot/pom.xml
@@ -65,6 +65,12 @@
rdms-spring-boot-starter-excel
+
+ org.apache.poi
+ poi-ooxml
+ 5.4.1
+
+
com.alibaba.cloud
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/OvertimeApplicationConstants.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/OvertimeApplicationConstants.java
index 04b4371..de05c72 100644
--- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/OvertimeApplicationConstants.java
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/OvertimeApplicationConstants.java
@@ -14,7 +14,6 @@ public final class OvertimeApplicationConstants {
public static final String STATUS_PENDING = "pending";
public static final String STATUS_APPROVED = "approved";
public static final String STATUS_REJECTED = "rejected";
- public static final String STATUS_CANCELLED = "cancelled";
/**
* 新建即提交的业务日志动作。
@@ -25,7 +24,6 @@ public final class OvertimeApplicationConstants {
public static final String ACTION_RESUBMIT = "resubmit";
public static final String ACTION_APPROVE = "approve";
public static final String ACTION_REJECT = "reject";
- public static final String ACTION_CANCEL = "cancel";
public static final String ACTION_DELETE = "delete";
public static final String PERMISSION_QUERY = "project:overtime-application:query";
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/WorkReportConstants.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/WorkReportConstants.java
new file mode 100644
index 0000000..f4c3772
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/constant/WorkReportConstants.java
@@ -0,0 +1,41 @@
+package com.njcn.rdms.module.project.constant;
+
+/**
+ * 工作报告常量。
+ */
+public final class WorkReportConstants {
+
+ private WorkReportConstants() {
+ }
+
+ public static final String STATUS_OBJECT_TYPE = "work_report";
+
+ public static final String REPORT_TYPE_WEEKLY = "weekly";
+ public static final String REPORT_TYPE_MONTHLY = "monthly";
+ public static final String REPORT_TYPE_PROJECT = "project";
+
+ public static final String BIZ_TYPE_WEEKLY = "weekly_report";
+ public static final String BIZ_TYPE_MONTHLY = "monthly_report";
+ public static final String BIZ_TYPE_PROJECT = "project_report";
+
+ public static final String STATUS_DRAFT = "draft";
+ public static final String STATUS_PENDING_APPROVAL = "pending_approval";
+ public static final String STATUS_APPROVED = "approved";
+ public static final String STATUS_REJECTED = "rejected";
+
+ public static final String ACTION_CREATE = "create";
+ public static final String ACTION_UPDATE = "update";
+ public static final String ACTION_DELETE = "delete";
+ public static final String ACTION_SUBMIT = "submit";
+ public static final String ACTION_RESUBMIT = "resubmit";
+ public static final String ACTION_APPROVE = "approve";
+ public static final String ACTION_REJECT = "reject";
+
+ public static final String PERMISSION_QUERY = "project:work-report:query";
+ public static final String PERMISSION_CREATE = "project:work-report:create";
+ public static final String PERMISSION_UPDATE = "project:work-report:update";
+ public static final String PERMISSION_DELETE = "project:work-report:delete";
+ public static final String PERMISSION_APPROVE = "project:work-report:approve";
+ public static final String PERMISSION_EXPORT = "project:work-report:export";
+ public static final String PERMISSION_PROJECT_OWNER = "project:work-report:project-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 a2557b5..19dc43a 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
@@ -5,6 +5,7 @@ import com.njcn.rdms.framework.common.pojo.CommonResult;
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.OvertimeApplicationExportVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO;
@@ -53,7 +54,7 @@ public class OvertimeApplicationController {
}
@PutMapping("/{id}")
- @Operation(summary = "退回或撤销后修改并重新提交加班申请")
+ @Operation(summary = "退回后修改并重新提交加班申请")
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_UPDATE + "')")
public CommonResult resubmit(@PathVariable("id") Long id,
@Valid @RequestBody OvertimeApplicationSaveReqVO reqVO) {
@@ -107,17 +108,8 @@ public class OvertimeApplicationController {
return success(true);
}
- @PostMapping("/{id}/cancel")
- @Operation(summary = "撤销加班申请")
- @PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_UPDATE + "')")
- public CommonResult cancel(@PathVariable("id") Long id,
- @Valid @RequestBody OvertimeApplicationStatusActionReqVO reqVO) {
- overtimeApplicationService.cancel(id, reqVO);
- return success(true);
- }
-
@DeleteMapping("/{id}")
- @Operation(summary = "删除已撤销的加班申请")
+ @Operation(summary = "删除已退回的加班申请")
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_DELETE + "')")
public CommonResult delete(@PathVariable("id") Long id) {
overtimeApplicationService.deleteApplication(id);
@@ -131,6 +123,13 @@ public class OvertimeApplicationController {
return success(overtimeApplicationService.getStatusLogs(id));
}
+ @GetMapping("/{id}/approval-records")
+ @Operation(summary = "获取加班申请审核记录")
+ @PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_QUERY + "')")
+ public CommonResult> approvalRecords(@PathVariable("id") Long id) {
+ return success(overtimeApplicationService.getApprovalRecords(id));
+ }
+
@GetMapping("/export")
@Operation(summary = "导出我的加班申请")
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_EXPORT + "')")
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationApprovalRecordRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationApprovalRecordRespVO.java
new file mode 100644
index 0000000..7b8e20e
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationApprovalRecordRespVO.java
@@ -0,0 +1,29 @@
+package com.njcn.rdms.module.project.controller.admin.overtime.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 加班申请审核记录 Response VO")
+@Data
+public class OvertimeApplicationApprovalRecordRespVO {
+
+ private Long id;
+
+ private Long overtimeApplicationId;
+
+ private Long statusLogId;
+
+ private Integer approvalRound;
+
+ private String conclusion;
+
+ private String opinion;
+
+ private Long auditorUserId;
+
+ private String auditorName;
+
+ private LocalDateTime createTime;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationStatusActionReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationStatusActionReqVO.java
index f81b2e1..9d1a706 100644
--- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationStatusActionReqVO.java
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/overtime/vo/OvertimeApplicationStatusActionReqVO.java
@@ -8,7 +8,7 @@ import lombok.Data;
@Data
public class OvertimeApplicationStatusActionReqVO {
- @Schema(description = "动作原因或审核意见。是否必填以状态机配置为准;当前退回必填,撤销选填",
+ @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/workreport/common/WorkReportExportResponseUtils.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/WorkReportExportResponseUtils.java
new file mode 100644
index 0000000..bc90347
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/WorkReportExportResponseUtils.java
@@ -0,0 +1,20 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common;
+
+import com.njcn.rdms.framework.common.util.http.HttpUtils;
+import com.njcn.rdms.module.project.service.workreport.export.WorkReportExportFile;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+public final class WorkReportExportResponseUtils {
+
+ private WorkReportExportResponseUtils() {
+ }
+
+ public static void write(HttpServletResponse response, WorkReportExportFile file) throws IOException {
+ response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(file.filename()));
+ response.setContentType(file.contentType());
+ response.setContentLength(file.content().length);
+ response.getOutputStream().write(file.content());
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/WorkReportStatusController.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/WorkReportStatusController.java
new file mode 100644
index 0000000..287795b
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/WorkReportStatusController.java
@@ -0,0 +1,35 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common;
+
+import com.njcn.rdms.framework.common.pojo.CommonResult;
+import com.njcn.rdms.module.project.constant.WorkReportConstants;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusDictRespVO;
+import com.njcn.rdms.module.project.service.workreport.common.WorkReportStatusService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 工作报告状态")
+@RestController
+@RequestMapping("/project/work-reports")
+@Validated
+public class WorkReportStatusController {
+
+ @Resource
+ private WorkReportStatusService workReportStatusService;
+
+ @GetMapping("/status/dict")
+ @Operation(summary = "获取工作报告所有状态字典")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult> getStatusDict() {
+ return success(workReportStatusService.getStatusDict());
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportPlanItemReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportPlanItemReqVO.java
new file mode 100644
index 0000000..19c07ff
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportPlanItemReqVO.java
@@ -0,0 +1,19 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 个人报告计划项 Request VO")
+@Data
+public class PersonalReportPlanItemReqVO {
+
+ private Integer itemNumber;
+
+ private String itemTitle;
+
+ private String targetText;
+
+ private Object targetJson;
+
+ private String supportNeed;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportPlanItemRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportPlanItemRespVO.java
new file mode 100644
index 0000000..5916d65
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportPlanItemRespVO.java
@@ -0,0 +1,21 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 个人报告计划项 Response VO")
+@Data
+public class PersonalReportPlanItemRespVO {
+
+ private Long id;
+
+ private Integer itemNumber;
+
+ private String itemTitle;
+
+ private String targetText;
+
+ private Object targetJson;
+
+ private String supportNeed;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportReviewItemReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportReviewItemReqVO.java
new file mode 100644
index 0000000..ed395d1
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportReviewItemReqVO.java
@@ -0,0 +1,23 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 个人报告回顾项 Request VO")
+@Data
+public class PersonalReportReviewItemReqVO {
+
+ private Integer itemNumber;
+
+ private String itemTitle;
+
+ private BigDecimal workHours;
+
+ private String contentText;
+
+ private Object contentJson;
+
+ private String reflectionText;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportReviewItemRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportReviewItemRespVO.java
new file mode 100644
index 0000000..ec8bb85
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/PersonalReportReviewItemRespVO.java
@@ -0,0 +1,25 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 个人报告回顾项 Response VO")
+@Data
+public class PersonalReportReviewItemRespVO {
+
+ private Long id;
+
+ private Integer itemNumber;
+
+ private String itemTitle;
+
+ private BigDecimal workHours;
+
+ private String contentText;
+
+ private Object contentJson;
+
+ private String reflectionText;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportApprovalRecordRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportApprovalRecordRespVO.java
new file mode 100644
index 0000000..59fb02d
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportApprovalRecordRespVO.java
@@ -0,0 +1,27 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 工作报告审核记录 Response VO")
+@Data
+public class WorkReportApprovalRecordRespVO {
+
+ private Long id;
+
+ private Long statusLogId;
+
+ private Integer approvalRound;
+
+ private String conclusion;
+
+ private String opinion;
+
+ private Long auditorUserId;
+
+ private String auditorName;
+
+ private LocalDateTime createTime;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportBasePageReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportBasePageReqVO.java
new file mode 100644
index 0000000..0c1aba8
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportBasePageReqVO.java
@@ -0,0 +1,32 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
+
+import com.njcn.rdms.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WorkReportBasePageReqVO extends PageParam {
+
+ @Schema(description = "关键字")
+ private String keyword;
+
+ @Schema(description = "状态编码")
+ private String statusCode;
+
+ @Schema(description = "周期开始日期范围")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate[] periodStartDate;
+
+ @Schema(description = "提交时间范围")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] submitTime;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportMemberSnapshotRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportMemberSnapshotRespVO.java
new file mode 100644
index 0000000..f734cf8
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportMemberSnapshotRespVO.java
@@ -0,0 +1,13 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 项目成员快照 Response VO")
+@Data
+public class WorkReportMemberSnapshotRespVO {
+
+ private Long userId;
+
+ private String userName;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusActionReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusActionReqVO.java
new file mode 100644
index 0000000..3160326
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusActionReqVO.java
@@ -0,0 +1,14 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 工作报告状态动作 Request VO")
+@Data
+public class WorkReportStatusActionReqVO {
+
+ @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/workreport/common/vo/WorkReportStatusDictRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusDictRespVO.java
new file mode 100644
index 0000000..72ea1ba
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusDictRespVO.java
@@ -0,0 +1,27 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 工作报告状态字典 Response VO")
+@Data
+public class WorkReportStatusDictRespVO {
+
+ @Schema(description = "状态编码", example = "draft")
+ private String statusCode;
+
+ @Schema(description = "状态名称", example = "待提交")
+ private String statusName;
+
+ @Schema(description = "排序", example = "10")
+ private Integer sort;
+
+ @Schema(description = "是否初始态")
+ private Boolean initialFlag;
+
+ @Schema(description = "是否终态")
+ private Boolean terminalFlag;
+
+ @Schema(description = "是否允许编辑")
+ private Boolean allowEdit;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusLogRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusLogRespVO.java
new file mode 100644
index 0000000..bab9053
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/common/vo/WorkReportStatusLogRespVO.java
@@ -0,0 +1,35 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.common.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 工作报告状态日志 Response VO")
+@Data
+public class WorkReportStatusLogRespVO {
+
+ private Long id;
+
+ private String reportType;
+
+ private Long reportId;
+
+ private String actionType;
+
+ private String fromStatus;
+
+ private String toStatus;
+
+ private String reason;
+
+ private Long operatorUserId;
+
+ private String operatorName;
+
+ private String periodLabelSnapshot;
+
+ private String remark;
+
+ private LocalDateTime createTime;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/MonthlyReportController.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/MonthlyReportController.java
new file mode 100644
index 0000000..4acf74a
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/MonthlyReportController.java
@@ -0,0 +1,172 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.monthly;
+
+import com.njcn.rdms.framework.apilog.core.annotation.ApiAccessLog;
+import com.njcn.rdms.framework.common.pojo.CommonResult;
+import com.njcn.rdms.framework.common.pojo.PageResult;
+import com.njcn.rdms.framework.excel.core.util.ExcelUtils;
+import com.njcn.rdms.module.project.constant.WorkReportConstants;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.WorkReportExportResponseUtils;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusActionReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusLogRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportApprovalRecordRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportApproveReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportContentExportReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportDefaultDraftReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportExportVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportPageReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportSaveReqVO;
+import com.njcn.rdms.module.project.service.workreport.export.WorkReportContentExportService;
+import com.njcn.rdms.module.project.service.workreport.monthly.MonthlyReportService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.util.List;
+
+import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 个人月报")
+@RestController
+@RequestMapping("/project/work-reports/monthly")
+@Validated
+public class MonthlyReportController {
+
+ @Resource
+ private MonthlyReportService monthlyReportService;
+ @Resource
+ private WorkReportContentExportService workReportContentExportService;
+
+ @GetMapping("/init")
+ @Operation(summary = "获取月报新建默认数据")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
+ public CommonResult initMonthlyReport() {
+ return success(monthlyReportService.initMonthlyReport());
+ }
+
+ @GetMapping("/default-draft")
+ @Operation(summary = "预览月报默认稿")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
+ public CommonResult previewMonthlyDefaultDraft(@Valid MonthlyReportDefaultDraftReqVO reqVO) {
+ return success(monthlyReportService.previewMonthlyDefaultDraft(reqVO));
+ }
+
+ @PostMapping
+ @Operation(summary = "新建月报草稿")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
+ public CommonResult createMonthlyReport(@Valid @RequestBody MonthlyReportSaveReqVO reqVO) {
+ return success(monthlyReportService.createMonthlyReport(reqVO));
+ }
+
+ @PutMapping("/{id}")
+ @Operation(summary = "修改月报草稿")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_UPDATE + "')")
+ public CommonResult updateMonthlyReport(@PathVariable("id") Long id,
+ @Valid @RequestBody MonthlyReportSaveReqVO reqVO) {
+ monthlyReportService.updateMonthlyReport(id, reqVO);
+ return success(true);
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "获取月报详情")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult getMonthlyReport(@PathVariable("id") Long id) {
+ return success(monthlyReportService.getMonthlyReport(id));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获取我的月报分页")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult> getMonthlyReportPage(@Valid MonthlyReportPageReqVO reqVO) {
+ return success(monthlyReportService.getMonthlyReportPage(reqVO));
+ }
+
+ @GetMapping("/approval-page")
+ @Operation(summary = "获取待我审批的月报分页")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
+ public CommonResult> getMonthlyApprovalPage(@Valid MonthlyReportPageReqVO reqVO) {
+ return success(monthlyReportService.getMonthlyApprovalPage(reqVO));
+ }
+
+ @PostMapping("/{id}/submit")
+ @Operation(summary = "提交月报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_UPDATE + "')")
+ public CommonResult submitMonthlyReport(@PathVariable("id") Long id) {
+ monthlyReportService.submitMonthlyReport(id);
+ return success(true);
+ }
+
+ @PostMapping("/{id}/approve")
+ @Operation(summary = "审批通过月报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
+ public CommonResult approveMonthlyReport(@PathVariable("id") Long id,
+ @Valid @RequestBody MonthlyReportApproveReqVO reqVO) {
+ monthlyReportService.approveMonthlyReport(id, reqVO);
+ return success(true);
+ }
+
+ @PostMapping("/{id}/reject")
+ @Operation(summary = "退回月报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
+ public CommonResult rejectMonthlyReport(@PathVariable("id") Long id,
+ @Valid @RequestBody WorkReportStatusActionReqVO reqVO) {
+ monthlyReportService.rejectMonthlyReport(id, reqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "删除月报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_DELETE + "')")
+ public CommonResult deleteMonthlyReport(@PathVariable("id") Long id) {
+ monthlyReportService.deleteMonthlyReport(id);
+ return success(true);
+ }
+
+ @GetMapping("/{id}/status-logs")
+ @Operation(summary = "获取月报状态日志")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult> getMonthlyStatusLogs(@PathVariable("id") Long id) {
+ return success(monthlyReportService.getMonthlyStatusLogs(id));
+ }
+
+ @GetMapping("/{id}/approval-records")
+ @Operation(summary = "获取月报审批记录")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult> getMonthlyApprovalRecords(@PathVariable("id") Long id) {
+ return success(monthlyReportService.getMonthlyApprovalRecords(id));
+ }
+
+ @GetMapping("/export")
+ @Operation(summary = "导出我的月报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportMonthlyReport(@Valid MonthlyReportPageReqVO reqVO, HttpServletResponse response)
+ throws IOException {
+ ExcelUtils.write(response, "个人月报_" + LocalDate.now() + ".xls", "个人月报",
+ MonthlyReportExportVO.class, monthlyReportService.getMonthlyExportList(reqVO));
+ }
+
+ @PostMapping("/content-export")
+ @Operation(summary = "导出月报内容 Word")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportMonthlyReportContent(@Valid @RequestBody MonthlyReportContentExportReqVO reqVO,
+ HttpServletResponse response) throws IOException {
+ WorkReportExportResponseUtils.write(response, workReportContentExportService.exportMonthly(reqVO));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportApprovalRecordRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportApprovalRecordRespVO.java
new file mode 100644
index 0000000..2bc7682
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportApprovalRecordRespVO.java
@@ -0,0 +1,50 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 月报审核记录 Response VO")
+@Data
+public class MonthlyReportApprovalRecordRespVO {
+
+ private Long id;
+
+ private Long statusLogId;
+
+ private Integer approvalRound;
+
+ private String conclusion;
+
+ private String opinion;
+
+ private LocalDate meetingDate;
+
+ private String strengthDesc;
+
+ private String strengthExample;
+
+ private String weaknessDesc;
+
+ private String weaknessExample;
+
+ private String improvementSuggestion;
+
+ private String performanceResult;
+
+ private String employeeSignName;
+
+ private LocalDate employeeSignedDate;
+
+ private String supervisorSignName;
+
+ private LocalDate supervisorSignedDate;
+
+ private Long auditorUserId;
+
+ private String auditorName;
+
+ private LocalDateTime createTime;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportApproveReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportApproveReqVO.java
new file mode 100644
index 0000000..eb2791d
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportApproveReqVO.java
@@ -0,0 +1,57 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
+
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusActionReqVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "管理后台 - 月报审核通过 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MonthlyReportApproveReqVO extends WorkReportStatusActionReqVO {
+
+ @Schema(description = "面谈时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ @NotNull
+ private LocalDate meetingDate;
+
+ @Schema(description = "优势描述")
+ private String strengthDesc;
+
+ @Schema(description = "优势行为事例")
+ private String strengthExample;
+
+ @Schema(description = "劣势描述")
+ private String weaknessDesc;
+
+ @Schema(description = "劣势行为事例")
+ private String weaknessExample;
+
+ @Schema(description = "改进建议")
+ private String improvementSuggestion;
+
+ @Schema(description = "绩效考核结果")
+ @NotBlank(message = "绩效考核结果不能为空")
+ private String performanceResult;
+
+ @Schema(description = "被考核人签名")
+ private String employeeSignName;
+
+ @Schema(description = "被考核人签字日期")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate employeeSignedDate;
+
+ @Schema(description = "上级签名")
+ private String supervisorSignName;
+
+ @Schema(description = "上级签字日期")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate supervisorSignedDate;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportContentExportReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportContentExportReqVO.java
new file mode 100644
index 0000000..d90c08e
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportContentExportReqVO.java
@@ -0,0 +1,19 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Schema(description = "管理后台 - 月报内容导出 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MonthlyReportContentExportReqVO extends MonthlyReportPageReqVO {
+
+ @Schema(description = "是否按当前搜索条件全量导出")
+ private Boolean exportAll;
+
+ @Schema(description = "选中的月报编号列表;exportAll=false 时必填")
+ private List ids;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportDefaultDraftReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportDefaultDraftReqVO.java
new file mode 100644
index 0000000..2ceb835
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportDefaultDraftReqVO.java
@@ -0,0 +1,30 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "管理后台 - 月报默认稿预览 Request VO")
+@Data
+public class MonthlyReportDefaultDraftReqVO {
+
+ @NotBlank(message = "周期编码不能为空")
+ private String periodKey;
+
+ @NotBlank(message = "周期名称不能为空")
+ private String periodLabel;
+
+ @NotNull(message = "周期开始日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodStartDate;
+
+ @NotNull(message = "周期结束日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodEndDate;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportExportVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportExportVO.java
new file mode 100644
index 0000000..82818a9
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportExportVO.java
@@ -0,0 +1,44 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@ExcelIgnoreUnannotated
+public class MonthlyReportExportVO {
+
+ @ExcelProperty("填报人")
+ private String reporterName;
+
+ @ExcelProperty("直属上级")
+ private String supervisorName;
+
+ @ExcelProperty("周期")
+ private String periodLabel;
+
+ @ExcelProperty("开始日期")
+ private LocalDate periodStartDate;
+
+ @ExcelProperty("结束日期")
+ private LocalDate periodEndDate;
+
+ @ExcelProperty("工时")
+ private BigDecimal totalWorkHours;
+
+ @ExcelProperty("状态")
+ private String statusName;
+
+ @ExcelProperty("审核意见")
+ private String approvalComment;
+
+ @ExcelProperty("提交时间")
+ private LocalDateTime submitTime;
+
+ @ExcelProperty("审核时间")
+ private LocalDateTime approvalTime;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportPageReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportPageReqVO.java
new file mode 100644
index 0000000..d50e782
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportPageReqVO.java
@@ -0,0 +1,12 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
+
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportBasePageReqVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Schema(description = "管理后台 - 月报分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MonthlyReportPageReqVO extends WorkReportBasePageReqVO {
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportRespVO.java
new file mode 100644
index 0000000..a2afa01
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportRespVO.java
@@ -0,0 +1,41 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
+
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportPlanItemRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportReviewItemRespVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - 月报 Response VO")
+@Data
+public class MonthlyReportRespVO {
+
+ private Long id;
+ private Long reporterId;
+ private String reporterName;
+ private String reporterDeptName;
+ private String reporterPostName;
+ private Long supervisorUserId;
+ private String supervisorName;
+ private String periodKey;
+ private String periodLabel;
+ private LocalDate periodStartDate;
+ private LocalDate periodEndDate;
+ private String statusCode;
+ private String statusName;
+ private Boolean allowEdit;
+ private Boolean terminal;
+ private BigDecimal totalWorkHours;
+ private String approvalComment;
+ private String lastStatusReason;
+ private LocalDateTime submitTime;
+ private LocalDateTime approvalTime;
+ private LocalDateTime createTime;
+ private LocalDateTime updateTime;
+ private List reviewItems;
+ private List planItems;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportSaveReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportSaveReqVO.java
new file mode 100644
index 0000000..fb925fd
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/monthly/vo/MonthlyReportSaveReqVO.java
@@ -0,0 +1,40 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo;
+
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportPlanItemReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportReviewItemReqVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "管理后台 - 月报保存 Request VO")
+@Data
+public class MonthlyReportSaveReqVO {
+
+ @NotBlank(message = "周期编码不能为空")
+ private String periodKey;
+
+ @NotBlank(message = "周期名称不能为空")
+ private String periodLabel;
+
+ @NotNull(message = "周期开始日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodStartDate;
+
+ @NotNull(message = "周期结束日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodEndDate;
+
+ @Valid
+ private List reviewItems;
+
+ @Valid
+ private List planItems;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/ProjectReportController.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/ProjectReportController.java
new file mode 100644
index 0000000..f4ddc65
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/ProjectReportController.java
@@ -0,0 +1,181 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.project;
+
+import com.njcn.rdms.framework.apilog.core.annotation.ApiAccessLog;
+import com.njcn.rdms.framework.common.pojo.CommonResult;
+import com.njcn.rdms.framework.common.pojo.PageResult;
+import com.njcn.rdms.framework.excel.core.util.ExcelUtils;
+import com.njcn.rdms.module.project.constant.WorkReportConstants;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.WorkReportExportResponseUtils;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportApprovalRecordRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusActionReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusLogRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportContentExportReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportDefaultDraftReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportExportVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportOwnerProjectOptionRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportPageReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportSaveReqVO;
+import com.njcn.rdms.module.project.service.workreport.export.WorkReportContentExportService;
+import com.njcn.rdms.module.project.service.workreport.project.ProjectReportService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.util.List;
+
+import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 项目半月报")
+@RestController
+@RequestMapping("/project/work-reports/project")
+@Validated
+public class ProjectReportController {
+
+ @Resource
+ private ProjectReportService projectReportService;
+ @Resource
+ private WorkReportContentExportService workReportContentExportService;
+
+ @GetMapping("/owner-project-options")
+ @Operation(summary = "获取项目半月报负责人可选项目列表")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_PROJECT_OWNER + "')")
+ public CommonResult> getOwnerProjectOptions() {
+ return success(projectReportService.getOwnerProjectOptions());
+ }
+
+ @GetMapping("/init")
+ @Operation(summary = "获取项目半月报新建默认数据")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_PROJECT_OWNER + "')")
+ public CommonResult initProjectReport(@RequestParam("projectId") Long projectId) {
+ return success(projectReportService.initProjectReport(projectId));
+ }
+
+ @GetMapping("/{projectId}/default-draft")
+ @Operation(summary = "预览项目半月报默认稿")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_PROJECT_OWNER + "')")
+ public CommonResult previewProjectDefaultDraft(@PathVariable("projectId") Long projectId,
+ @Valid ProjectReportDefaultDraftReqVO reqVO) {
+ return success(projectReportService.previewProjectDefaultDraft(projectId, reqVO));
+ }
+
+ @PostMapping
+ @Operation(summary = "新建项目半月报草稿")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_PROJECT_OWNER + "')")
+ public CommonResult createProjectReport(@Valid @RequestBody ProjectReportSaveReqVO reqVO) {
+ return success(projectReportService.createProjectReport(reqVO));
+ }
+
+ @PutMapping("/{id}")
+ @Operation(summary = "修改项目半月报草稿")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_PROJECT_OWNER + "')")
+ public CommonResult updateProjectReport(@PathVariable("id") Long id,
+ @Valid @RequestBody ProjectReportSaveReqVO reqVO) {
+ projectReportService.updateProjectReport(id, reqVO);
+ return success(true);
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "获取项目半月报详情")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult getProjectReport(@PathVariable("id") Long id) {
+ return success(projectReportService.getProjectReport(id));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获取我的项目半月报分页")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult> getProjectReportPage(@Valid ProjectReportPageReqVO reqVO) {
+ return success(projectReportService.getProjectReportPage(reqVO));
+ }
+
+ @GetMapping("/approval-page")
+ @Operation(summary = "获取待我审批的项目半月报分页")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
+ public CommonResult> getProjectApprovalPage(@Valid ProjectReportPageReqVO reqVO) {
+ return success(projectReportService.getProjectApprovalPage(reqVO));
+ }
+
+ @PostMapping("/{id}/submit")
+ @Operation(summary = "提交项目半月报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_UPDATE + "')")
+ public CommonResult submitProjectReport(@PathVariable("id") Long id) {
+ projectReportService.submitProjectReport(id);
+ return success(true);
+ }
+
+ @PostMapping("/{id}/approve")
+ @Operation(summary = "审批通过项目半月报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
+ public CommonResult approveProjectReport(@PathVariable("id") Long id,
+ @Valid @RequestBody WorkReportStatusActionReqVO reqVO) {
+ projectReportService.approveProjectReport(id, reqVO);
+ return success(true);
+ }
+
+ @PostMapping("/{id}/reject")
+ @Operation(summary = "退回项目半月报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
+ public CommonResult rejectProjectReport(@PathVariable("id") Long id,
+ @Valid @RequestBody WorkReportStatusActionReqVO reqVO) {
+ projectReportService.rejectProjectReport(id, reqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "删除项目半月报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_DELETE + "')")
+ public CommonResult deleteProjectReport(@PathVariable("id") Long id) {
+ projectReportService.deleteProjectReport(id);
+ return success(true);
+ }
+
+ @GetMapping("/{id}/status-logs")
+ @Operation(summary = "获取项目半月报状态日志")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult> getProjectStatusLogs(@PathVariable("id") Long id) {
+ return success(projectReportService.getProjectStatusLogs(id));
+ }
+
+ @GetMapping("/{id}/approval-records")
+ @Operation(summary = "获取项目半月报审批记录")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult> getProjectApprovalRecords(@PathVariable("id") Long id) {
+ return success(projectReportService.getProjectApprovalRecords(id));
+ }
+
+ @GetMapping("/export")
+ @Operation(summary = "导出我的项目半月报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportProjectReport(@Valid ProjectReportPageReqVO reqVO, HttpServletResponse response)
+ throws IOException {
+ ExcelUtils.write(response, "项目半月报_" + LocalDate.now() + ".xls", "项目半月报",
+ ProjectReportExportVO.class, projectReportService.getProjectExportList(reqVO));
+ }
+
+ @PostMapping("/content-export")
+ @Operation(summary = "导出项目半月报内容 Word")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportProjectReportContent(@Valid @RequestBody ProjectReportContentExportReqVO reqVO,
+ HttpServletResponse response) throws IOException {
+ WorkReportExportResponseUtils.write(response, workReportContentExportService.exportProject(reqVO));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportContentExportReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportContentExportReqVO.java
new file mode 100644
index 0000000..29551ca
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportContentExportReqVO.java
@@ -0,0 +1,19 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Schema(description = "管理后台 - 项目半月报内容导出 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ProjectReportContentExportReqVO extends ProjectReportPageReqVO {
+
+ @Schema(description = "是否按当前搜索条件全量导出")
+ private Boolean exportAll;
+
+ @Schema(description = "选中的项目半月报编号列表;exportAll=false 时必填")
+ private List ids;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportDefaultDraftReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportDefaultDraftReqVO.java
new file mode 100644
index 0000000..4bcf25c
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportDefaultDraftReqVO.java
@@ -0,0 +1,33 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "管理后台 - 项目半月报默认稿预览 Request VO")
+@Data
+public class ProjectReportDefaultDraftReqVO {
+
+ @NotBlank(message = "周期编码不能为空")
+ private String periodKey;
+
+ @NotBlank(message = "周期名称不能为空")
+ private String periodLabel;
+
+ @NotNull(message = "周期开始日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodStartDate;
+
+ @NotNull(message = "周期结束日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodEndDate;
+
+ @NotNull(message = "上半月/下半月标记不能为空")
+ private Integer flag;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportExportVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportExportVO.java
new file mode 100644
index 0000000..8fe43d6
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportExportVO.java
@@ -0,0 +1,47 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@ExcelIgnoreUnannotated
+public class ProjectReportExportVO {
+
+ @ExcelProperty("项目名称")
+ private String projectName;
+
+ @ExcelProperty("填报人")
+ private String projectOwnerName;
+
+ @ExcelProperty("直属上级")
+ private String supervisorName;
+
+ @ExcelProperty("周期")
+ private String periodLabel;
+
+ @ExcelProperty("开始日期")
+ private LocalDate periodStartDate;
+
+ @ExcelProperty("结束日期")
+ private LocalDate periodEndDate;
+
+ @ExcelProperty("工时")
+ private BigDecimal totalWorkHours;
+
+ @ExcelProperty("状态")
+ private String statusName;
+
+ @ExcelProperty("审核意见")
+ private String approvalComment;
+
+ @ExcelProperty("提交时间")
+ private LocalDateTime submitTime;
+
+ @ExcelProperty("审核时间")
+ private LocalDateTime approvalTime;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportItemReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportItemReqVO.java
new file mode 100644
index 0000000..69155d0
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportItemReqVO.java
@@ -0,0 +1,19 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 项目半月报工作项 Request VO")
+@Data
+public class ProjectReportItemReqVO {
+
+ private String itemTitle;
+
+ private BigDecimal workHours;
+
+ private String priorityCode;
+
+ private BigDecimal progressRate;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportItemRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportItemRespVO.java
new file mode 100644
index 0000000..8beb64d
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportItemRespVO.java
@@ -0,0 +1,21 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 项目半月报工作项 Response VO")
+@Data
+public class ProjectReportItemRespVO {
+
+ private Long id;
+
+ private String itemTitle;
+
+ private BigDecimal workHours;
+
+ private String priorityCode;
+
+ private BigDecimal progressRate;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportOwnerProjectOptionRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportOwnerProjectOptionRespVO.java
new file mode 100644
index 0000000..70fdf3e
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportOwnerProjectOptionRespVO.java
@@ -0,0 +1,18 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 项目半月报负责人可选项目 Response VO")
+@Data
+public class ProjectReportOwnerProjectOptionRespVO {
+
+ @Schema(description = "项目编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @Schema(description = "项目编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "CNPJ2026001")
+ private String projectCode;
+
+ @Schema(description = "项目名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "客户交付项目")
+ private String projectName;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportPageReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportPageReqVO.java
new file mode 100644
index 0000000..134fdfc
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportPageReqVO.java
@@ -0,0 +1,18 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
+
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportBasePageReqVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Schema(description = "管理后台 - 项目半月报分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ProjectReportPageReqVO extends WorkReportBasePageReqVO {
+
+ @Schema(description = "项目编号")
+ private Long projectId;
+
+ @Schema(description = "上半月/下半月标记")
+ private Integer flag;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportRespVO.java
new file mode 100644
index 0000000..d99882e
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportRespVO.java
@@ -0,0 +1,47 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
+
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportMemberSnapshotRespVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - 项目半月报 Response VO")
+@Data
+public class ProjectReportRespVO {
+
+ private Long id;
+ private Long projectId;
+ private String projectName;
+ private Long projectOwnerId;
+ private String projectOwnerName;
+ private String technicalOwnerName;
+ private List projectMemberSnapshot;
+ private Long supervisorUserId;
+ private String supervisorName;
+ private String periodKey;
+ private String periodLabel;
+ private LocalDate periodStartDate;
+ private LocalDate periodEndDate;
+ private Integer flag;
+ private String statusCode;
+ private String statusName;
+ private Boolean allowEdit;
+ private Boolean terminal;
+ private String projectStatusDesc;
+ private String projectProgressPlan;
+ private String projectKeyPoints;
+ private String projectProblems;
+ private BigDecimal totalWorkHours;
+ private String approvalComment;
+ private String lastStatusReason;
+ private LocalDateTime submitTime;
+ private LocalDateTime approvalTime;
+ private LocalDateTime createTime;
+ private LocalDateTime updateTime;
+ private List currentItems;
+ private List nextItems;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportSaveReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportSaveReqVO.java
new file mode 100644
index 0000000..55608f3
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/project/vo/ProjectReportSaveReqVO.java
@@ -0,0 +1,52 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.project.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "管理后台 - 项目半月报保存 Request VO")
+@Data
+public class ProjectReportSaveReqVO {
+
+ @NotNull(message = "项目编号不能为空")
+ private Long projectId;
+
+ @NotBlank(message = "周期编码不能为空")
+ private String periodKey;
+
+ @NotBlank(message = "周期名称不能为空")
+ private String periodLabel;
+
+ @NotNull(message = "周期开始日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodStartDate;
+
+ @NotNull(message = "周期结束日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodEndDate;
+
+ @NotNull(message = "上半月/下半月标记不能为空")
+ private Integer flag;
+
+ private String projectStatusDesc;
+
+ private String projectProgressPlan;
+
+ private String projectKeyPoints;
+
+ private String projectProblems;
+
+ @Valid
+ private List currentItems;
+
+ @Valid
+ private List nextItems;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/WeeklyReportController.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/WeeklyReportController.java
new file mode 100644
index 0000000..78d4442
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/WeeklyReportController.java
@@ -0,0 +1,171 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.weekly;
+
+import com.njcn.rdms.framework.apilog.core.annotation.ApiAccessLog;
+import com.njcn.rdms.framework.common.pojo.CommonResult;
+import com.njcn.rdms.framework.common.pojo.PageResult;
+import com.njcn.rdms.framework.excel.core.util.ExcelUtils;
+import com.njcn.rdms.module.project.constant.WorkReportConstants;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.WorkReportExportResponseUtils;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportApprovalRecordRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusActionReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportStatusLogRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportContentExportReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportDefaultDraftReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportExportVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportPageReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportSaveReqVO;
+import com.njcn.rdms.module.project.service.workreport.export.WorkReportContentExportService;
+import com.njcn.rdms.module.project.service.workreport.weekly.WeeklyReportService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.util.List;
+
+import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 个人周报")
+@RestController
+@RequestMapping("/project/work-reports/weekly")
+@Validated
+public class WeeklyReportController {
+
+ @Resource
+ private WeeklyReportService weeklyReportService;
+ @Resource
+ private WorkReportContentExportService workReportContentExportService;
+
+ @GetMapping("/init")
+ @Operation(summary = "获取周报新建默认数据")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
+ public CommonResult initWeeklyReport() {
+ return success(weeklyReportService.initWeeklyReport());
+ }
+
+ @GetMapping("/default-draft")
+ @Operation(summary = "预览周报默认稿")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
+ public CommonResult previewWeeklyDefaultDraft(@Valid WeeklyReportDefaultDraftReqVO reqVO) {
+ return success(weeklyReportService.previewWeeklyDefaultDraft(reqVO));
+ }
+
+ @PostMapping
+ @Operation(summary = "新建周报草稿")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_CREATE + "')")
+ public CommonResult createWeeklyReport(@Valid @RequestBody WeeklyReportSaveReqVO reqVO) {
+ return success(weeklyReportService.createWeeklyReport(reqVO));
+ }
+
+ @PutMapping("/{id}")
+ @Operation(summary = "修改周报草稿")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_UPDATE + "')")
+ public CommonResult updateWeeklyReport(@PathVariable("id") Long id,
+ @Valid @RequestBody WeeklyReportSaveReqVO reqVO) {
+ weeklyReportService.updateWeeklyReport(id, reqVO);
+ return success(true);
+ }
+
+ @GetMapping("/{id}")
+ @Operation(summary = "获取周报详情")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult getWeeklyReport(@PathVariable("id") Long id) {
+ return success(weeklyReportService.getWeeklyReport(id));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获取我的周报分页")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult> getWeeklyReportPage(@Valid WeeklyReportPageReqVO reqVO) {
+ return success(weeklyReportService.getWeeklyReportPage(reqVO));
+ }
+
+ @GetMapping("/approval-page")
+ @Operation(summary = "获取待我审批的周报分页")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
+ public CommonResult> getWeeklyApprovalPage(@Valid WeeklyReportPageReqVO reqVO) {
+ return success(weeklyReportService.getWeeklyApprovalPage(reqVO));
+ }
+
+ @PostMapping("/{id}/submit")
+ @Operation(summary = "提交周报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_UPDATE + "')")
+ public CommonResult submitWeeklyReport(@PathVariable("id") Long id) {
+ weeklyReportService.submitWeeklyReport(id);
+ return success(true);
+ }
+
+ @PostMapping("/{id}/approve")
+ @Operation(summary = "审批通过周报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
+ public CommonResult approveWeeklyReport(@PathVariable("id") Long id,
+ @Valid @RequestBody WorkReportStatusActionReqVO reqVO) {
+ weeklyReportService.approveWeeklyReport(id, reqVO);
+ return success(true);
+ }
+
+ @PostMapping("/{id}/reject")
+ @Operation(summary = "退回周报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_APPROVE + "')")
+ public CommonResult rejectWeeklyReport(@PathVariable("id") Long id,
+ @Valid @RequestBody WorkReportStatusActionReqVO reqVO) {
+ weeklyReportService.rejectWeeklyReport(id, reqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/{id}")
+ @Operation(summary = "删除周报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_DELETE + "')")
+ public CommonResult deleteWeeklyReport(@PathVariable("id") Long id) {
+ weeklyReportService.deleteWeeklyReport(id);
+ return success(true);
+ }
+
+ @GetMapping("/{id}/status-logs")
+ @Operation(summary = "获取周报状态日志")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult> getWeeklyStatusLogs(@PathVariable("id") Long id) {
+ return success(weeklyReportService.getWeeklyStatusLogs(id));
+ }
+
+ @GetMapping("/{id}/approval-records")
+ @Operation(summary = "获取周报审批记录")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_QUERY + "')")
+ public CommonResult> getWeeklyApprovalRecords(@PathVariable("id") Long id) {
+ return success(weeklyReportService.getWeeklyApprovalRecords(id));
+ }
+
+ @GetMapping("/export")
+ @Operation(summary = "导出我的周报")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportWeeklyReport(@Valid WeeklyReportPageReqVO reqVO, HttpServletResponse response)
+ throws IOException {
+ ExcelUtils.write(response, "个人周报_" + LocalDate.now() + ".xls", "个人周报",
+ WeeklyReportExportVO.class, weeklyReportService.getWeeklyExportList(reqVO));
+ }
+
+ @PostMapping("/content-export")
+ @Operation(summary = "导出周报内容 Word")
+ @PreAuthorize("@ss.hasPermission('" + WorkReportConstants.PERMISSION_EXPORT + "')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportWeeklyReportContent(@Valid @RequestBody WeeklyReportContentExportReqVO reqVO,
+ HttpServletResponse response) throws IOException {
+ WorkReportExportResponseUtils.write(response, workReportContentExportService.exportWeekly(reqVO));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportContentExportReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportContentExportReqVO.java
new file mode 100644
index 0000000..06a736f
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportContentExportReqVO.java
@@ -0,0 +1,19 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Schema(description = "管理后台 - 周报内容导出 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WeeklyReportContentExportReqVO extends WeeklyReportPageReqVO {
+
+ @Schema(description = "是否按当前搜索条件全量导出")
+ private Boolean exportAll;
+
+ @Schema(description = "选中的周报编号列表;exportAll=false 时必填")
+ private List ids;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportDefaultDraftReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportDefaultDraftReqVO.java
new file mode 100644
index 0000000..d2f2d43
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportDefaultDraftReqVO.java
@@ -0,0 +1,30 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "管理后台 - 周报默认稿预览 Request VO")
+@Data
+public class WeeklyReportDefaultDraftReqVO {
+
+ @NotBlank(message = "周期编码不能为空")
+ private String periodKey;
+
+ @NotBlank(message = "周期名称不能为空")
+ private String periodLabel;
+
+ @NotNull(message = "周期开始日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodStartDate;
+
+ @NotNull(message = "周期结束日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodEndDate;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportExportVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportExportVO.java
new file mode 100644
index 0000000..4a5bb06
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportExportVO.java
@@ -0,0 +1,50 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@ExcelIgnoreUnannotated
+public class WeeklyReportExportVO {
+
+ @ExcelProperty("填报人")
+ private String reporterName;
+
+ @ExcelProperty("直属上级")
+ private String supervisorName;
+
+ @ExcelProperty("周期")
+ private String periodLabel;
+
+ @ExcelProperty("开始日期")
+ private LocalDate periodStartDate;
+
+ @ExcelProperty("结束日期")
+ private LocalDate periodEndDate;
+
+ @ExcelProperty("是否出差")
+ private Boolean isBusinessTrip;
+
+ @ExcelProperty("出差天数")
+ private BigDecimal totalTravelDays;
+
+ @ExcelProperty("工时")
+ private BigDecimal totalWorkHours;
+
+ @ExcelProperty("状态")
+ private String statusName;
+
+ @ExcelProperty("审核意见")
+ private String approvalComment;
+
+ @ExcelProperty("提交时间")
+ private LocalDateTime submitTime;
+
+ @ExcelProperty("审核时间")
+ private LocalDateTime approvalTime;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportPageReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportPageReqVO.java
new file mode 100644
index 0000000..24ba9aa
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportPageReqVO.java
@@ -0,0 +1,15 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
+
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.WorkReportBasePageReqVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Schema(description = "管理后台 - 周报分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WeeklyReportPageReqVO extends WorkReportBasePageReqVO {
+
+ @Schema(description = "是否出差")
+ private Boolean isBusinessTrip;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportRespVO.java
new file mode 100644
index 0000000..f734cd7
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportRespVO.java
@@ -0,0 +1,44 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
+
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportPlanItemRespVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportReviewItemRespVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "管理后台 - 周报 Response VO")
+@Data
+public class WeeklyReportRespVO {
+
+ private Long id;
+ private Long reporterId;
+ private String reporterName;
+ private String reporterDeptName;
+ private String reporterPostName;
+ private Long supervisorUserId;
+ private String supervisorName;
+ private String periodKey;
+ private String periodLabel;
+ private LocalDate periodStartDate;
+ private LocalDate periodEndDate;
+ private String statusCode;
+ private String statusName;
+ private Boolean allowEdit;
+ private Boolean terminal;
+ private Boolean isBusinessTrip;
+ private BigDecimal totalTravelDays;
+ private BigDecimal totalWorkHours;
+ private String approvalComment;
+ private String lastStatusReason;
+ private LocalDateTime submitTime;
+ private LocalDateTime approvalTime;
+ private LocalDateTime createTime;
+ private LocalDateTime updateTime;
+ private List reviewItems;
+ private List planItems;
+ private List travelSegments;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportSaveReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportSaveReqVO.java
new file mode 100644
index 0000000..8fe9a89
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportSaveReqVO.java
@@ -0,0 +1,46 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
+
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportPlanItemReqVO;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportReviewItemReqVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "管理后台 - 周报保存 Request VO")
+@Data
+public class WeeklyReportSaveReqVO {
+
+ @NotBlank(message = "周期编码不能为空")
+ private String periodKey;
+
+ @NotBlank(message = "周期名称不能为空")
+ private String periodLabel;
+
+ @NotNull(message = "周期开始日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodStartDate;
+
+ @NotNull(message = "周期结束日期不能为空")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate periodEndDate;
+
+ @NotNull(message = "是否出差不能为空")
+ private Boolean isBusinessTrip;
+
+ @Valid
+ private List reviewItems;
+
+ @Valid
+ private List planItems;
+
+ @Valid
+ private List travelSegments;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportTravelSegmentReqVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportTravelSegmentReqVO.java
new file mode 100644
index 0000000..de4b9b6
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportTravelSegmentReqVO.java
@@ -0,0 +1,27 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "管理后台 - 周报出差分段 Request VO")
+@Data
+public class WeeklyReportTravelSegmentReqVO {
+
+ private Integer sort;
+
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate startDate;
+
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+ private LocalDate endDate;
+
+ private BigDecimal travelDays;
+
+ private String location;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportTravelSegmentRespVO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportTravelSegmentRespVO.java
new file mode 100644
index 0000000..ce7438a
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/controller/admin/workreport/weekly/vo/WeeklyReportTravelSegmentRespVO.java
@@ -0,0 +1,24 @@
+package com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Schema(description = "管理后台 - 周报出差分段 Response VO")
+@Data
+public class WeeklyReportTravelSegmentRespVO {
+
+ private Long id;
+
+ private Integer sort;
+
+ private LocalDate startDate;
+
+ private LocalDate endDate;
+
+ private BigDecimal travelDays;
+
+ private String location;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/overtime/OvertimeApplicationApprovalRecordDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/overtime/OvertimeApplicationApprovalRecordDO.java
new file mode 100644
index 0000000..fc391fa
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/overtime/OvertimeApplicationApprovalRecordDO.java
@@ -0,0 +1,36 @@
+package com.njcn.rdms.module.project.dal.dataobject.overtime;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 加班申请审核记录。
+ */
+@TableName("rdms_overtime_application_approval_record")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class OvertimeApplicationApprovalRecordDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private Long overtimeApplicationId;
+
+ private Long statusLogId;
+
+ private Integer approvalRound;
+
+ private String conclusion;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String opinion;
+
+ private Long auditorUserId;
+
+ private String auditorName;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/PersonalReportPlanItemDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/PersonalReportPlanItemDO.java
new file mode 100644
index 0000000..e598594
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/PersonalReportPlanItemDO.java
@@ -0,0 +1,40 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.common;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 个人周报/月报计划项。
+ */
+@TableName(value = "rdms_work_report_personal_report_plan_item", autoResultMap = true)
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class PersonalReportPlanItemDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private String reportType;
+
+ private Long reportId;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private Integer itemNumber;
+
+ private String itemTitle;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String targetText;
+
+ @TableField(typeHandler = JacksonTypeHandler.class, updateStrategy = FieldStrategy.ALWAYS)
+ private Object targetJson;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String supportNeed;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/PersonalReportReviewItemDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/PersonalReportReviewItemDO.java
new file mode 100644
index 0000000..b0ecbe7
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/PersonalReportReviewItemDO.java
@@ -0,0 +1,45 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.common;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 个人周报/月报回顾项。
+ */
+@TableName(value = "rdms_work_report_personal_report_review_item", autoResultMap = true)
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class PersonalReportReviewItemDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private String reportType;
+
+ private Long reportId;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private Integer itemNumber;
+
+ private String itemTitle;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private BigDecimal workHours;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String contentText;
+
+ @TableField(typeHandler = JacksonTypeHandler.class, updateStrategy = FieldStrategy.ALWAYS)
+ private Object contentJson;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String reflectionText;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/WorkReportMemberSnapshotItem.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/WorkReportMemberSnapshotItem.java
new file mode 100644
index 0000000..b073eb6
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/WorkReportMemberSnapshotItem.java
@@ -0,0 +1,14 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.common;
+
+import lombok.Data;
+
+/**
+ * 项目半月报中的项目成员快照。
+ */
+@Data
+public class WorkReportMemberSnapshotItem {
+
+ private Long userId;
+
+ private String userName;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/WorkReportStatusLogDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/WorkReportStatusLogDO.java
new file mode 100644
index 0000000..65fd9fe
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/common/WorkReportStatusLogDO.java
@@ -0,0 +1,44 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.common;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 工作报告状态日志。
+ */
+@TableName("rdms_work_report_status_log")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WorkReportStatusLogDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private String reportType;
+
+ private Long reportId;
+
+ private String actionType;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String fromStatus;
+
+ private String toStatus;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String reason;
+
+ private Long operatorUserId;
+
+ private String operatorName;
+
+ private String periodLabelSnapshot;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String remark;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/monthly/MonthlyReportApprovalRecordDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/monthly/MonthlyReportApprovalRecordDO.java
new file mode 100644
index 0000000..6c953a8
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/monthly/MonthlyReportApprovalRecordDO.java
@@ -0,0 +1,71 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.monthly;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDate;
+
+/**
+ * 月报审核记录。
+ */
+@TableName("rdms_work_report_monthly_report_approval_record")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MonthlyReportApprovalRecordDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private Long monthlyReportId;
+
+ private Long statusLogId;
+
+ private Integer approvalRound;
+
+ private String conclusion;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String opinion;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private LocalDate meetingDate;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String strengthDesc;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String strengthExample;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String weaknessDesc;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String weaknessExample;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String improvementSuggestion;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String performanceResult;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String employeeSignName;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private LocalDate employeeSignedDate;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String supervisorSignName;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private LocalDate supervisorSignedDate;
+
+ private Long auditorUserId;
+
+ private String auditorName;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/monthly/MonthlyReportDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/monthly/MonthlyReportDO.java
new file mode 100644
index 0000000..6cf08c6
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/monthly/MonthlyReportDO.java
@@ -0,0 +1,64 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.monthly;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 工作报告-月报主表。
+ */
+@TableName("rdms_work_report_monthly_report")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MonthlyReportDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private Long reporterId;
+
+ private String reporterName;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String reporterDeptName;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String reporterPostName;
+
+ private Long supervisorUserId;
+
+ private String supervisorName;
+
+ private String periodKey;
+
+ private String periodLabel;
+
+ private LocalDate periodStartDate;
+
+ private LocalDate periodEndDate;
+
+ private String statusCode;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private BigDecimal totalWorkHours;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private LocalDateTime submitTime;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private LocalDateTime approvalTime;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String approvalComment;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String lastStatusReason;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportApprovalRecordDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportApprovalRecordDO.java
new file mode 100644
index 0000000..ee40216
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportApprovalRecordDO.java
@@ -0,0 +1,36 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.project;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 项目半月报审核记录。
+ */
+@TableName("rdms_work_report_project_report_approval_record")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ProjectReportApprovalRecordDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private Long projectReportId;
+
+ private Long statusLogId;
+
+ private Integer approvalRound;
+
+ private String conclusion;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String opinion;
+
+ private Long auditorUserId;
+
+ private String auditorName;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportCurrentItemDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportCurrentItemDO.java
new file mode 100644
index 0000000..d159434
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportCurrentItemDO.java
@@ -0,0 +1,36 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.project;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 项目半月报本期工作项。
+ */
+@TableName("rdms_work_report_project_report_current_item")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ProjectReportCurrentItemDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private Long reportId;
+
+ private String itemTitle;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private BigDecimal workHours;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String priorityCode;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private BigDecimal progressRate;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportDO.java
new file mode 100644
index 0000000..5a65054
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportDO.java
@@ -0,0 +1,85 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.project;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.common.WorkReportMemberSnapshotItem;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 工作报告-项目半月报主表。
+ */
+@TableName(value = "rdms_work_report_project_report", autoResultMap = true)
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ProjectReportDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private Long projectId;
+
+ private String projectName;
+
+ private Long projectOwnerId;
+
+ private String projectOwnerName;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String technicalOwnerName;
+
+ @TableField(typeHandler = JacksonTypeHandler.class, updateStrategy = FieldStrategy.ALWAYS)
+ private List projectMemberSnapshot;
+
+ private Long supervisorUserId;
+
+ private String supervisorName;
+
+ private String periodKey;
+
+ private String periodLabel;
+
+ private LocalDate periodStartDate;
+
+ private LocalDate periodEndDate;
+
+ private Integer flag;
+
+ private String statusCode;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String projectStatusDesc;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String projectProgressPlan;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String projectKeyPoints;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String projectProblems;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private BigDecimal totalWorkHours;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private LocalDateTime submitTime;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private LocalDateTime approvalTime;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String approvalComment;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String lastStatusReason;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportNextItemDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportNextItemDO.java
new file mode 100644
index 0000000..56dd6b7
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/project/ProjectReportNextItemDO.java
@@ -0,0 +1,33 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.project;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+
+/**
+ * 项目半月报下期工作项。
+ */
+@TableName("rdms_work_report_project_report_next_item")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ProjectReportNextItemDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private Long reportId;
+
+ private String itemTitle;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String priorityCode;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private BigDecimal progressRate;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportApprovalRecordDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportApprovalRecordDO.java
new file mode 100644
index 0000000..bf9fe9a
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportApprovalRecordDO.java
@@ -0,0 +1,36 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.weekly;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 周报审核记录。
+ */
+@TableName("rdms_work_report_weekly_report_approval_record")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WeeklyReportApprovalRecordDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private Long weeklyReportId;
+
+ private Long statusLogId;
+
+ private Integer approvalRound;
+
+ private String conclusion;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String opinion;
+
+ private Long auditorUserId;
+
+ private String auditorName;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportDO.java
new file mode 100644
index 0000000..cc70544
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportDO.java
@@ -0,0 +1,69 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.weekly;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 工作报告-周报主表。
+ */
+@TableName("rdms_work_report_weekly_report")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WeeklyReportDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private Long reporterId;
+
+ private String reporterName;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String reporterDeptName;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String reporterPostName;
+
+ private Long supervisorUserId;
+
+ private String supervisorName;
+
+ private String periodKey;
+
+ private String periodLabel;
+
+ private LocalDate periodStartDate;
+
+ private LocalDate periodEndDate;
+
+ private String statusCode;
+
+ private Boolean isBusinessTrip;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private BigDecimal totalTravelDays;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private BigDecimal totalWorkHours;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private LocalDateTime submitTime;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private LocalDateTime approvalTime;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String approvalComment;
+
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ private String lastStatusReason;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportTravelSegmentDO.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportTravelSegmentDO.java
new file mode 100644
index 0000000..1aa3347
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/dataobject/workreport/weekly/WeeklyReportTravelSegmentDO.java
@@ -0,0 +1,36 @@
+package com.njcn.rdms.module.project.dal.dataobject.workreport.weekly;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 周报出差分段。
+ */
+@TableName("rdms_work_report_weekly_report_travel_segment")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WeeklyReportTravelSegmentDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ private Long weeklyReportId;
+
+ @TableField("sort")
+ private Integer sort;
+
+ private LocalDate startDate;
+
+ private LocalDate endDate;
+
+ private BigDecimal travelDays;
+
+ private String location;
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/overtime/OvertimeApplicationApprovalRecordMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/overtime/OvertimeApplicationApprovalRecordMapper.java
new file mode 100644
index 0000000..83eb091
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/overtime/OvertimeApplicationApprovalRecordMapper.java
@@ -0,0 +1,24 @@
+package com.njcn.rdms.module.project.dal.mysql.overtime;
+
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.dal.dataobject.overtime.OvertimeApplicationApprovalRecordDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface OvertimeApplicationApprovalRecordMapper extends BaseMapperX {
+
+ default List selectListByApplicationId(Long applicationId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(OvertimeApplicationApprovalRecordDO::getOvertimeApplicationId, applicationId)
+ .orderByDesc(OvertimeApplicationApprovalRecordDO::getApprovalRound)
+ .orderByDesc(OvertimeApplicationApprovalRecordDO::getId));
+ }
+
+ default int countByApplicationId(Long applicationId) {
+ return Math.toIntExact(selectCount(new LambdaQueryWrapperX()
+ .eq(OvertimeApplicationApprovalRecordDO::getOvertimeApplicationId, applicationId)));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/personal/PersonalItemMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/personal/PersonalItemMapper.java
index 321c6e7..b06e746 100644
--- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/personal/PersonalItemMapper.java
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/personal/PersonalItemMapper.java
@@ -9,6 +9,9 @@ import com.njcn.rdms.module.project.dal.dataobject.personal.PersonalItemDO;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.util.StringUtils;
+import java.time.LocalDate;
+import java.util.List;
+
@Mapper
public interface PersonalItemMapper extends BaseMapperX {
@@ -45,4 +48,33 @@ public interface PersonalItemMapper extends BaseMapperX {
.eq(PersonalItemDO::getId, id)
.eq(PersonalItemDO::getStatusCode, fromStatus));
}
+
+ default List selectPlannedListByOwnerIdAndOverlap(Long ownerId, LocalDate startDate, LocalDate endDate,
+ String excludedStatusCode) {
+ LambdaQueryWrapperX queryWrapper = new LambdaQueryWrapperX()
+ .eq(PersonalItemDO::getOwnerId, ownerId);
+ if (StringUtils.hasText(excludedStatusCode)) {
+ queryWrapper.ne(PersonalItemDO::getStatusCode, excludedStatusCode);
+ }
+ queryWrapper.isNotNull(PersonalItemDO::getPlannedStartDate);
+ queryWrapper.isNotNull(PersonalItemDO::getPlannedEndDate);
+ queryWrapper.le(PersonalItemDO::getPlannedStartDate, endDate);
+ queryWrapper.ge(PersonalItemDO::getPlannedEndDate, startDate);
+ queryWrapper.orderByAsc(PersonalItemDO::getPlannedStartDate);
+ queryWrapper.orderByAsc(PersonalItemDO::getId);
+ return selectList(queryWrapper);
+ }
+
+ default List selectListByOwnerIdAndStatusNot(Long ownerId, String excludedStatusCode) {
+ if (ownerId == null) {
+ return List.of();
+ }
+ LambdaQueryWrapperX queryWrapper = new LambdaQueryWrapperX()
+ .eq(PersonalItemDO::getOwnerId, ownerId);
+ if (StringUtils.hasText(excludedStatusCode)) {
+ queryWrapper.ne(PersonalItemDO::getStatusCode, excludedStatusCode);
+ }
+ queryWrapper.orderByAsc(PersonalItemDO::getId);
+ return selectList(queryWrapper);
+ }
}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/ProjectMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/ProjectMapper.java
index edf9fbf..8d8c0f5 100644
--- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/ProjectMapper.java
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/ProjectMapper.java
@@ -58,6 +58,12 @@ public interface ProjectMapper extends BaseMapperX {
.orderByDesc(BaseDO::getCreateTime));
}
+ default List selectListByManagerUserId(Long managerUserId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ProjectDO::getManagerUserId, managerUserId)
+ .orderByDesc(BaseDO::getCreateTime));
+ }
+
default int updateStatusByIdAndStatus(Long id, String fromStatus, String toStatus, String lastStatusReason) {
ProjectDO update = new ProjectDO();
update.setStatusCode(toStatus);
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/execution/ProjectExecutionMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/execution/ProjectExecutionMapper.java
index 6d8b574..1d82ea7 100644
--- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/execution/ProjectExecutionMapper.java
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/execution/ProjectExecutionMapper.java
@@ -12,6 +12,7 @@ import com.njcn.rdms.module.project.dal.dataobject.project.execution.ProjectExec
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
+import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.util.Collection;
@@ -143,18 +144,6 @@ public interface ProjectExecutionMapper extends BaseMapperX
return Math.toIntExact(selectCount(queryWrapper));
}
- /**
- * 统计指定项目下处于非终态的执行数。用于项目 complete 前置校验(TD-015)。
- */
- default Integer countNonTerminalByProjectId(Long projectId, List terminalStatusCodes) {
- LambdaQueryWrapperX queryWrapper = new LambdaQueryWrapperX()
- .eq(ProjectExecutionDO::getProjectId, projectId);
- if (terminalStatusCodes != null && !terminalStatusCodes.isEmpty()) {
- queryWrapper.notIn(ProjectExecutionDO::getStatusCode, terminalStatusCodes);
- }
- return Math.toIntExact(selectCount(queryWrapper));
- }
-
default Integer countByProjectIdAndStatusCode(Long projectId,
ProjectExecutionStatusBoardReqVO reqVO,
String statusCode,
@@ -312,4 +301,45 @@ public interface ProjectExecutionMapper extends BaseMapperX
@Param("projectIds") Collection projectIds,
@Param("terminalStatusCodes") Collection terminalStatusCodes);
+ default List selectListByProjectId(Long projectId) {
+ if (projectId == null) {
+ return java.util.Collections.emptyList();
+ }
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ProjectExecutionDO::getProjectId, projectId)
+ .orderByAsc(ProjectExecutionDO::getId));
+ }
+
+ default List selectPlannedListByProjectIdAndOverlap(Long projectId, LocalDate startDate,
+ LocalDate endDate, String excludedStatusCode) {
+ if (projectId == null || startDate == null || endDate == null) {
+ return java.util.Collections.emptyList();
+ }
+ LambdaQueryWrapperX queryWrapper = new LambdaQueryWrapperX()
+ .eq(ProjectExecutionDO::getProjectId, projectId);
+ if (StringUtils.hasText(excludedStatusCode)) {
+ queryWrapper.ne(ProjectExecutionDO::getStatusCode, excludedStatusCode);
+ }
+ queryWrapper.isNotNull(ProjectExecutionDO::getPlannedStartDate);
+ queryWrapper.isNotNull(ProjectExecutionDO::getPlannedEndDate);
+ queryWrapper.le(ProjectExecutionDO::getPlannedStartDate, endDate);
+ queryWrapper.ge(ProjectExecutionDO::getPlannedEndDate, startDate);
+ queryWrapper.orderByAsc(ProjectExecutionDO::getPlannedStartDate);
+ queryWrapper.orderByAsc(ProjectExecutionDO::getId);
+ return selectList(queryWrapper);
+ }
+
+ default List selectListByProjectIdAndStatusNot(Long projectId, String excludedStatusCode) {
+ if (projectId == null) {
+ return java.util.Collections.emptyList();
+ }
+ LambdaQueryWrapperX queryWrapper = new LambdaQueryWrapperX()
+ .eq(ProjectExecutionDO::getProjectId, projectId);
+ if (StringUtils.hasText(excludedStatusCode)) {
+ queryWrapper.ne(ProjectExecutionDO::getStatusCode, excludedStatusCode);
+ }
+ queryWrapper.orderByAsc(ProjectExecutionDO::getId);
+ return selectList(queryWrapper);
+ }
+
}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/task/ProjectTaskMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/task/ProjectTaskMapper.java
index f997fd4..e518fa6 100644
--- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/task/ProjectTaskMapper.java
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/task/ProjectTaskMapper.java
@@ -791,4 +791,72 @@ public interface ProjectTaskMapper extends BaseMapperX {
@Param("projectIds") Collection projectIds,
@Param("terminalStatusCodes") Collection terminalStatusCodes);
+ default List selectListByProjectId(Long projectId) {
+ if (projectId == null) {
+ return List.of();
+ }
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ProjectTaskDO::getProjectId, projectId)
+ .orderByAsc(ProjectTaskDO::getExecutionId)
+ .orderByAsc(ProjectTaskDO::getId));
+ }
+
+ @Select("""
+
+ """)
+ List selectPlannedInvolvedListByUserIdAndOverlap(@Param("userId") Long userId,
+ @Param("startDate") LocalDate startDate,
+ @Param("endDate") LocalDate endDate,
+ @Param("excludedStatusCode") String excludedStatusCode);
+
+ @Select("""
+
+ """)
+ List selectInvolvedListByUserIdAndStatusNot(@Param("userId") Long userId,
+ @Param("excludedStatusCode") String excludedStatusCode);
+
}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/task/TaskWorklogMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/task/TaskWorklogMapper.java
index 4b69791..4998d60 100644
--- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/task/TaskWorklogMapper.java
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/project/task/TaskWorklogMapper.java
@@ -188,4 +188,30 @@ public interface TaskWorklogMapper extends BaseMapperX {
.eq(TaskWorklogDO::getTaskId, taskId)));
}
+ default List selectListByUserIdAndPeriod(Long userId, LocalDate startDate, LocalDate endDate) {
+ if (userId == null || startDate == null || endDate == null) {
+ return List.of();
+ }
+ return selectList(new LambdaQueryWrapperX()
+ .eq(TaskWorklogDO::getUserId, userId)
+ .le(TaskWorklogDO::getStartDate, endDate)
+ .ge(TaskWorklogDO::getEndDate, startDate)
+ .orderByAsc(TaskWorklogDO::getTaskId)
+ .orderByAsc(TaskWorklogDO::getEndDate)
+ .orderByAsc(TaskWorklogDO::getId));
+ }
+
+ default List selectListByTaskIdsAndPeriod(Collection taskIds, LocalDate startDate, LocalDate endDate) {
+ if (taskIds == null || taskIds.isEmpty() || startDate == null || endDate == null) {
+ return List.of();
+ }
+ return selectList(new LambdaQueryWrapperX()
+ .in(TaskWorklogDO::getTaskId, taskIds)
+ .le(TaskWorklogDO::getStartDate, endDate)
+ .ge(TaskWorklogDO::getEndDate, startDate)
+ .orderByAsc(TaskWorklogDO::getTaskId)
+ .orderByAsc(TaskWorklogDO::getEndDate)
+ .orderByAsc(TaskWorklogDO::getId));
+ }
+
}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/PersonalReportPlanItemMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/PersonalReportPlanItemMapper.java
new file mode 100644
index 0000000..a1d50f8
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/PersonalReportPlanItemMapper.java
@@ -0,0 +1,26 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.common;
+
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.common.PersonalReportPlanItemDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface PersonalReportPlanItemMapper extends BaseMapperX {
+
+ default List selectListByReport(String reportType, Long reportId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(PersonalReportPlanItemDO::getReportType, reportType)
+ .eq(PersonalReportPlanItemDO::getReportId, reportId)
+ .orderByAsc(PersonalReportPlanItemDO::getItemNumber)
+ .orderByAsc(PersonalReportPlanItemDO::getId));
+ }
+
+ default int deleteByReport(String reportType, Long reportId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(PersonalReportPlanItemDO::getReportType, reportType)
+ .eq(PersonalReportPlanItemDO::getReportId, reportId));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/PersonalReportReviewItemMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/PersonalReportReviewItemMapper.java
new file mode 100644
index 0000000..b637394
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/PersonalReportReviewItemMapper.java
@@ -0,0 +1,26 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.common;
+
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.common.PersonalReportReviewItemDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface PersonalReportReviewItemMapper extends BaseMapperX {
+
+ default List selectListByReport(String reportType, Long reportId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(PersonalReportReviewItemDO::getReportType, reportType)
+ .eq(PersonalReportReviewItemDO::getReportId, reportId)
+ .orderByAsc(PersonalReportReviewItemDO::getItemNumber)
+ .orderByAsc(PersonalReportReviewItemDO::getId));
+ }
+
+ default int deleteByReport(String reportType, Long reportId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(PersonalReportReviewItemDO::getReportType, reportType)
+ .eq(PersonalReportReviewItemDO::getReportId, reportId));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/WorkReportStatusLogMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/WorkReportStatusLogMapper.java
new file mode 100644
index 0000000..23b41b9
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/common/WorkReportStatusLogMapper.java
@@ -0,0 +1,26 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.common;
+
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.common.WorkReportStatusLogDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface WorkReportStatusLogMapper extends BaseMapperX {
+
+ default List selectListByReport(String reportType, Long reportId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(WorkReportStatusLogDO::getReportType, reportType)
+ .eq(WorkReportStatusLogDO::getReportId, reportId)
+ .orderByDesc(WorkReportStatusLogDO::getCreateTime)
+ .orderByDesc(WorkReportStatusLogDO::getId));
+ }
+
+ default int deleteByReport(String reportType, Long reportId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(WorkReportStatusLogDO::getReportType, reportType)
+ .eq(WorkReportStatusLogDO::getReportId, reportId));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/monthly/MonthlyReportApprovalRecordMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/monthly/MonthlyReportApprovalRecordMapper.java
new file mode 100644
index 0000000..a1924de
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/monthly/MonthlyReportApprovalRecordMapper.java
@@ -0,0 +1,29 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.monthly;
+
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.monthly.MonthlyReportApprovalRecordDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface MonthlyReportApprovalRecordMapper extends BaseMapperX {
+
+ default List selectListByMonthlyReportId(Long monthlyReportId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(MonthlyReportApprovalRecordDO::getMonthlyReportId, monthlyReportId)
+ .orderByDesc(MonthlyReportApprovalRecordDO::getApprovalRound)
+ .orderByDesc(MonthlyReportApprovalRecordDO::getId));
+ }
+
+ default int countByMonthlyReportId(Long monthlyReportId) {
+ return Math.toIntExact(selectCount(new LambdaQueryWrapperX()
+ .eq(MonthlyReportApprovalRecordDO::getMonthlyReportId, monthlyReportId)));
+ }
+
+ default int deleteByMonthlyReportId(Long monthlyReportId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(MonthlyReportApprovalRecordDO::getMonthlyReportId, monthlyReportId));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/monthly/MonthlyReportMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/monthly/MonthlyReportMapper.java
new file mode 100644
index 0000000..01f0732
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/monthly/MonthlyReportMapper.java
@@ -0,0 +1,123 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.monthly;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.njcn.rdms.framework.common.pojo.PageResult;
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.constant.WorkReportConstants;
+import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportPageReqVO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.monthly.MonthlyReportDO;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.List;
+
+@Mapper
+public interface MonthlyReportMapper extends BaseMapperX {
+
+ default MonthlyReportDO selectByReporterIdAndPeriodKey(Long reporterId, String periodKey) {
+ return selectOne(new LambdaQueryWrapperX()
+ .eq(MonthlyReportDO::getReporterId, reporterId)
+ .eq(MonthlyReportDO::getPeriodKey, periodKey));
+ }
+
+ default PageResult selectReporterPage(Long reporterId, MonthlyReportPageReqVO reqVO) {
+ return selectReporterPage(reporterId, reqVO, null);
+ }
+
+ default PageResult selectReporterPage(Long reporterId, MonthlyReportPageReqVO reqVO,
+ Collection allowedStatusCodes) {
+ if (allowedStatusCodes != null && allowedStatusCodes.isEmpty()) {
+ return new PageResult<>(List.of(), 0L);
+ }
+ LambdaQueryWrapperX wrapper = buildPageQuery(reqVO)
+ .eq(MonthlyReportDO::getReporterId, reporterId)
+ .orderByDesc(MonthlyReportDO::getPeriodStartDate)
+ .orderByDesc(MonthlyReportDO::getId);
+ if (allowedStatusCodes != null) {
+ wrapper.in(MonthlyReportDO::getStatusCode, allowedStatusCodes);
+ }
+ return selectPage(reqVO, wrapper);
+ }
+
+ default PageResult selectApprovalPage(Long supervisorUserId, MonthlyReportPageReqVO reqVO) {
+ LambdaQueryWrapperX wrapper = buildPageQuery(reqVO)
+ .eq(MonthlyReportDO::getSupervisorUserId, supervisorUserId)
+ .eq(MonthlyReportDO::getStatusCode, WorkReportConstants.STATUS_PENDING_APPROVAL)
+ .orderByDesc(MonthlyReportDO::getSubmitTime)
+ .orderByDesc(MonthlyReportDO::getId);
+ return selectPage(reqVO, wrapper);
+ }
+
+ default int updateByIdAndStatus(MonthlyReportDO update, Long id, String fromStatus) {
+ return update(update, new LambdaQueryWrapperX()
+ .eq(MonthlyReportDO::getId, id)
+ .eq(MonthlyReportDO::getStatusCode, fromStatus));
+ }
+
+ default int updateSubmitFieldsByIdAndStatus(Long id, String fromStatus, String reporterDeptName,
+ String reporterPostName, Long supervisorUserId,
+ String supervisorName, String toStatus, LocalDateTime submitTime,
+ String updater) {
+ return update(null, new LambdaUpdateWrapper()
+ .set(MonthlyReportDO::getReporterDeptName, reporterDeptName)
+ .set(MonthlyReportDO::getReporterPostName, reporterPostName)
+ .set(MonthlyReportDO::getSupervisorUserId, supervisorUserId)
+ .set(MonthlyReportDO::getSupervisorName, supervisorName)
+ .set(MonthlyReportDO::getStatusCode, toStatus)
+ .set(MonthlyReportDO::getSubmitTime, submitTime)
+ .set(MonthlyReportDO::getApprovalTime, null)
+ .set(MonthlyReportDO::getApprovalComment, null)
+ .set(MonthlyReportDO::getLastStatusReason, null)
+ .set(MonthlyReportDO::getUpdateTime, submitTime)
+ .set(MonthlyReportDO::getUpdater, updater)
+ .eq(MonthlyReportDO::getId, id)
+ .eq(MonthlyReportDO::getStatusCode, fromStatus));
+ }
+
+ default int updateApprovalFieldsByIdAndStatus(Long id, String fromStatus, String toStatus,
+ LocalDateTime approvalTime, String approvalComment,
+ String lastStatusReason, String updater) {
+ return update(null, new LambdaUpdateWrapper()
+ .set(MonthlyReportDO::getStatusCode, toStatus)
+ .set(MonthlyReportDO::getApprovalTime, approvalTime)
+ .set(MonthlyReportDO::getApprovalComment, approvalComment)
+ .set(MonthlyReportDO::getLastStatusReason, lastStatusReason)
+ .set(MonthlyReportDO::getUpdateTime, approvalTime)
+ .set(MonthlyReportDO::getUpdater, updater)
+ .eq(MonthlyReportDO::getId, id)
+ .eq(MonthlyReportDO::getStatusCode, fromStatus));
+ }
+
+ default int updateByIdAndStatusesAndReporterId(MonthlyReportDO update, Long id, Collection statuses,
+ Long reporterId) {
+ return update(update, new LambdaQueryWrapperX()
+ .eq(MonthlyReportDO::getId, id)
+ .eq(MonthlyReportDO::getReporterId, reporterId)
+ .in(MonthlyReportDO::getStatusCode, statuses));
+ }
+
+ default int deleteByIdAndStatusesAndReporterId(Long id, Collection statuses, Long reporterId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(MonthlyReportDO::getId, id)
+ .eq(MonthlyReportDO::getReporterId, reporterId)
+ .in(MonthlyReportDO::getStatusCode, statuses));
+ }
+
+ private LambdaQueryWrapperX buildPageQuery(MonthlyReportPageReqVO reqVO) {
+ LambdaQueryWrapperX wrapper = new LambdaQueryWrapperX()
+ .eqIfPresent(MonthlyReportDO::getStatusCode, reqVO.getStatusCode())
+ .betweenIfPresent(MonthlyReportDO::getPeriodStartDate, reqVO.getPeriodStartDate())
+ .betweenIfPresent(MonthlyReportDO::getSubmitTime, reqVO.getSubmitTime());
+ if (StringUtils.hasText(reqVO.getKeyword())) {
+ wrapper.and(w -> w.like(MonthlyReportDO::getPeriodLabel, reqVO.getKeyword())
+ .or()
+ .like(MonthlyReportDO::getReporterName, reqVO.getKeyword())
+ .or()
+ .like(MonthlyReportDO::getSupervisorName, reqVO.getKeyword()));
+ }
+ return wrapper;
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportApprovalRecordMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportApprovalRecordMapper.java
new file mode 100644
index 0000000..ea5ddcd
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportApprovalRecordMapper.java
@@ -0,0 +1,29 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.project;
+
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportApprovalRecordDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface ProjectReportApprovalRecordMapper extends BaseMapperX {
+
+ default List selectListByProjectReportId(Long projectReportId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ProjectReportApprovalRecordDO::getProjectReportId, projectReportId)
+ .orderByDesc(ProjectReportApprovalRecordDO::getApprovalRound)
+ .orderByDesc(ProjectReportApprovalRecordDO::getId));
+ }
+
+ default int countByProjectReportId(Long projectReportId) {
+ return Math.toIntExact(selectCount(new LambdaQueryWrapperX()
+ .eq(ProjectReportApprovalRecordDO::getProjectReportId, projectReportId)));
+ }
+
+ default int deleteByProjectReportId(Long projectReportId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(ProjectReportApprovalRecordDO::getProjectReportId, projectReportId));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportCurrentItemMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportCurrentItemMapper.java
new file mode 100644
index 0000000..b1ba562
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportCurrentItemMapper.java
@@ -0,0 +1,23 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.project;
+
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportCurrentItemDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface ProjectReportCurrentItemMapper extends BaseMapperX {
+
+ default List selectListByReportId(Long reportId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ProjectReportCurrentItemDO::getReportId, reportId)
+ .orderByAsc(ProjectReportCurrentItemDO::getId));
+ }
+
+ default int deleteByReportId(Long reportId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(ProjectReportCurrentItemDO::getReportId, reportId));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportMapper.java
new file mode 100644
index 0000000..7281a1d
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportMapper.java
@@ -0,0 +1,136 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.project;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.njcn.rdms.framework.common.pojo.PageResult;
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.constant.WorkReportConstants;
+import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportPageReqVO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.common.WorkReportMemberSnapshotItem;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportDO;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.List;
+
+@Mapper
+public interface ProjectReportMapper extends BaseMapperX {
+
+ String JACKSON_TYPE_HANDLER_MAPPING = "typeHandler=" + JacksonTypeHandler.class.getCanonicalName();
+
+ default ProjectReportDO selectByProjectIdAndPeriodKeyAndProjectOwnerId(Long projectId, String periodKey,
+ Long projectOwnerId) {
+ return selectOne(new LambdaQueryWrapperX()
+ .eq(ProjectReportDO::getProjectId, projectId)
+ .eq(ProjectReportDO::getPeriodKey, periodKey)
+ .eq(ProjectReportDO::getProjectOwnerId, projectOwnerId));
+ }
+
+ default PageResult selectReporterPage(Long reporterId, ProjectReportPageReqVO reqVO) {
+ return selectReporterPage(reporterId, reqVO, null);
+ }
+
+ default PageResult selectReporterPage(Long reporterId, ProjectReportPageReqVO reqVO,
+ Collection allowedStatusCodes) {
+ if (allowedStatusCodes != null && allowedStatusCodes.isEmpty()) {
+ return new PageResult<>(List.of(), 0L);
+ }
+ LambdaQueryWrapperX wrapper = buildPageQuery(reqVO)
+ .eq(ProjectReportDO::getProjectOwnerId, reporterId)
+ .orderByDesc(ProjectReportDO::getPeriodStartDate)
+ .orderByDesc(ProjectReportDO::getId);
+ if (allowedStatusCodes != null) {
+ wrapper.in(ProjectReportDO::getStatusCode, allowedStatusCodes);
+ }
+ return selectPage(reqVO, wrapper);
+ }
+
+ default PageResult selectApprovalPage(Long supervisorUserId, ProjectReportPageReqVO reqVO) {
+ LambdaQueryWrapperX wrapper = buildPageQuery(reqVO)
+ .eq(ProjectReportDO::getSupervisorUserId, supervisorUserId)
+ .eq(ProjectReportDO::getStatusCode, WorkReportConstants.STATUS_PENDING_APPROVAL)
+ .orderByDesc(ProjectReportDO::getSubmitTime)
+ .orderByDesc(ProjectReportDO::getId);
+ return selectPage(reqVO, wrapper);
+ }
+
+ default int updateByIdAndStatus(ProjectReportDO update, Long id, String fromStatus) {
+ return update(update, new LambdaQueryWrapperX()
+ .eq(ProjectReportDO::getId, id)
+ .eq(ProjectReportDO::getStatusCode, fromStatus));
+ }
+
+ default int updateSubmitFieldsByIdAndStatus(Long id, String fromStatus, String projectName,
+ Long projectOwnerId, String projectOwnerName,
+ List projectMemberSnapshot,
+ Long supervisorUserId, String supervisorName, String toStatus,
+ LocalDateTime submitTime, String updater) {
+ return update(null, new LambdaUpdateWrapper()
+ .set(ProjectReportDO::getProjectName, projectName)
+ .set(ProjectReportDO::getProjectOwnerId, projectOwnerId)
+ .set(ProjectReportDO::getProjectOwnerName, projectOwnerName)
+ .set(ProjectReportDO::getProjectMemberSnapshot, projectMemberSnapshot, JACKSON_TYPE_HANDLER_MAPPING)
+ .set(ProjectReportDO::getSupervisorUserId, supervisorUserId)
+ .set(ProjectReportDO::getSupervisorName, supervisorName)
+ .set(ProjectReportDO::getStatusCode, toStatus)
+ .set(ProjectReportDO::getSubmitTime, submitTime)
+ .set(ProjectReportDO::getApprovalTime, null)
+ .set(ProjectReportDO::getApprovalComment, null)
+ .set(ProjectReportDO::getLastStatusReason, null)
+ .set(ProjectReportDO::getUpdateTime, submitTime)
+ .set(ProjectReportDO::getUpdater, updater)
+ .eq(ProjectReportDO::getId, id)
+ .eq(ProjectReportDO::getStatusCode, fromStatus));
+ }
+
+ default int updateApprovalFieldsByIdAndStatus(Long id, String fromStatus, String toStatus,
+ LocalDateTime approvalTime, String approvalComment,
+ String lastStatusReason, String updater) {
+ return update(null, new LambdaUpdateWrapper()
+ .set(ProjectReportDO::getStatusCode, toStatus)
+ .set(ProjectReportDO::getApprovalTime, approvalTime)
+ .set(ProjectReportDO::getApprovalComment, approvalComment)
+ .set(ProjectReportDO::getLastStatusReason, lastStatusReason)
+ .set(ProjectReportDO::getUpdateTime, approvalTime)
+ .set(ProjectReportDO::getUpdater, updater)
+ .eq(ProjectReportDO::getId, id)
+ .eq(ProjectReportDO::getStatusCode, fromStatus));
+ }
+
+ default int updateByIdAndStatusesAndReporterId(ProjectReportDO update, Long id, Collection statuses,
+ Long reporterId) {
+ return update(update, new LambdaQueryWrapperX()
+ .eq(ProjectReportDO::getId, id)
+ .eq(ProjectReportDO::getProjectOwnerId, reporterId)
+ .in(ProjectReportDO::getStatusCode, statuses));
+ }
+
+ default int deleteByIdAndStatusesAndReporterId(Long id, Collection statuses, Long reporterId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(ProjectReportDO::getId, id)
+ .eq(ProjectReportDO::getProjectOwnerId, reporterId)
+ .in(ProjectReportDO::getStatusCode, statuses));
+ }
+
+ private LambdaQueryWrapperX buildPageQuery(ProjectReportPageReqVO reqVO) {
+ LambdaQueryWrapperX wrapper = new LambdaQueryWrapperX()
+ .eqIfPresent(ProjectReportDO::getProjectId, reqVO.getProjectId())
+ .eqIfPresent(ProjectReportDO::getFlag, reqVO.getFlag())
+ .eqIfPresent(ProjectReportDO::getStatusCode, reqVO.getStatusCode())
+ .betweenIfPresent(ProjectReportDO::getPeriodStartDate, reqVO.getPeriodStartDate())
+ .betweenIfPresent(ProjectReportDO::getSubmitTime, reqVO.getSubmitTime());
+ if (StringUtils.hasText(reqVO.getKeyword())) {
+ wrapper.and(w -> w.like(ProjectReportDO::getProjectName, reqVO.getKeyword())
+ .or()
+ .like(ProjectReportDO::getPeriodLabel, reqVO.getKeyword())
+ .or()
+ .like(ProjectReportDO::getProjectOwnerName, reqVO.getKeyword())
+ .or()
+ .like(ProjectReportDO::getSupervisorName, reqVO.getKeyword()));
+ }
+ return wrapper;
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportNextItemMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportNextItemMapper.java
new file mode 100644
index 0000000..6542f10
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/project/ProjectReportNextItemMapper.java
@@ -0,0 +1,23 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.project;
+
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportNextItemDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface ProjectReportNextItemMapper extends BaseMapperX {
+
+ default List selectListByReportId(Long reportId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(ProjectReportNextItemDO::getReportId, reportId)
+ .orderByAsc(ProjectReportNextItemDO::getId));
+ }
+
+ default int deleteByReportId(Long reportId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(ProjectReportNextItemDO::getReportId, reportId));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportApprovalRecordMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportApprovalRecordMapper.java
new file mode 100644
index 0000000..2bd8c4b
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportApprovalRecordMapper.java
@@ -0,0 +1,29 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.weekly;
+
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.weekly.WeeklyReportApprovalRecordDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface WeeklyReportApprovalRecordMapper extends BaseMapperX {
+
+ default List selectListByWeeklyReportId(Long weeklyReportId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(WeeklyReportApprovalRecordDO::getWeeklyReportId, weeklyReportId)
+ .orderByDesc(WeeklyReportApprovalRecordDO::getApprovalRound)
+ .orderByDesc(WeeklyReportApprovalRecordDO::getId));
+ }
+
+ default int countByWeeklyReportId(Long weeklyReportId) {
+ return Math.toIntExact(selectCount(new LambdaQueryWrapperX()
+ .eq(WeeklyReportApprovalRecordDO::getWeeklyReportId, weeklyReportId)));
+ }
+
+ default int deleteByWeeklyReportId(Long weeklyReportId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(WeeklyReportApprovalRecordDO::getWeeklyReportId, weeklyReportId));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportMapper.java
new file mode 100644
index 0000000..cd05f10
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportMapper.java
@@ -0,0 +1,124 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.weekly;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.njcn.rdms.framework.common.pojo.PageResult;
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.constant.WorkReportConstants;
+import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportPageReqVO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.weekly.WeeklyReportDO;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.List;
+
+@Mapper
+public interface WeeklyReportMapper extends BaseMapperX {
+
+ default WeeklyReportDO selectByReporterIdAndPeriodKey(Long reporterId, String periodKey) {
+ return selectOne(new LambdaQueryWrapperX()
+ .eq(WeeklyReportDO::getReporterId, reporterId)
+ .eq(WeeklyReportDO::getPeriodKey, periodKey));
+ }
+
+ default PageResult selectReporterPage(Long reporterId, WeeklyReportPageReqVO reqVO) {
+ return selectReporterPage(reporterId, reqVO, null);
+ }
+
+ default PageResult selectReporterPage(Long reporterId, WeeklyReportPageReqVO reqVO,
+ Collection allowedStatusCodes) {
+ if (allowedStatusCodes != null && allowedStatusCodes.isEmpty()) {
+ return new PageResult<>(List.of(), 0L);
+ }
+ LambdaQueryWrapperX wrapper = buildPageQuery(reqVO)
+ .eq(WeeklyReportDO::getReporterId, reporterId)
+ .orderByDesc(WeeklyReportDO::getPeriodStartDate)
+ .orderByDesc(WeeklyReportDO::getId);
+ if (allowedStatusCodes != null) {
+ wrapper.in(WeeklyReportDO::getStatusCode, allowedStatusCodes);
+ }
+ return selectPage(reqVO, wrapper);
+ }
+
+ default PageResult selectApprovalPage(Long supervisorUserId, WeeklyReportPageReqVO reqVO) {
+ LambdaQueryWrapperX wrapper = buildPageQuery(reqVO)
+ .eq(WeeklyReportDO::getSupervisorUserId, supervisorUserId)
+ .eq(WeeklyReportDO::getStatusCode, WorkReportConstants.STATUS_PENDING_APPROVAL)
+ .orderByDesc(WeeklyReportDO::getSubmitTime)
+ .orderByDesc(WeeklyReportDO::getId);
+ return selectPage(reqVO, wrapper);
+ }
+
+ default int updateByIdAndStatus(WeeklyReportDO update, Long id, String fromStatus) {
+ return update(update, new LambdaQueryWrapperX()
+ .eq(WeeklyReportDO::getId, id)
+ .eq(WeeklyReportDO::getStatusCode, fromStatus));
+ }
+
+ default int updateSubmitFieldsByIdAndStatus(Long id, String fromStatus, String reporterDeptName,
+ String reporterPostName, Long supervisorUserId,
+ String supervisorName, String toStatus, LocalDateTime submitTime,
+ String updater) {
+ return update(null, new LambdaUpdateWrapper()
+ .set(WeeklyReportDO::getReporterDeptName, reporterDeptName)
+ .set(WeeklyReportDO::getReporterPostName, reporterPostName)
+ .set(WeeklyReportDO::getSupervisorUserId, supervisorUserId)
+ .set(WeeklyReportDO::getSupervisorName, supervisorName)
+ .set(WeeklyReportDO::getStatusCode, toStatus)
+ .set(WeeklyReportDO::getSubmitTime, submitTime)
+ .set(WeeklyReportDO::getApprovalTime, null)
+ .set(WeeklyReportDO::getApprovalComment, null)
+ .set(WeeklyReportDO::getLastStatusReason, null)
+ .set(WeeklyReportDO::getUpdateTime, submitTime)
+ .set(WeeklyReportDO::getUpdater, updater)
+ .eq(WeeklyReportDO::getId, id)
+ .eq(WeeklyReportDO::getStatusCode, fromStatus));
+ }
+
+ default int updateApprovalFieldsByIdAndStatus(Long id, String fromStatus, String toStatus,
+ LocalDateTime approvalTime, String approvalComment,
+ String lastStatusReason, String updater) {
+ return update(null, new LambdaUpdateWrapper()
+ .set(WeeklyReportDO::getStatusCode, toStatus)
+ .set(WeeklyReportDO::getApprovalTime, approvalTime)
+ .set(WeeklyReportDO::getApprovalComment, approvalComment)
+ .set(WeeklyReportDO::getLastStatusReason, lastStatusReason)
+ .set(WeeklyReportDO::getUpdateTime, approvalTime)
+ .set(WeeklyReportDO::getUpdater, updater)
+ .eq(WeeklyReportDO::getId, id)
+ .eq(WeeklyReportDO::getStatusCode, fromStatus));
+ }
+
+ default int updateByIdAndStatusesAndReporterId(WeeklyReportDO update, Long id, Collection statuses,
+ Long reporterId) {
+ return update(update, new LambdaQueryWrapperX()
+ .eq(WeeklyReportDO::getId, id)
+ .eq(WeeklyReportDO::getReporterId, reporterId)
+ .in(WeeklyReportDO::getStatusCode, statuses));
+ }
+
+ default int deleteByIdAndStatusesAndReporterId(Long id, Collection statuses, Long reporterId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(WeeklyReportDO::getId, id)
+ .eq(WeeklyReportDO::getReporterId, reporterId)
+ .in(WeeklyReportDO::getStatusCode, statuses));
+ }
+
+ private LambdaQueryWrapperX buildPageQuery(WeeklyReportPageReqVO reqVO) {
+ LambdaQueryWrapperX wrapper = new LambdaQueryWrapperX()
+ .eqIfPresent(WeeklyReportDO::getStatusCode, reqVO.getStatusCode())
+ .eqIfPresent(WeeklyReportDO::getIsBusinessTrip, reqVO.getIsBusinessTrip())
+ .betweenIfPresent(WeeklyReportDO::getPeriodStartDate, reqVO.getPeriodStartDate())
+ .betweenIfPresent(WeeklyReportDO::getSubmitTime, reqVO.getSubmitTime());
+ if (StringUtils.hasText(reqVO.getKeyword())) {
+ wrapper.and(w -> w.like(WeeklyReportDO::getPeriodLabel, reqVO.getKeyword())
+ .or()
+ .like(WeeklyReportDO::getReporterName, reqVO.getKeyword())
+ .or()
+ .like(WeeklyReportDO::getSupervisorName, reqVO.getKeyword()));
+ }
+ return wrapper;
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportTravelSegmentMapper.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportTravelSegmentMapper.java
new file mode 100644
index 0000000..ebf543c
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/dal/mysql/workreport/weekly/WeeklyReportTravelSegmentMapper.java
@@ -0,0 +1,24 @@
+package com.njcn.rdms.module.project.dal.mysql.workreport.weekly;
+
+import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
+import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.weekly.WeeklyReportTravelSegmentDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface WeeklyReportTravelSegmentMapper extends BaseMapperX {
+
+ default List selectListByWeeklyReportId(Long weeklyReportId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(WeeklyReportTravelSegmentDO::getWeeklyReportId, weeklyReportId)
+ .orderByAsc(WeeklyReportTravelSegmentDO::getSort)
+ .orderByAsc(WeeklyReportTravelSegmentDO::getId));
+ }
+
+ default int deleteByWeeklyReportId(Long weeklyReportId) {
+ return delete(new LambdaQueryWrapperX()
+ .eq(WeeklyReportTravelSegmentDO::getWeeklyReportId, weeklyReportId));
+ }
+}
diff --git a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/framework/rpc/config/RpcConfiguration.java b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/framework/rpc/config/RpcConfiguration.java
index 986acf8..a5acaac 100644
--- a/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/framework/rpc/config/RpcConfiguration.java
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/framework/rpc/config/RpcConfiguration.java
@@ -1,13 +1,15 @@
package com.njcn.rdms.module.project.framework.rpc.config;
+import com.njcn.rdms.module.system.api.dept.DeptApi;
import com.njcn.rdms.module.system.api.dept.OrgLeaderApi;
+import com.njcn.rdms.module.system.api.dept.PostApi;
import com.njcn.rdms.module.system.api.dict.DictDataApi;
import com.njcn.rdms.module.system.api.file.FileApi;
-import com.njcn.rdms.module.system.api.notify.NotifyMessageSendApi;
import com.njcn.rdms.module.system.api.permission.ObjectPermissionApi;
import com.njcn.rdms.module.system.api.permission.PermissionApi;
import com.njcn.rdms.module.system.api.permission.UserVisibilityConfigApi;
import com.njcn.rdms.module.system.api.user.AdminUserApi;
+import com.njcn.rdms.module.system.api.user.UserManagementRelationApi;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@@ -15,6 +17,9 @@ import org.springframework.context.annotation.Configuration;
* Project 模块的 RPC 配置
*/
@Configuration(value = "projectRpcConfiguration", proxyBeanMethods = false)
-@EnableFeignClients(clients = {AdminUserApi.class, ObjectPermissionApi.class, DictDataApi.class, FileApi.class, PermissionApi.class, OrgLeaderApi.class, UserVisibilityConfigApi.class, NotifyMessageSendApi.class})
+@EnableFeignClients(clients =
+ {AdminUserApi.class, ObjectPermissionApi.class, DictDataApi.class, FileApi.class,
+ PermissionApi.class, OrgLeaderApi.class, UserVisibilityConfigApi.class,
+ DeptApi.class, PostApi.class, UserManagementRelationApi.class})
public class RpcConfiguration {
}
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 d30aa05..c67cc10 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
@@ -1,6 +1,7 @@
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.OvertimeApplicationExportVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO;
@@ -21,8 +22,6 @@ public interface OvertimeApplicationService {
void reject(Long id, OvertimeApplicationStatusActionReqVO reqVO);
- void cancel(Long id, OvertimeApplicationStatusActionReqVO reqVO);
-
void deleteApplication(Long id);
OvertimeApplicationRespVO getApplication(Long id);
@@ -35,5 +34,7 @@ public interface OvertimeApplicationService {
List getStatusLogs(Long id);
+ List getApprovalRecords(Long id);
+
List getExportList(OvertimeApplicationPageReqVO reqVO);
}
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 7d6d479..6b165b1 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
@@ -7,6 +7,7 @@ import com.njcn.rdms.framework.common.util.json.JsonUtils;
import com.njcn.rdms.framework.common.util.object.BeanUtils;
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.OvertimeApplicationExportVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO;
@@ -15,11 +16,13 @@ import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplica
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusDictRespVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusLogRespVO;
import com.njcn.rdms.module.project.dal.dataobject.audit.BizAuditLogDO;
+import com.njcn.rdms.module.project.dal.dataobject.overtime.OvertimeApplicationApprovalRecordDO;
import com.njcn.rdms.module.project.dal.dataobject.overtime.OvertimeApplicationDO;
import com.njcn.rdms.module.project.dal.dataobject.overtime.OvertimeApplicationStatusLogDO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusTransitionDO;
import com.njcn.rdms.module.project.dal.mysql.audit.BizAuditLogMapper;
+import com.njcn.rdms.module.project.dal.mysql.overtime.OvertimeApplicationApprovalRecordMapper;
import com.njcn.rdms.module.project.dal.mysql.overtime.OvertimeApplicationMapper;
import com.njcn.rdms.module.project.dal.mysql.overtime.OvertimeApplicationStatusLogMapper;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper;
@@ -53,6 +56,8 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
@Resource
private OvertimeApplicationStatusLogMapper overtimeApplicationStatusLogMapper;
@Resource
+ private OvertimeApplicationApprovalRecordMapper overtimeApplicationApprovalRecordMapper;
+ @Resource
private BizAuditLogMapper bizAuditLogMapper;
@Resource
private ObjectStatusModelMapper objectStatusModelMapper;
@@ -108,8 +113,7 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
update.setApprovalTime(null);
int updateCount = overtimeApplicationMapper.updateByIdAndStatusesAndApplicantId(update, id,
- List.of(OvertimeApplicationConstants.STATUS_REJECTED, OvertimeApplicationConstants.STATUS_CANCELLED),
- loginUserId);
+ List.of(OvertimeApplicationConstants.STATUS_REJECTED), loginUserId);
if (updateCount != 1) {
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_CONCURRENT_MODIFIED);
}
@@ -133,36 +137,6 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
processApprovalAction(id, OvertimeApplicationConstants.ACTION_REJECT, reqVO);
}
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void cancel(Long id, OvertimeApplicationStatusActionReqVO reqVO) {
- Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
- OvertimeApplicationDO current = validateApplicationExists(id);
- if (!Objects.equals(current.getApplicantId(), loginUserId)) {
- throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPLICANT_ONLY);
- }
- String reason = normalizeNullableText(reqVO == null ? null : reqVO.getReason());
- String fromStatus = current.getStatusCode();
- ObjectStatusTransitionDO transition = validateTransition(fromStatus, OvertimeApplicationConstants.ACTION_CANCEL,
- reason);
-
- OvertimeApplicationDO update = new OvertimeApplicationDO();
- update.setStatusCode(transition.getToStatusCode());
- update.setApprovalComment(reason);
- update.setApprovalTime(LocalDateTime.now());
- int updateCount = overtimeApplicationMapper.updateByIdAndStatusAndApplicantId(update, id, fromStatus,
- loginUserId);
- if (updateCount != 1) {
- throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_CONCURRENT_MODIFIED);
- }
-
- OvertimeApplicationDO after = mergeUpdated(current, update);
- writeStatusLog(after, OvertimeApplicationConstants.ACTION_CANCEL, fromStatus, transition.getToStatusCode(),
- reason);
- writeAuditLog(after, OvertimeApplicationConstants.ACTION_CANCEL, fromStatus, transition.getToStatusCode(),
- null, reason, null);
- }
-
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteApplication(Long id) {
@@ -171,8 +145,8 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
if (!Objects.equals(current.getApplicantId(), loginUserId)) {
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPLICANT_ONLY);
}
- if (!OvertimeApplicationConstants.STATUS_CANCELLED.equals(current.getStatusCode())) {
- throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_DELETE_ONLY_CANCELLED);
+ if (!OvertimeApplicationConstants.STATUS_REJECTED.equals(current.getStatusCode())) {
+ throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_DELETE_ONLY_REJECTED);
}
overtimeApplicationMapper.deleteById(id);
writeAuditLog(current, OvertimeApplicationConstants.ACTION_DELETE, current.getStatusCode(), null, null, null,
@@ -215,6 +189,13 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
OvertimeApplicationStatusLogRespVO.class);
}
+ @Override
+ public List getApprovalRecords(Long id) {
+ validateReadableApplication(id);
+ return BeanUtils.toBean(overtimeApplicationApprovalRecordMapper.selectListByApplicationId(id),
+ OvertimeApplicationApprovalRecordRespVO.class);
+ }
+
@Override
public List getExportList(OvertimeApplicationPageReqVO reqVO) {
reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
@@ -242,7 +223,9 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
}
OvertimeApplicationDO after = mergeUpdated(current, update);
- writeStatusLog(after, actionCode, fromStatus, transition.getToStatusCode(), reason);
+ OvertimeApplicationStatusLogDO statusLog = writeStatusLog(after, actionCode, fromStatus,
+ transition.getToStatusCode(), reason);
+ writeApprovalRecord(after, statusLog, reason);
writeAuditLog(after, actionCode, fromStatus, transition.getToStatusCode(), null, reason, null);
}
@@ -354,8 +337,8 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
return respVO;
}
- private void writeStatusLog(OvertimeApplicationDO application, String actionType, String fromStatus,
- String toStatus, String reason) {
+ private OvertimeApplicationStatusLogDO writeStatusLog(OvertimeApplicationDO application, String actionType,
+ String fromStatus, String toStatus, String reason) {
OvertimeApplicationStatusLogDO log = new OvertimeApplicationStatusLogDO();
log.setApplicationId(application.getId());
log.setActionType(actionType);
@@ -369,6 +352,20 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
log.setOvertimeDurationSnapshot(application.getOvertimeDuration());
log.setRemark(buildSnapshotRemark(application));
overtimeApplicationStatusLogMapper.insert(log);
+ return log;
+ }
+
+ private void writeApprovalRecord(OvertimeApplicationDO application, OvertimeApplicationStatusLogDO statusLog,
+ String reason) {
+ OvertimeApplicationApprovalRecordDO record = new OvertimeApplicationApprovalRecordDO();
+ record.setOvertimeApplicationId(application.getId());
+ record.setStatusLogId(statusLog.getId());
+ record.setApprovalRound(overtimeApplicationApprovalRecordMapper.countByApplicationId(application.getId()) + 1);
+ record.setConclusion(statusLog.getToStatus());
+ record.setOpinion(reason);
+ record.setAuditorUserId(SecurityFrameworkUtils.getLoginUserId());
+ record.setAuditorName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
+ overtimeApplicationApprovalRecordMapper.insert(record);
}
private void writeAuditLog(OvertimeApplicationDO application, String actionType, String fromStatus,
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
new file mode 100644
index 0000000..ec2323f
--- /dev/null
+++ b/rdms-project/rdms-project-boot/src/main/java/com/njcn/rdms/module/project/service/workreport/common/WorkReportCommonService.java
@@ -0,0 +1,1354 @@
+package com.njcn.rdms.module.project.service.workreport.common;
+
+import com.njcn.rdms.framework.common.pojo.CommonResult;
+import com.njcn.rdms.framework.common.pojo.PageParam;
+import com.njcn.rdms.framework.common.pojo.PageResult;
+import com.njcn.rdms.framework.common.util.object.BeanUtils;
+import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
+import com.njcn.rdms.module.project.constant.ProjectObjectConstants;
+import com.njcn.rdms.module.project.constant.WorkReportConstants;
+import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.*;
+import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.*;
+import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.*;
+import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.*;
+import com.njcn.rdms.module.project.dal.dataobject.audit.BizAuditLogDO;
+import com.njcn.rdms.module.project.dal.dataobject.member.UserObjectRoleDO;
+import com.njcn.rdms.module.project.dal.dataobject.project.ProjectDO;
+import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
+import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusTransitionDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.common.PersonalReportPlanItemDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.common.PersonalReportReviewItemDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.common.WorkReportMemberSnapshotItem;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.common.WorkReportStatusLogDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.monthly.MonthlyReportApprovalRecordDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.monthly.MonthlyReportDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportApprovalRecordDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportCurrentItemDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportNextItemDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.weekly.WeeklyReportApprovalRecordDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.weekly.WeeklyReportDO;
+import com.njcn.rdms.module.project.dal.dataobject.workreport.weekly.WeeklyReportTravelSegmentDO;
+import com.njcn.rdms.module.project.dal.mysql.audit.BizAuditLogMapper;
+import com.njcn.rdms.module.project.dal.mysql.member.UserObjectRoleMapper;
+import com.njcn.rdms.module.project.dal.mysql.project.ProjectMapper;
+import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper;
+import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusTransitionMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.common.PersonalReportPlanItemMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.common.PersonalReportReviewItemMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.common.WorkReportStatusLogMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.monthly.MonthlyReportApprovalRecordMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.monthly.MonthlyReportMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.project.ProjectReportApprovalRecordMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.project.ProjectReportCurrentItemMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.project.ProjectReportMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.project.ProjectReportNextItemMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.weekly.WeeklyReportApprovalRecordMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.weekly.WeeklyReportMapper;
+import com.njcn.rdms.module.project.dal.mysql.workreport.weekly.WeeklyReportTravelSegmentMapper;
+import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
+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.user.AdminUserApi;
+import com.njcn.rdms.module.system.api.user.UserManagementRelationApi;
+import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
+
+@Service
+public class WorkReportCommonService {
+
+ private static final List ALLOW_DELETE_STATUSES = List.of(
+ WorkReportConstants.STATUS_DRAFT,
+ WorkReportConstants.STATUS_REJECTED);
+
+ @Resource
+ private WeeklyReportMapper weeklyReportMapper;
+ @Resource
+ private MonthlyReportMapper monthlyReportMapper;
+ @Resource
+ private ProjectReportMapper projectReportMapper;
+ @Resource
+ private PersonalReportReviewItemMapper personalReportReviewItemMapper;
+ @Resource
+ private PersonalReportPlanItemMapper personalReportPlanItemMapper;
+ @Resource
+ private WeeklyReportTravelSegmentMapper weeklyReportTravelSegmentMapper;
+ @Resource
+ private ProjectReportCurrentItemMapper projectReportCurrentItemMapper;
+ @Resource
+ private ProjectReportNextItemMapper projectReportNextItemMapper;
+ @Resource
+ private WeeklyReportApprovalRecordMapper weeklyReportApprovalRecordMapper;
+ @Resource
+ private MonthlyReportApprovalRecordMapper monthlyReportApprovalRecordMapper;
+ @Resource
+ private ProjectReportApprovalRecordMapper projectReportApprovalRecordMapper;
+ @Resource
+ private WorkReportStatusLogMapper workReportStatusLogMapper;
+ @Resource
+ private BizAuditLogMapper bizAuditLogMapper;
+ @Resource
+ private ObjectStatusModelMapper objectStatusModelMapper;
+ @Resource
+ private ObjectStatusTransitionMapper objectStatusTransitionMapper;
+ @Resource
+ private AdminUserApi adminUserApi;
+ @Resource
+ private DeptApi deptApi;
+ @Resource
+ private PostApi postApi;
+ @Resource
+ private UserManagementRelationApi userManagementRelationApi;
+ @Resource
+ private ProjectMapper projectMapper;
+ @Resource
+ private UserObjectRoleMapper userObjectRoleMapper;
+
+ public List getStatusDict() {
+ return objectStatusModelMapper.selectListByObjectTypeEnabled(WorkReportConstants.STATUS_OBJECT_TYPE).stream()
+ .map(this::toStatusDictRespVO)
+ .collect(Collectors.toList());
+ }
+
+ public List getProjectReportOwnerProjectOptions() {
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ return projectMapper.selectListByManagerUserId(loginUserId).stream()
+ .map(project -> {
+ ProjectReportOwnerProjectOptionRespVO respVO = new ProjectReportOwnerProjectOptionRespVO();
+ respVO.setId(project.getId());
+ respVO.setProjectCode(project.getProjectCode());
+ respVO.setProjectName(project.getProjectName());
+ return respVO;
+ })
+ .collect(Collectors.toList());
+ }
+
+ public void validateCurrentUserIsProjectReportProjectOwner(Long projectId) {
+ ProjectDO project = validateProjectExists(projectId);
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ if (!Objects.equals(project.getManagerUserId(), loginUserId)) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_PROJECT_OWNER_ONLY);
+ }
+ }
+
+ public WeeklyReportRespVO initWeeklyReport() {
+ CurrentUserProfile profile = loadCurrentUserProfile(true);
+ WeeklyReportRespVO respVO = new WeeklyReportRespVO();
+ fillPersonalBase(respVO, profile);
+ applyStatusView(respVO, getInitialStatusModel());
+ respVO.setIsBusinessTrip(false);
+ respVO.setReviewItems(Collections.emptyList());
+ respVO.setPlanItems(Collections.emptyList());
+ respVO.setTravelSegments(Collections.emptyList());
+ return respVO;
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public Long createWeeklyReport(WeeklyReportSaveReqVO reqVO) {
+ validatePeriod(reqVO.getPeriodStartDate(), reqVO.getPeriodEndDate());
+ CurrentUserProfile profile = loadCurrentUserProfile(true);
+ validateWeeklyDuplicate(profile.userId(), reqVO.getPeriodKey(), null);
+
+ WeeklyReportDO report = new WeeklyReportDO();
+ applyWeeklySaveFields(report, reqVO, profile);
+ report.setStatusCode(getInitialStatusCode());
+ weeklyReportMapper.insert(report);
+ replaceWeeklyChildren(report.getId(), reqVO);
+ WeeklyReportDO latest = weeklyReportMapper.selectById(report.getId());
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_WEEKLY, latest.getId(), WorkReportConstants.ACTION_CREATE,
+ null, latest.getStatusCode(), null, buildWeeklyRemark(latest));
+ return report.getId();
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void updateWeeklyReport(Long id, WeeklyReportSaveReqVO reqVO) {
+ validatePeriod(reqVO.getPeriodStartDate(), reqVO.getPeriodEndDate());
+ WeeklyReportDO current = validateWeeklyReportExists(id);
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateReporter(loginUserId, current.getReporterId());
+ validateAllowEdit(current.getStatusCode());
+ validateWeeklyDuplicate(loginUserId, reqVO.getPeriodKey(), id);
+
+ CurrentUserProfile profile = loadCurrentUserProfile(false);
+ WeeklyReportDO update = new WeeklyReportDO();
+ update.setId(id);
+ applyWeeklySaveFields(update, reqVO, profile);
+ int updateCount = weeklyReportMapper.updateByIdAndStatus(update, id, current.getStatusCode());
+ if (updateCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_CONCURRENT_MODIFIED);
+ }
+ replaceWeeklyChildren(id, reqVO);
+ WeeklyReportDO latest = weeklyReportMapper.selectById(id);
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_WEEKLY, id, WorkReportConstants.ACTION_UPDATE,
+ current.getStatusCode(), latest.getStatusCode(), null, buildWeeklyRemark(latest));
+ }
+
+ public WeeklyReportRespVO getWeeklyReport(Long id) {
+ WeeklyReportDO report = validateReadableWeeklyReport(id);
+ return toWeeklyRespVO(report, true);
+ }
+
+ public PageResult getWeeklyReportPage(WeeklyReportPageReqVO reqVO) {
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ PageResult pageResult = weeklyReportMapper.selectReporterPage(loginUserId, reqVO,
+ getEnabledStatusCodes());
+ return new PageResult<>(pageResult.getList().stream()
+ .map(report -> toWeeklyRespVO(report, false))
+ .collect(Collectors.toList()), pageResult.getTotal());
+ }
+
+ public PageResult getWeeklyApprovalPage(WeeklyReportPageReqVO reqVO) {
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ PageResult pageResult = weeklyReportMapper.selectApprovalPage(loginUserId, reqVO);
+ return new PageResult<>(pageResult.getList().stream()
+ .map(report -> toWeeklyRespVO(report, false))
+ .collect(Collectors.toList()), pageResult.getTotal());
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void submitWeeklyReport(Long id) {
+ WeeklyReportDO current = validateWeeklyReportExists(id);
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateReporter(loginUserId, current.getReporterId());
+ String actionCode = resolveSubmitAction(current.getStatusCode());
+ ObjectStatusTransitionDO transition = validateTransition(current.getStatusCode(), actionCode, null);
+ CurrentUserProfile profile = loadCurrentUserProfile(true);
+
+ LocalDateTime submitTime = LocalDateTime.now();
+ int updateCount = weeklyReportMapper.updateSubmitFieldsByIdAndStatus(id, current.getStatusCode(),
+ profile.deptName(), profile.postName(), profile.directManagerId(), profile.directManagerName(),
+ transition.getToStatusCode(), submitTime, String.valueOf(loginUserId));
+ if (updateCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_CONCURRENT_MODIFIED);
+ }
+
+ WeeklyReportDO latest = weeklyReportMapper.selectById(id);
+ writeStatusLog(latest, WorkReportConstants.REPORT_TYPE_WEEKLY, actionCode, current.getStatusCode(),
+ transition.getToStatusCode(), null, buildWeeklyRemark(latest));
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_WEEKLY, id, actionCode, current.getStatusCode(),
+ transition.getToStatusCode(), null, buildWeeklyRemark(latest));
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void approveWeeklyReport(Long id, WorkReportStatusActionReqVO reqVO) {
+ processWeeklyApproval(id, WorkReportConstants.ACTION_APPROVE, normalizeReason(reqVO));
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void rejectWeeklyReport(Long id, WorkReportStatusActionReqVO reqVO) {
+ processWeeklyApproval(id, WorkReportConstants.ACTION_REJECT, normalizeReason(reqVO));
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteWeeklyReport(Long id) {
+ WeeklyReportDO current = validateWeeklyReportExists(id);
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateReporter(loginUserId, current.getReporterId());
+ validateDeleteAllowed(current.getStatusCode());
+ int deleteCount = weeklyReportMapper.deleteByIdAndStatusesAndReporterId(id, ALLOW_DELETE_STATUSES, loginUserId);
+ if (deleteCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_DELETE_NOT_ALLOWED);
+ }
+ personalReportReviewItemMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_WEEKLY, id);
+ personalReportPlanItemMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_WEEKLY, id);
+ weeklyReportTravelSegmentMapper.deleteByWeeklyReportId(id);
+ weeklyReportApprovalRecordMapper.deleteByWeeklyReportId(id);
+ workReportStatusLogMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_WEEKLY, id);
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_WEEKLY, id, WorkReportConstants.ACTION_DELETE,
+ current.getStatusCode(), null, null, buildWeeklyRemark(current));
+ }
+
+ public List getWeeklyStatusLogs(Long id) {
+ validateReadableWeeklyReport(id);
+ return BeanUtils.toBean(workReportStatusLogMapper.selectListByReport(WorkReportConstants.REPORT_TYPE_WEEKLY, id),
+ WorkReportStatusLogRespVO.class);
+ }
+
+ public List getWeeklyApprovalRecords(Long id) {
+ validateReadableWeeklyReport(id);
+ return BeanUtils.toBean(weeklyReportApprovalRecordMapper.selectListByWeeklyReportId(id),
+ WorkReportApprovalRecordRespVO.class);
+ }
+
+ public List getWeeklyExportList(WeeklyReportPageReqVO reqVO) {
+ reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ return BeanUtils.toBean(getWeeklyReportPage(reqVO).getList(), WeeklyReportExportVO.class);
+ }
+
+ public MonthlyReportRespVO initMonthlyReport() {
+ CurrentUserProfile profile = loadCurrentUserProfile(true);
+ MonthlyReportRespVO respVO = new MonthlyReportRespVO();
+ fillPersonalBase(respVO, profile);
+ applyStatusView(respVO, getInitialStatusModel());
+ respVO.setReviewItems(Collections.emptyList());
+ respVO.setPlanItems(Collections.emptyList());
+ return respVO;
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public Long createMonthlyReport(MonthlyReportSaveReqVO reqVO) {
+ validatePeriod(reqVO.getPeriodStartDate(), reqVO.getPeriodEndDate());
+ CurrentUserProfile profile = loadCurrentUserProfile(true);
+ validateMonthlyDuplicate(profile.userId(), reqVO.getPeriodKey(), null);
+
+ MonthlyReportDO report = new MonthlyReportDO();
+ applyMonthlySaveFields(report, reqVO, profile);
+ report.setStatusCode(getInitialStatusCode());
+ monthlyReportMapper.insert(report);
+ replaceMonthlyChildren(report.getId(), reqVO);
+ MonthlyReportDO latest = monthlyReportMapper.selectById(report.getId());
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_MONTHLY, latest.getId(), WorkReportConstants.ACTION_CREATE,
+ null, latest.getStatusCode(), null, buildMonthlyRemark(latest));
+ return report.getId();
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void updateMonthlyReport(Long id, MonthlyReportSaveReqVO reqVO) {
+ validatePeriod(reqVO.getPeriodStartDate(), reqVO.getPeriodEndDate());
+ MonthlyReportDO current = validateMonthlyReportExists(id);
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateReporter(loginUserId, current.getReporterId());
+ validateAllowEdit(current.getStatusCode());
+ validateMonthlyDuplicate(loginUserId, reqVO.getPeriodKey(), id);
+
+ CurrentUserProfile profile = loadCurrentUserProfile(false);
+ MonthlyReportDO update = new MonthlyReportDO();
+ update.setId(id);
+ applyMonthlySaveFields(update, reqVO, profile);
+ int updateCount = monthlyReportMapper.updateByIdAndStatus(update, id, current.getStatusCode());
+ if (updateCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_CONCURRENT_MODIFIED);
+ }
+ replaceMonthlyChildren(id, reqVO);
+ MonthlyReportDO latest = monthlyReportMapper.selectById(id);
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_MONTHLY, id, WorkReportConstants.ACTION_UPDATE,
+ current.getStatusCode(), latest.getStatusCode(), null, buildMonthlyRemark(latest));
+ }
+
+ public MonthlyReportRespVO getMonthlyReport(Long id) {
+ MonthlyReportDO report = validateReadableMonthlyReport(id);
+ return toMonthlyRespVO(report, true);
+ }
+
+ public PageResult getMonthlyReportPage(MonthlyReportPageReqVO reqVO) {
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ PageResult pageResult = monthlyReportMapper.selectReporterPage(loginUserId, reqVO,
+ getEnabledStatusCodes());
+ return new PageResult<>(pageResult.getList().stream()
+ .map(report -> toMonthlyRespVO(report, false))
+ .collect(Collectors.toList()), pageResult.getTotal());
+ }
+
+ public PageResult getMonthlyApprovalPage(MonthlyReportPageReqVO reqVO) {
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ PageResult pageResult = monthlyReportMapper.selectApprovalPage(loginUserId, reqVO);
+ return new PageResult<>(pageResult.getList().stream()
+ .map(report -> toMonthlyRespVO(report, false))
+ .collect(Collectors.toList()), pageResult.getTotal());
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void submitMonthlyReport(Long id) {
+ MonthlyReportDO current = validateMonthlyReportExists(id);
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateReporter(loginUserId, current.getReporterId());
+ String actionCode = resolveSubmitAction(current.getStatusCode());
+ ObjectStatusTransitionDO transition = validateTransition(current.getStatusCode(), actionCode, null);
+ CurrentUserProfile profile = loadCurrentUserProfile(true);
+
+ LocalDateTime submitTime = LocalDateTime.now();
+ int updateCount = monthlyReportMapper.updateSubmitFieldsByIdAndStatus(id, current.getStatusCode(),
+ profile.deptName(), profile.postName(), profile.directManagerId(), profile.directManagerName(),
+ transition.getToStatusCode(), submitTime, String.valueOf(loginUserId));
+ if (updateCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_CONCURRENT_MODIFIED);
+ }
+
+ MonthlyReportDO latest = monthlyReportMapper.selectById(id);
+ writeStatusLog(latest, WorkReportConstants.REPORT_TYPE_MONTHLY, actionCode, current.getStatusCode(),
+ transition.getToStatusCode(), null, buildMonthlyRemark(latest));
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_MONTHLY, id, actionCode, current.getStatusCode(),
+ transition.getToStatusCode(), null, buildMonthlyRemark(latest));
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void approveMonthlyReport(Long id, MonthlyReportApproveReqVO reqVO) {
+ processMonthlyApproval(id, WorkReportConstants.ACTION_APPROVE, reqVO);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void rejectMonthlyReport(Long id, WorkReportStatusActionReqVO reqVO) {
+ MonthlyReportApproveReqVO rejectReqVO = new MonthlyReportApproveReqVO();
+ rejectReqVO.setReason(normalizeReason(reqVO));
+ processMonthlyApproval(id, WorkReportConstants.ACTION_REJECT, rejectReqVO);
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteMonthlyReport(Long id) {
+ MonthlyReportDO current = validateMonthlyReportExists(id);
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateReporter(loginUserId, current.getReporterId());
+ validateDeleteAllowed(current.getStatusCode());
+ int deleteCount = monthlyReportMapper.deleteByIdAndStatusesAndReporterId(id, ALLOW_DELETE_STATUSES, loginUserId);
+ if (deleteCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_DELETE_NOT_ALLOWED);
+ }
+ personalReportReviewItemMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_MONTHLY, id);
+ personalReportPlanItemMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_MONTHLY, id);
+ monthlyReportApprovalRecordMapper.deleteByMonthlyReportId(id);
+ workReportStatusLogMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_MONTHLY, id);
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_MONTHLY, id, WorkReportConstants.ACTION_DELETE,
+ current.getStatusCode(), null, null, buildMonthlyRemark(current));
+ }
+
+ public List getMonthlyStatusLogs(Long id) {
+ validateReadableMonthlyReport(id);
+ return BeanUtils.toBean(workReportStatusLogMapper.selectListByReport(WorkReportConstants.REPORT_TYPE_MONTHLY, id),
+ WorkReportStatusLogRespVO.class);
+ }
+
+ public List getMonthlyApprovalRecords(Long id) {
+ validateReadableMonthlyReport(id);
+ return BeanUtils.toBean(monthlyReportApprovalRecordMapper.selectListByMonthlyReportId(id),
+ MonthlyReportApprovalRecordRespVO.class);
+ }
+
+ public List getMonthlyExportList(MonthlyReportPageReqVO reqVO) {
+ reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ return BeanUtils.toBean(getMonthlyReportPage(reqVO).getList(), MonthlyReportExportVO.class);
+ }
+
+ public ProjectReportRespVO initProjectReport(Long projectId) {
+ CurrentUserProfile profile = loadCurrentUserProfile(true);
+ ProjectDO project = validateProjectExists(projectId);
+ ProjectReportRespVO respVO = new ProjectReportRespVO();
+ respVO.setProjectId(project.getId());
+ respVO.setProjectName(project.getProjectName());
+ respVO.setProjectOwnerId(profile.userId());
+ respVO.setProjectOwnerName(profile.userName());
+ respVO.setProjectMemberSnapshot(BeanUtils.toBean(buildProjectMemberSnapshot(projectId),
+ WorkReportMemberSnapshotRespVO.class));
+ respVO.setSupervisorUserId(profile.directManagerId());
+ respVO.setSupervisorName(profile.directManagerName());
+ applyStatusView(respVO, getInitialStatusModel());
+ respVO.setCurrentItems(Collections.emptyList());
+ respVO.setNextItems(Collections.emptyList());
+ return respVO;
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public Long createProjectReport(ProjectReportSaveReqVO reqVO) {
+ validatePeriod(reqVO.getPeriodStartDate(), reqVO.getPeriodEndDate());
+ validateProjectFlag(reqVO.getFlag());
+ CurrentUserProfile profile = loadCurrentUserProfile(true);
+ validateProjectReportDuplicate(reqVO.getProjectId(), reqVO.getPeriodKey(), profile.userId(), null);
+ ProjectDO project = validateProjectExists(reqVO.getProjectId());
+
+ ProjectReportDO report = new ProjectReportDO();
+ applyProjectSaveFields(report, reqVO, profile, project);
+ report.setStatusCode(getInitialStatusCode());
+ projectReportMapper.insert(report);
+ replaceProjectChildren(report.getId(), reqVO);
+ ProjectReportDO latest = projectReportMapper.selectById(report.getId());
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_PROJECT, latest.getId(), WorkReportConstants.ACTION_CREATE,
+ null, latest.getStatusCode(), null, buildProjectRemark(latest));
+ return report.getId();
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void updateProjectReport(Long id, ProjectReportSaveReqVO reqVO) {
+ validatePeriod(reqVO.getPeriodStartDate(), reqVO.getPeriodEndDate());
+ validateProjectFlag(reqVO.getFlag());
+ ProjectReportDO current = validateProjectReportExists(id);
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateReporter(loginUserId, current.getProjectOwnerId());
+ validateAllowEdit(current.getStatusCode());
+ validateProjectReportDuplicate(reqVO.getProjectId(), reqVO.getPeriodKey(), loginUserId, id);
+
+ CurrentUserProfile profile = loadCurrentUserProfile(false);
+ ProjectDO project = validateProjectExists(reqVO.getProjectId());
+ ProjectReportDO update = new ProjectReportDO();
+ update.setId(id);
+ applyProjectSaveFields(update, reqVO, profile, project);
+ update.setTechnicalOwnerName(current.getTechnicalOwnerName());
+ int updateCount = projectReportMapper.updateByIdAndStatus(update, id, current.getStatusCode());
+ if (updateCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_CONCURRENT_MODIFIED);
+ }
+ replaceProjectChildren(id, reqVO);
+ ProjectReportDO latest = projectReportMapper.selectById(id);
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_PROJECT, id, WorkReportConstants.ACTION_UPDATE,
+ current.getStatusCode(), latest.getStatusCode(), null, buildProjectRemark(latest));
+ }
+
+ public ProjectReportRespVO getProjectReport(Long id) {
+ ProjectReportDO report = validateReadableProjectReport(id);
+ return toProjectRespVO(report, true);
+ }
+
+ public PageResult getProjectReportPage(ProjectReportPageReqVO reqVO) {
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ PageResult pageResult = projectReportMapper.selectReporterPage(loginUserId, reqVO,
+ getEnabledStatusCodes());
+ return new PageResult<>(pageResult.getList().stream()
+ .map(report -> toProjectRespVO(report, false))
+ .collect(Collectors.toList()), pageResult.getTotal());
+ }
+
+ public PageResult getProjectApprovalPage(ProjectReportPageReqVO reqVO) {
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ PageResult pageResult = projectReportMapper.selectApprovalPage(loginUserId, reqVO);
+ return new PageResult<>(pageResult.getList().stream()
+ .map(report -> toProjectRespVO(report, false))
+ .collect(Collectors.toList()), pageResult.getTotal());
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void submitProjectReport(Long id) {
+ ProjectReportDO current = validateProjectReportExists(id);
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateReporter(loginUserId, current.getProjectOwnerId());
+ String actionCode = resolveSubmitAction(current.getStatusCode());
+ ObjectStatusTransitionDO transition = validateTransition(current.getStatusCode(), actionCode, null);
+ CurrentUserProfile profile = loadCurrentUserProfile(true);
+ ProjectDO project = validateProjectExists(current.getProjectId());
+
+ LocalDateTime submitTime = LocalDateTime.now();
+ int updateCount = projectReportMapper.updateSubmitFieldsByIdAndStatus(id, current.getStatusCode(),
+ project.getProjectName(), profile.userId(), profile.userName(), buildProjectMemberSnapshot(project.getId()),
+ profile.directManagerId(), profile.directManagerName(), transition.getToStatusCode(), submitTime,
+ String.valueOf(loginUserId));
+ if (updateCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_CONCURRENT_MODIFIED);
+ }
+
+ ProjectReportDO latest = projectReportMapper.selectById(id);
+ writeStatusLog(latest, WorkReportConstants.REPORT_TYPE_PROJECT, actionCode, current.getStatusCode(),
+ transition.getToStatusCode(), null, buildProjectRemark(latest));
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_PROJECT, id, actionCode, current.getStatusCode(),
+ transition.getToStatusCode(), null, buildProjectRemark(latest));
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void approveProjectReport(Long id, WorkReportStatusActionReqVO reqVO) {
+ processProjectApproval(id, WorkReportConstants.ACTION_APPROVE, normalizeReason(reqVO));
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void rejectProjectReport(Long id, WorkReportStatusActionReqVO reqVO) {
+ processProjectApproval(id, WorkReportConstants.ACTION_REJECT, normalizeReason(reqVO));
+ }
+
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteProjectReport(Long id) {
+ ProjectReportDO current = validateProjectReportExists(id);
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateReporter(loginUserId, current.getProjectOwnerId());
+ validateDeleteAllowed(current.getStatusCode());
+ int deleteCount = projectReportMapper.deleteByIdAndStatusesAndReporterId(id, ALLOW_DELETE_STATUSES, loginUserId);
+ if (deleteCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_DELETE_NOT_ALLOWED);
+ }
+ projectReportCurrentItemMapper.deleteByReportId(id);
+ projectReportNextItemMapper.deleteByReportId(id);
+ projectReportApprovalRecordMapper.deleteByProjectReportId(id);
+ workReportStatusLogMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_PROJECT, id);
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_PROJECT, id, WorkReportConstants.ACTION_DELETE,
+ current.getStatusCode(), null, null, buildProjectRemark(current));
+ }
+
+ public List getProjectStatusLogs(Long id) {
+ validateReadableProjectReport(id);
+ return BeanUtils.toBean(workReportStatusLogMapper.selectListByReport(WorkReportConstants.REPORT_TYPE_PROJECT, id),
+ WorkReportStatusLogRespVO.class);
+ }
+
+ public List getProjectApprovalRecords(Long id) {
+ validateReadableProjectReport(id);
+ return BeanUtils.toBean(projectReportApprovalRecordMapper.selectListByProjectReportId(id),
+ WorkReportApprovalRecordRespVO.class);
+ }
+
+ public List getProjectExportList(ProjectReportPageReqVO reqVO) {
+ reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ return BeanUtils.toBean(getProjectReportPage(reqVO).getList(), ProjectReportExportVO.class);
+ }
+
+ private void processWeeklyApproval(Long id, String actionCode, String reason) {
+ WeeklyReportDO current = validateWeeklyReportExists(id);
+ Long approverUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateApprover(approverUserId, current.getSupervisorUserId());
+ ObjectStatusTransitionDO transition = validateTransition(current.getStatusCode(), actionCode, reason);
+
+ LocalDateTime approvalTime = LocalDateTime.now();
+ int updateCount = weeklyReportMapper.updateApprovalFieldsByIdAndStatus(id, current.getStatusCode(),
+ transition.getToStatusCode(), approvalTime, reason, reason, String.valueOf(approverUserId));
+ if (updateCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_CONCURRENT_MODIFIED);
+ }
+
+ WeeklyReportDO latest = weeklyReportMapper.selectById(id);
+ WorkReportStatusLogDO statusLog = writeStatusLog(latest, WorkReportConstants.REPORT_TYPE_WEEKLY, actionCode,
+ current.getStatusCode(), transition.getToStatusCode(), reason, buildWeeklyRemark(latest));
+ writeWeeklyApprovalRecord(latest, statusLog, reason);
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_WEEKLY, id, actionCode, current.getStatusCode(),
+ transition.getToStatusCode(), reason, buildWeeklyRemark(latest));
+ }
+
+ private void processMonthlyApproval(Long id, String actionCode, MonthlyReportApproveReqVO reqVO) {
+ MonthlyReportDO current = validateMonthlyReportExists(id);
+ Long approverUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateApprover(approverUserId, current.getSupervisorUserId());
+ String reason = normalizeReason(reqVO);
+ ObjectStatusTransitionDO transition = validateTransition(current.getStatusCode(), actionCode, reason);
+
+ LocalDateTime approvalTime = LocalDateTime.now();
+ int updateCount = monthlyReportMapper.updateApprovalFieldsByIdAndStatus(id, current.getStatusCode(),
+ transition.getToStatusCode(), approvalTime, reason, reason, String.valueOf(approverUserId));
+ if (updateCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_CONCURRENT_MODIFIED);
+ }
+
+ MonthlyReportDO latest = monthlyReportMapper.selectById(id);
+ WorkReportStatusLogDO statusLog = writeStatusLog(latest, WorkReportConstants.REPORT_TYPE_MONTHLY, actionCode,
+ current.getStatusCode(), transition.getToStatusCode(), reason, buildMonthlyRemark(latest));
+ writeMonthlyApprovalRecord(latest, statusLog, actionCode, reqVO);
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_MONTHLY, id, actionCode, current.getStatusCode(),
+ transition.getToStatusCode(), reason, buildMonthlyRemark(latest));
+ }
+
+ private void processProjectApproval(Long id, String actionCode, String reason) {
+ ProjectReportDO current = validateProjectReportExists(id);
+ Long approverUserId = SecurityFrameworkUtils.getLoginUserId();
+ validateApprover(approverUserId, current.getSupervisorUserId());
+ ObjectStatusTransitionDO transition = validateTransition(current.getStatusCode(), actionCode, reason);
+
+ LocalDateTime approvalTime = LocalDateTime.now();
+ int updateCount = projectReportMapper.updateApprovalFieldsByIdAndStatus(id, current.getStatusCode(),
+ transition.getToStatusCode(), approvalTime, reason, reason, String.valueOf(approverUserId));
+ if (updateCount != 1) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_CONCURRENT_MODIFIED);
+ }
+
+ ProjectReportDO latest = projectReportMapper.selectById(id);
+ WorkReportStatusLogDO statusLog = writeStatusLog(latest, WorkReportConstants.REPORT_TYPE_PROJECT, actionCode,
+ current.getStatusCode(), transition.getToStatusCode(), reason, buildProjectRemark(latest));
+ writeProjectApprovalRecord(latest, statusLog, reason);
+ writeAuditLog(WorkReportConstants.BIZ_TYPE_PROJECT, id, actionCode, current.getStatusCode(),
+ transition.getToStatusCode(), reason, buildProjectRemark(latest));
+ }
+
+ private WeeklyReportDO validateWeeklyReportExists(Long id) {
+ WeeklyReportDO report = weeklyReportMapper.selectById(id);
+ if (report == null) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_NOT_EXISTS);
+ }
+ return report;
+ }
+
+ private MonthlyReportDO validateMonthlyReportExists(Long id) {
+ MonthlyReportDO report = monthlyReportMapper.selectById(id);
+ if (report == null) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_NOT_EXISTS);
+ }
+ return report;
+ }
+
+ private ProjectReportDO validateProjectReportExists(Long id) {
+ ProjectReportDO report = projectReportMapper.selectById(id);
+ if (report == null) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_NOT_EXISTS);
+ }
+ return report;
+ }
+
+ private WeeklyReportDO validateReadableWeeklyReport(Long id) {
+ WeeklyReportDO report = validateWeeklyReportExists(id);
+ validateReadable(report.getReporterId(), report.getSupervisorUserId());
+ return report;
+ }
+
+ private MonthlyReportDO validateReadableMonthlyReport(Long id) {
+ MonthlyReportDO report = validateMonthlyReportExists(id);
+ validateReadable(report.getReporterId(), report.getSupervisorUserId());
+ return report;
+ }
+
+ private ProjectReportDO validateReadableProjectReport(Long id) {
+ ProjectReportDO report = validateProjectReportExists(id);
+ validateReadable(report.getProjectOwnerId(), report.getSupervisorUserId());
+ return report;
+ }
+
+ private void validateReadable(Long reporterId, Long supervisorUserId) {
+ Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+ if (!Objects.equals(loginUserId, reporterId) && !Objects.equals(loginUserId, supervisorUserId)) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_READ_FORBIDDEN);
+ }
+ }
+
+ private void validateReporter(Long loginUserId, Long reporterId) {
+ if (!Objects.equals(loginUserId, reporterId)) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_REPORTER_ONLY);
+ }
+ }
+
+ private void validateApprover(Long loginUserId, Long approverId) {
+ if (!Objects.equals(loginUserId, approverId)) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_APPROVER_ONLY);
+ }
+ }
+
+ private void validateDeleteAllowed(String statusCode) {
+ if (WorkReportConstants.STATUS_APPROVED.equals(statusCode)) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_DELETE_NOT_ALLOWED);
+ }
+ }
+
+ private void validateAllowEdit(String statusCode) {
+ ObjectStatusModelDO statusModel = getStatusModel(statusCode);
+ if (!Boolean.TRUE.equals(statusModel.getAllowEdit())) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_NOT_ALLOW_EDIT);
+ }
+ }
+
+ private ObjectStatusTransitionDO validateTransition(String fromStatus, String actionCode, String reason) {
+ ObjectStatusTransitionDO transition = objectStatusTransitionMapper
+ .selectByObjectTypeAndFromStatusAndAction(WorkReportConstants.STATUS_OBJECT_TYPE, fromStatus, actionCode);
+ if (transition == null) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_ACTION_NOT_ALLOWED, actionCode);
+ }
+ if (Boolean.TRUE.equals(transition.getNeedReason()) && !StringUtils.hasText(reason)) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_ACTION_REASON_REQUIRED, actionCode);
+ }
+ getStatusModel(transition.getToStatusCode());
+ return transition;
+ }
+
+ private ObjectStatusModelDO getStatusModel(String statusCode) {
+ ObjectStatusModelDO statusModel = objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled(
+ WorkReportConstants.STATUS_OBJECT_TYPE, statusCode);
+ if (statusModel == null) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_MODEL_NOT_EXISTS_OR_DISABLED);
+ }
+ return statusModel;
+ }
+
+ private List getEnabledStatusCodes() {
+ return objectStatusModelMapper.selectListByObjectTypeEnabled(WorkReportConstants.STATUS_OBJECT_TYPE).stream()
+ .map(ObjectStatusModelDO::getStatusCode)
+ .filter(StringUtils::hasText)
+ .collect(Collectors.toList());
+ }
+
+ private ObjectStatusModelDO getInitialStatusModel() {
+ ObjectStatusModelDO statusModel = objectStatusModelMapper
+ .selectInitialByObjectTypeEnabled(WorkReportConstants.STATUS_OBJECT_TYPE);
+ if (statusModel == null || !StringUtils.hasText(statusModel.getStatusCode())) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_MODEL_NOT_EXISTS_OR_DISABLED);
+ }
+ return statusModel;
+ }
+
+ private String getInitialStatusCode() {
+ return getInitialStatusModel().getStatusCode();
+ }
+
+ private String resolveSubmitAction(String statusCode) {
+ if (WorkReportConstants.STATUS_DRAFT.equals(statusCode)) {
+ return WorkReportConstants.ACTION_SUBMIT;
+ }
+ if (WorkReportConstants.STATUS_REJECTED.equals(statusCode)) {
+ return WorkReportConstants.ACTION_RESUBMIT;
+ }
+ throw exception(ErrorCodeConstants.WORK_REPORT_STATUS_ACTION_NOT_ALLOWED, WorkReportConstants.ACTION_SUBMIT);
+ }
+
+ private void validateWeeklyDuplicate(Long reporterId, String periodKey, Long excludeId) {
+ WeeklyReportDO exist = weeklyReportMapper.selectByReporterIdAndPeriodKey(reporterId, normalizeRequiredText(periodKey, "周期编码不能为空"));
+ if (exist != null && !Objects.equals(exist.getId(), excludeId)) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_PERIOD_DUPLICATE);
+ }
+ }
+
+ private void validateMonthlyDuplicate(Long reporterId, String periodKey, Long excludeId) {
+ MonthlyReportDO exist = monthlyReportMapper.selectByReporterIdAndPeriodKey(reporterId, normalizeRequiredText(periodKey, "周期编码不能为空"));
+ if (exist != null && !Objects.equals(exist.getId(), excludeId)) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_PERIOD_DUPLICATE);
+ }
+ }
+
+ private void validateProjectReportDuplicate(Long projectId, String periodKey, Long reporterId, Long excludeId) {
+ ProjectReportDO exist = projectReportMapper.selectByProjectIdAndPeriodKeyAndProjectOwnerId(projectId,
+ normalizeRequiredText(periodKey, "周期编码不能为空"), reporterId);
+ if (exist != null && !Objects.equals(exist.getId(), excludeId)) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_PERIOD_DUPLICATE);
+ }
+ }
+
+ private void validatePeriod(LocalDate startDate, LocalDate endDate) {
+ if (startDate == null || endDate == null) {
+ throw invalidParamException("周期开始日期和结束日期不能为空");
+ }
+ if (endDate.isBefore(startDate)) {
+ throw invalidParamException("周期结束日期不能早于开始日期");
+ }
+ }
+
+ private void validateProjectFlag(Integer flag) {
+ if (!Objects.equals(flag, 1) && !Objects.equals(flag, 2)) {
+ throw invalidParamException("上半月/下半月标记只能为 1 或 2");
+ }
+ }
+
+ private CurrentUserProfile loadCurrentUserProfile(boolean requireManager) {
+ Long userId = SecurityFrameworkUtils.getLoginUserId();
+ AdminUserRespDTO user = loadUser(userId);
+ String userName = StringUtils.hasText(user.getNickname())
+ ? user.getNickname().trim()
+ : defaultText(SecurityFrameworkUtils.getLoginUserNickname());
+
+ String deptName = null;
+ if (user.getDeptId() != null) {
+ CommonResult deptResult = deptApi.getDept(user.getDeptId());
+ DeptRespDTO dept = deptResult == null ? null : deptResult.getCheckedData();
+ deptName = dept == null ? null : dept.getName();
+ }
+
+ String postName = null;
+ if (user.getPositionId() != null) {
+ Map postMap = postApi.getPostMap(Collections.singleton(user.getPositionId()));
+ PostRespDTO post = postMap.get(user.getPositionId());
+ postName = post == null ? null : post.getName();
+ }
+
+ Long directManagerId = null;
+ String directManagerName = null;
+ CommonResult directManagerResult = userManagementRelationApi.getDirectManager(userId);
+ AdminUserRespDTO directManager = directManagerResult == null ? null : directManagerResult.getCheckedData();
+ if (directManager != null) {
+ directManagerId = directManager.getId();
+ directManagerName = defaultText(directManager.getNickname());
+ }
+ if (requireManager && directManagerId == null) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_DIRECT_MANAGER_NOT_EXISTS);
+ }
+ return new CurrentUserProfile(userId, defaultText(userName), deptName, postName, directManagerId, directManagerName);
+ }
+
+ private AdminUserRespDTO loadUser(Long userId) {
+ if (userId == null) {
+ throw invalidParamException("用户编号不能为空");
+ }
+ adminUserApi.validateUserList(Collections.singleton(userId)).getCheckedData();
+ CommonResult result = adminUserApi.getUser(userId);
+ AdminUserRespDTO user = result == null ? null : result.getCheckedData();
+ if (user == null) {
+ throw invalidParamException("用户不存在:{}", userId);
+ }
+ return user;
+ }
+
+ private ProjectDO validateProjectExists(Long projectId) {
+ ProjectDO project = projectMapper.selectById(projectId);
+ if (project == null) {
+ throw exception(ErrorCodeConstants.WORK_REPORT_PROJECT_NOT_EXISTS);
+ }
+ return project;
+ }
+
+ private List buildProjectMemberSnapshot(Long projectId) {
+ List members = userObjectRoleMapper.selectListByObject(ProjectObjectConstants.OBJECT_TYPE, projectId);
+ Set userIds = new LinkedHashSet<>();
+ for (UserObjectRoleDO member : members) {
+ if (member == null || !Objects.equals(member.getStatus(), 0) || member.getUserId() == null) {
+ continue;
+ }
+ userIds.add(member.getUserId());
+ }
+ if (userIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+ Map userMap = adminUserApi.getUserMap(userIds);
+ List snapshot = new ArrayList<>(userIds.size());
+ for (Long userId : userIds) {
+ WorkReportMemberSnapshotItem item = new WorkReportMemberSnapshotItem();
+ item.setUserId(userId);
+ AdminUserRespDTO user = userMap.get(userId);
+ item.setUserName(user == null ? "" : defaultText(user.getNickname()));
+ snapshot.add(item);
+ }
+ return snapshot;
+ }
+
+ private void applyWeeklySaveFields(WeeklyReportDO target, WeeklyReportSaveReqVO reqVO, CurrentUserProfile profile) {
+ List travelSegments = Boolean.TRUE.equals(reqVO.getIsBusinessTrip())
+ ? defaultList(reqVO.getTravelSegments()) : Collections.emptyList();
+ validateTravelSegments(reqVO.getPeriodStartDate(), reqVO.getPeriodEndDate(), travelSegments);
+ target.setReporterId(profile.userId());
+ target.setReporterName(profile.userName());
+ target.setReporterDeptName(profile.deptName());
+ target.setReporterPostName(profile.postName());
+ target.setSupervisorUserId(profile.directManagerId());
+ target.setSupervisorName(profile.directManagerName());
+ target.setPeriodKey(normalizeRequiredText(reqVO.getPeriodKey(), "周期编码不能为空"));
+ target.setPeriodLabel(normalizeRequiredText(reqVO.getPeriodLabel(), "周期名称不能为空"));
+ target.setPeriodStartDate(reqVO.getPeriodStartDate());
+ target.setPeriodEndDate(reqVO.getPeriodEndDate());
+ target.setIsBusinessTrip(Boolean.TRUE.equals(reqVO.getIsBusinessTrip()));
+ target.setTotalTravelDays(sumTravelDays(travelSegments));
+ target.setTotalWorkHours(sumReviewWorkHours(reqVO.getReviewItems()));
+ }
+
+ private void applyMonthlySaveFields(MonthlyReportDO target, MonthlyReportSaveReqVO reqVO, CurrentUserProfile profile) {
+ target.setReporterId(profile.userId());
+ target.setReporterName(profile.userName());
+ target.setReporterDeptName(profile.deptName());
+ target.setReporterPostName(profile.postName());
+ target.setSupervisorUserId(profile.directManagerId());
+ target.setSupervisorName(profile.directManagerName());
+ target.setPeriodKey(normalizeRequiredText(reqVO.getPeriodKey(), "周期编码不能为空"));
+ target.setPeriodLabel(normalizeRequiredText(reqVO.getPeriodLabel(), "周期名称不能为空"));
+ target.setPeriodStartDate(reqVO.getPeriodStartDate());
+ target.setPeriodEndDate(reqVO.getPeriodEndDate());
+ target.setTotalWorkHours(sumReviewWorkHours(reqVO.getReviewItems()));
+ }
+
+ private void applyProjectSaveFields(ProjectReportDO target, ProjectReportSaveReqVO reqVO,
+ CurrentUserProfile profile, ProjectDO project) {
+ target.setProjectId(project.getId());
+ target.setProjectName(project.getProjectName());
+ target.setProjectOwnerId(profile.userId());
+ target.setProjectOwnerName(profile.userName());
+ target.setProjectMemberSnapshot(buildProjectMemberSnapshot(project.getId()));
+ target.setSupervisorUserId(profile.directManagerId());
+ target.setSupervisorName(profile.directManagerName());
+ target.setPeriodKey(normalizeRequiredText(reqVO.getPeriodKey(), "周期编码不能为空"));
+ target.setPeriodLabel(normalizeRequiredText(reqVO.getPeriodLabel(), "周期名称不能为空"));
+ target.setPeriodStartDate(reqVO.getPeriodStartDate());
+ target.setPeriodEndDate(reqVO.getPeriodEndDate());
+ target.setFlag(reqVO.getFlag());
+ target.setProjectStatusDesc(normalizeNullableText(reqVO.getProjectStatusDesc()));
+ target.setProjectProgressPlan(normalizeNullableText(reqVO.getProjectProgressPlan()));
+ target.setProjectKeyPoints(normalizeNullableText(reqVO.getProjectKeyPoints()));
+ target.setProjectProblems(normalizeNullableText(reqVO.getProjectProblems()));
+ target.setTotalWorkHours(sumProjectCurrentWorkHours(reqVO.getCurrentItems()));
+ }
+
+ private void replaceWeeklyChildren(Long reportId, WeeklyReportSaveReqVO reqVO) {
+ personalReportReviewItemMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_WEEKLY, reportId);
+ personalReportPlanItemMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_WEEKLY, reportId);
+ weeklyReportTravelSegmentMapper.deleteByWeeklyReportId(reportId);
+ insertReviewItems(WorkReportConstants.REPORT_TYPE_WEEKLY, reportId, reqVO.getReviewItems());
+ insertPlanItems(WorkReportConstants.REPORT_TYPE_WEEKLY, reportId, reqVO.getPlanItems());
+ if (Boolean.TRUE.equals(reqVO.getIsBusinessTrip())) {
+ insertTravelSegments(reportId, reqVO.getTravelSegments());
+ }
+ }
+
+ private void replaceMonthlyChildren(Long reportId, MonthlyReportSaveReqVO reqVO) {
+ personalReportReviewItemMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_MONTHLY, reportId);
+ personalReportPlanItemMapper.deleteByReport(WorkReportConstants.REPORT_TYPE_MONTHLY, reportId);
+ insertReviewItems(WorkReportConstants.REPORT_TYPE_MONTHLY, reportId, reqVO.getReviewItems());
+ insertPlanItems(WorkReportConstants.REPORT_TYPE_MONTHLY, reportId, reqVO.getPlanItems());
+ }
+
+ private void replaceProjectChildren(Long reportId, ProjectReportSaveReqVO reqVO) {
+ projectReportCurrentItemMapper.deleteByReportId(reportId);
+ projectReportNextItemMapper.deleteByReportId(reportId);
+ insertProjectCurrentItems(reportId, reqVO.getCurrentItems());
+ insertProjectNextItems(reportId, reqVO.getNextItems());
+ }
+
+ private void insertReviewItems(String reportType, Long reportId, List items) {
+ List source = defaultList(items);
+ for (int i = 0; i < source.size(); i++) {
+ PersonalReportReviewItemReqVO item = source.get(i);
+ if (!StringUtils.hasText(item.getItemTitle())) {
+ continue;
+ }
+ PersonalReportReviewItemDO data = new PersonalReportReviewItemDO();
+ data.setReportType(reportType);
+ data.setReportId(reportId);
+ data.setItemNumber(item.getItemNumber() != null ? item.getItemNumber() : i + 1);
+ data.setItemTitle(item.getItemTitle().trim());
+ data.setWorkHours(item.getWorkHours());
+ data.setContentText(normalizeNullableText(item.getContentText()));
+ data.setContentJson(item.getContentJson());
+ data.setReflectionText(normalizeNullableText(item.getReflectionText()));
+ personalReportReviewItemMapper.insert(data);
+ }
+ }
+
+ private void insertPlanItems(String reportType, Long reportId, List items) {
+ List source = defaultList(items);
+ for (int i = 0; i < source.size(); i++) {
+ PersonalReportPlanItemReqVO item = source.get(i);
+ if (!StringUtils.hasText(item.getItemTitle())) {
+ continue;
+ }
+ PersonalReportPlanItemDO data = new PersonalReportPlanItemDO();
+ data.setReportType(reportType);
+ data.setReportId(reportId);
+ data.setItemNumber(item.getItemNumber() != null ? item.getItemNumber() : i + 1);
+ data.setItemTitle(item.getItemTitle().trim());
+ data.setTargetText(normalizeNullableText(item.getTargetText()));
+ data.setTargetJson(item.getTargetJson());
+ data.setSupportNeed(normalizeNullableText(item.getSupportNeed()));
+ personalReportPlanItemMapper.insert(data);
+ }
+ }
+
+ private void insertTravelSegments(Long reportId, List items) {
+ List source = defaultList(items);
+ for (int i = 0; i < source.size(); i++) {
+ WeeklyReportTravelSegmentReqVO item = source.get(i);
+ if (item.getStartDate() == null || item.getEndDate() == null) {
+ continue;
+ }
+ WeeklyReportTravelSegmentDO data = new WeeklyReportTravelSegmentDO();
+ data.setWeeklyReportId(reportId);
+ data.setSort(item.getSort() != null ? item.getSort() : i + 1);
+ data.setStartDate(item.getStartDate());
+ data.setEndDate(item.getEndDate());
+ data.setTravelDays(item.getTravelDays());
+ data.setLocation(normalizeNullableText(item.getLocation()));
+ weeklyReportTravelSegmentMapper.insert(data);
+ }
+ }
+
+ private void insertProjectCurrentItems(Long reportId, List items) {
+ List source = defaultList(items);
+ for (ProjectReportItemReqVO item : source) {
+ if (!StringUtils.hasText(item.getItemTitle())) {
+ continue;
+ }
+ ProjectReportCurrentItemDO data = new ProjectReportCurrentItemDO();
+ data.setReportId(reportId);
+ data.setItemTitle(item.getItemTitle().trim());
+ data.setWorkHours(item.getWorkHours());
+ data.setPriorityCode(normalizeNullableText(item.getPriorityCode()));
+ data.setProgressRate(item.getProgressRate());
+ projectReportCurrentItemMapper.insert(data);
+ }
+ }
+
+ private void insertProjectNextItems(Long reportId, List items) {
+ List source = defaultList(items);
+ for (ProjectReportItemReqVO item : source) {
+ if (!StringUtils.hasText(item.getItemTitle())) {
+ continue;
+ }
+ ProjectReportNextItemDO data = new ProjectReportNextItemDO();
+ data.setReportId(reportId);
+ data.setItemTitle(item.getItemTitle().trim());
+ data.setPriorityCode(normalizeNullableText(item.getPriorityCode()));
+ data.setProgressRate(item.getProgressRate());
+ projectReportNextItemMapper.insert(data);
+ }
+ }
+
+ private WorkReportStatusLogDO writeStatusLog(Object report, String reportType, String actionType,
+ String fromStatus, String toStatus, String reason, String remark) {
+ WorkReportStatusLogDO log = new WorkReportStatusLogDO();
+ log.setReportType(reportType);
+ log.setReportId(extractReportId(report));
+ log.setActionType(actionType);
+ log.setFromStatus(fromStatus);
+ log.setToStatus(toStatus);
+ log.setReason(reason);
+ log.setOperatorUserId(SecurityFrameworkUtils.getLoginUserId());
+ log.setOperatorName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
+ log.setPeriodLabelSnapshot(extractPeriodLabel(report));
+ log.setRemark(remark);
+ workReportStatusLogMapper.insert(log);
+ return log;
+ }
+
+ private void writeAuditLog(String bizType, Long bizId, String actionType, String fromStatus,
+ String toStatus, String reason, String remark) {
+ BizAuditLogDO auditLog = new BizAuditLogDO();
+ auditLog.setBizType(bizType);
+ auditLog.setBizId(bizId);
+ auditLog.setActionType(actionType);
+ auditLog.setFromStatus(fromStatus);
+ auditLog.setToStatus(toStatus);
+ auditLog.setReason(reason);
+ auditLog.setOperatorUserId(SecurityFrameworkUtils.getLoginUserId());
+ auditLog.setOperatorName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
+ auditLog.setRemark(remark);
+ bizAuditLogMapper.insert(auditLog);
+ }
+
+ private void writeWeeklyApprovalRecord(WeeklyReportDO report, WorkReportStatusLogDO statusLog, String reason) {
+ WeeklyReportApprovalRecordDO record = new WeeklyReportApprovalRecordDO();
+ record.setWeeklyReportId(report.getId());
+ record.setStatusLogId(statusLog.getId());
+ record.setApprovalRound(weeklyReportApprovalRecordMapper.countByWeeklyReportId(report.getId()) + 1);
+ record.setConclusion(statusLog.getToStatus());
+ record.setOpinion(reason);
+ record.setAuditorUserId(SecurityFrameworkUtils.getLoginUserId());
+ record.setAuditorName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
+ weeklyReportApprovalRecordMapper.insert(record);
+ }
+
+ private void writeMonthlyApprovalRecord(MonthlyReportDO report, WorkReportStatusLogDO statusLog,
+ String actionCode, MonthlyReportApproveReqVO reqVO) {
+ MonthlyReportApprovalRecordDO record = new MonthlyReportApprovalRecordDO();
+ record.setMonthlyReportId(report.getId());
+ record.setStatusLogId(statusLog.getId());
+ record.setApprovalRound(monthlyReportApprovalRecordMapper.countByMonthlyReportId(report.getId()) + 1);
+ record.setConclusion(statusLog.getToStatus());
+ record.setOpinion(normalizeReason(reqVO));
+ if (WorkReportConstants.ACTION_APPROVE.equals(actionCode)) {
+ record.setMeetingDate(reqVO.getMeetingDate());
+ record.setStrengthDesc(normalizeNullableText(reqVO.getStrengthDesc()));
+ record.setStrengthExample(normalizeNullableText(reqVO.getStrengthExample()));
+ record.setWeaknessDesc(normalizeNullableText(reqVO.getWeaknessDesc()));
+ record.setWeaknessExample(normalizeNullableText(reqVO.getWeaknessExample()));
+ record.setImprovementSuggestion(normalizeNullableText(reqVO.getImprovementSuggestion()));
+ record.setPerformanceResult(normalizeNullableText(reqVO.getPerformanceResult()));
+ record.setEmployeeSignName(normalizeNullableText(reqVO.getEmployeeSignName()));
+ record.setEmployeeSignedDate(reqVO.getEmployeeSignedDate());
+ record.setSupervisorSignName(normalizeNullableText(reqVO.getSupervisorSignName()));
+ record.setSupervisorSignedDate(reqVO.getSupervisorSignedDate());
+ }
+ record.setAuditorUserId(SecurityFrameworkUtils.getLoginUserId());
+ record.setAuditorName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
+ monthlyReportApprovalRecordMapper.insert(record);
+ }
+
+ private void writeProjectApprovalRecord(ProjectReportDO report, WorkReportStatusLogDO statusLog, String reason) {
+ ProjectReportApprovalRecordDO record = new ProjectReportApprovalRecordDO();
+ record.setProjectReportId(report.getId());
+ record.setStatusLogId(statusLog.getId());
+ record.setApprovalRound(projectReportApprovalRecordMapper.countByProjectReportId(report.getId()) + 1);
+ record.setConclusion(statusLog.getToStatus());
+ record.setOpinion(reason);
+ record.setAuditorUserId(SecurityFrameworkUtils.getLoginUserId());
+ record.setAuditorName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
+ projectReportApprovalRecordMapper.insert(record);
+ }
+
+ private Long extractReportId(Object report) {
+ if (report instanceof WeeklyReportDO weeklyReport) {
+ return weeklyReport.getId();
+ }
+ if (report instanceof MonthlyReportDO monthlyReport) {
+ return monthlyReport.getId();
+ }
+ if (report instanceof ProjectReportDO projectReport) {
+ return projectReport.getId();
+ }
+ return null;
+ }
+
+ private String extractPeriodLabel(Object report) {
+ if (report instanceof WeeklyReportDO weeklyReport) {
+ return weeklyReport.getPeriodLabel();
+ }
+ if (report instanceof MonthlyReportDO monthlyReport) {
+ return monthlyReport.getPeriodLabel();
+ }
+ if (report instanceof ProjectReportDO projectReport) {
+ return projectReport.getPeriodLabel();
+ }
+ return null;
+ }
+
+ private WeeklyReportRespVO toWeeklyRespVO(WeeklyReportDO report, boolean withChildren) {
+ WeeklyReportRespVO respVO = BeanUtils.toBean(report, WeeklyReportRespVO.class);
+ applyStatusView(respVO, getStatusModel(report.getStatusCode()));
+ if (withChildren) {
+ respVO.setReviewItems(BeanUtils.toBean(
+ personalReportReviewItemMapper.selectListByReport(WorkReportConstants.REPORT_TYPE_WEEKLY, report.getId()),
+ PersonalReportReviewItemRespVO.class));
+ respVO.setPlanItems(BeanUtils.toBean(
+ personalReportPlanItemMapper.selectListByReport(WorkReportConstants.REPORT_TYPE_WEEKLY, report.getId()),
+ PersonalReportPlanItemRespVO.class));
+ respVO.setTravelSegments(BeanUtils.toBean(
+ weeklyReportTravelSegmentMapper.selectListByWeeklyReportId(report.getId()),
+ WeeklyReportTravelSegmentRespVO.class));
+ }
+ return respVO;
+ }
+
+ private MonthlyReportRespVO toMonthlyRespVO(MonthlyReportDO report, boolean withChildren) {
+ MonthlyReportRespVO respVO = BeanUtils.toBean(report, MonthlyReportRespVO.class);
+ applyStatusView(respVO, getStatusModel(report.getStatusCode()));
+ if (withChildren) {
+ respVO.setReviewItems(BeanUtils.toBean(
+ personalReportReviewItemMapper.selectListByReport(WorkReportConstants.REPORT_TYPE_MONTHLY, report.getId()),
+ PersonalReportReviewItemRespVO.class));
+ respVO.setPlanItems(BeanUtils.toBean(
+ personalReportPlanItemMapper.selectListByReport(WorkReportConstants.REPORT_TYPE_MONTHLY, report.getId()),
+ PersonalReportPlanItemRespVO.class));
+ }
+ return respVO;
+ }
+
+ private ProjectReportRespVO toProjectRespVO(ProjectReportDO report, boolean withChildren) {
+ ProjectReportRespVO respVO = BeanUtils.toBean(report, ProjectReportRespVO.class);
+ applyStatusView(respVO, getStatusModel(report.getStatusCode()));
+ respVO.setProjectMemberSnapshot(BeanUtils.toBean(defaultList(report.getProjectMemberSnapshot()),
+ WorkReportMemberSnapshotRespVO.class));
+ if (withChildren) {
+ respVO.setCurrentItems(BeanUtils.toBean(projectReportCurrentItemMapper.selectListByReportId(report.getId()),
+ ProjectReportItemRespVO.class));
+ respVO.setNextItems(BeanUtils.toBean(projectReportNextItemMapper.selectListByReportId(report.getId()),
+ ProjectReportItemRespVO.class));
+ }
+ return respVO;
+ }
+
+ private void fillPersonalBase(WeeklyReportRespVO respVO, CurrentUserProfile profile) {
+ respVO.setReporterId(profile.userId());
+ respVO.setReporterName(profile.userName());
+ respVO.setReporterDeptName(profile.deptName());
+ respVO.setReporterPostName(profile.postName());
+ respVO.setSupervisorUserId(profile.directManagerId());
+ respVO.setSupervisorName(profile.directManagerName());
+ }
+
+ private void fillPersonalBase(MonthlyReportRespVO respVO, CurrentUserProfile profile) {
+ respVO.setReporterId(profile.userId());
+ respVO.setReporterName(profile.userName());
+ respVO.setReporterDeptName(profile.deptName());
+ respVO.setReporterPostName(profile.postName());
+ respVO.setSupervisorUserId(profile.directManagerId());
+ respVO.setSupervisorName(profile.directManagerName());
+ }
+
+ private void applyStatusView(WeeklyReportRespVO respVO, ObjectStatusModelDO statusModel) {
+ respVO.setStatusCode(statusModel.getStatusCode());
+ respVO.setStatusName(statusModel.getStatusName());
+ respVO.setAllowEdit(Boolean.TRUE.equals(statusModel.getAllowEdit()));
+ respVO.setTerminal(Boolean.TRUE.equals(statusModel.getTerminalFlag()));
+ }
+
+ private void applyStatusView(MonthlyReportRespVO respVO, ObjectStatusModelDO statusModel) {
+ respVO.setStatusCode(statusModel.getStatusCode());
+ respVO.setStatusName(statusModel.getStatusName());
+ respVO.setAllowEdit(Boolean.TRUE.equals(statusModel.getAllowEdit()));
+ respVO.setTerminal(Boolean.TRUE.equals(statusModel.getTerminalFlag()));
+ }
+
+ private void applyStatusView(ProjectReportRespVO respVO, ObjectStatusModelDO statusModel) {
+ respVO.setStatusCode(statusModel.getStatusCode());
+ respVO.setStatusName(statusModel.getStatusName());
+ respVO.setAllowEdit(Boolean.TRUE.equals(statusModel.getAllowEdit()));
+ respVO.setTerminal(Boolean.TRUE.equals(statusModel.getTerminalFlag()));
+ }
+
+ private WorkReportStatusDictRespVO toStatusDictRespVO(ObjectStatusModelDO statusModel) {
+ WorkReportStatusDictRespVO respVO = new WorkReportStatusDictRespVO();
+ respVO.setStatusCode(statusModel.getStatusCode());
+ respVO.setStatusName(statusModel.getStatusName());
+ respVO.setSort(statusModel.getSort());
+ respVO.setInitialFlag(statusModel.getInitialFlag());
+ respVO.setTerminalFlag(statusModel.getTerminalFlag());
+ respVO.setAllowEdit(statusModel.getAllowEdit());
+ return respVO;
+ }
+
+ private void validateTravelSegments(LocalDate periodStartDate, LocalDate periodEndDate,
+ List travelSegments) {
+ for (WeeklyReportTravelSegmentReqVO item : defaultList(travelSegments)) {
+ if (item.getStartDate() == null || item.getEndDate() == null) {
+ continue;
+ }
+ if (item.getEndDate().isBefore(item.getStartDate())) {
+ throw invalidParamException("出差结束日期不能早于开始日期");
+ }
+ if (item.getStartDate().isBefore(periodStartDate) || item.getEndDate().isAfter(periodEndDate)) {
+ throw invalidParamException("出差分段必须落在报告周期内");
+ }
+ }
+ }
+
+ private BigDecimal sumReviewWorkHours(List items) {
+ BigDecimal total = BigDecimal.ZERO;
+ for (PersonalReportReviewItemReqVO item : defaultList(items)) {
+ if (item.getWorkHours() != null) {
+ total = total.add(item.getWorkHours());
+ }
+ }
+ return total.compareTo(BigDecimal.ZERO) == 0 ? null : total;
+ }
+
+ private BigDecimal sumProjectCurrentWorkHours(List