feat(工作报告团队视角): 工作报告现在可以查看团队视角了(查看下属)。
fix(工作报告定时生成): 修复工作报告定时生成带来的一些问题。
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
package com.njcn.rdms.module.project.constant;
|
||||
|
||||
/**
|
||||
* 团队视角常量。
|
||||
*/
|
||||
public final class TeamDashboardConstants {
|
||||
|
||||
private TeamDashboardConstants() {
|
||||
}
|
||||
|
||||
public static final String PERMISSION = "project:work-report:team-dashboard";
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.team;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.module.project.constant.TeamDashboardConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.team.vo.TeamOvertimeSummaryReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.team.vo.TeamOvertimeSummaryRespVO;
|
||||
import com.njcn.rdms.module.project.service.overtime.team.TeamOvertimeService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
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 static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 团队加班申请")
|
||||
@RestController
|
||||
@RequestMapping("/project/overtime-applications/team")
|
||||
@Validated
|
||||
public class TeamOvertimeController {
|
||||
|
||||
@Resource
|
||||
private TeamOvertimeService teamOvertimeService;
|
||||
|
||||
@GetMapping("/summary")
|
||||
@Operation(summary = "获取团队加班申请统计")
|
||||
@PreAuthorize("@ss.hasPermission('" + TeamDashboardConstants.PERMISSION + "')")
|
||||
public CommonResult<TeamOvertimeSummaryRespVO> getSummary(@Valid TeamOvertimeSummaryReqVO reqVO) {
|
||||
return success(teamOvertimeService.getSummary(reqVO));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.team.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 团队加班申请统计 Request VO")
|
||||
@Data
|
||||
public class TeamOvertimeSummaryReqVO {
|
||||
|
||||
@Schema(description = "统计月份,不传默认当前月", example = "2026-06")
|
||||
private String month;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.team.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 团队加班申请统计 Response VO")
|
||||
@Data
|
||||
public class TeamOvertimeSummaryRespVO {
|
||||
|
||||
@Schema(description = "统计月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-06")
|
||||
private String month;
|
||||
|
||||
@Schema(description = "申请总数", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
|
||||
private Integer totalApplicationCount;
|
||||
|
||||
@Schema(description = "待审批数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||
private Integer pendingCount;
|
||||
|
||||
@Schema(description = "已通过数", requiredMode = Schema.RequiredMode.REQUIRED, example = "7")
|
||||
private Integer approvedCount;
|
||||
|
||||
@Schema(description = "已退回数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer rejectedCount;
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
@@ -18,6 +19,9 @@ import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MON
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OvertimeApplicationPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "团队视角下的申请人用户编号列表")
|
||||
private List<Long> applicantIds;
|
||||
|
||||
@Schema(description = "关键词,匹配加班原因或加班内容", example = "上线")
|
||||
private String keyword;
|
||||
|
||||
|
||||
@@ -5,8 +5,13 @@ 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 MonthlyReportPageReqVO extends WorkReportBasePageReqVO {
|
||||
|
||||
@Schema(description = "团队视角下的填报人用户编号列表")
|
||||
private List<Long> reporterIds;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,16 @@ 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 ProjectReportPageReqVO extends WorkReportBasePageReqVO {
|
||||
|
||||
@Schema(description = "团队视角下的项目负责人用户编号列表")
|
||||
private List<Long> projectOwnerIds;
|
||||
|
||||
@Schema(description = "项目编号")
|
||||
private Long projectId;
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.team;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.module.project.constant.TeamDashboardConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportRemindReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportRemindRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportSummaryReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportSummaryRespVO;
|
||||
import com.njcn.rdms.module.project.service.workreport.team.TeamWorkReportService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
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.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 团队工作报告")
|
||||
@RestController
|
||||
@RequestMapping("/project/work-reports/team")
|
||||
@Validated
|
||||
public class TeamWorkReportController {
|
||||
|
||||
@Resource
|
||||
private TeamWorkReportService teamWorkReportService;
|
||||
|
||||
@GetMapping("/summary")
|
||||
@Operation(summary = "获取团队工作报告统计")
|
||||
@PreAuthorize("@ss.hasPermission('" + TeamDashboardConstants.PERMISSION + "')")
|
||||
public CommonResult<TeamReportSummaryRespVO> getSummary(@Valid TeamReportSummaryReqVO reqVO) {
|
||||
return success(teamWorkReportService.getSummary(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/remind")
|
||||
@Operation(summary = "催办团队工作报告")
|
||||
@PreAuthorize("@ss.hasPermission('" + TeamDashboardConstants.PERMISSION + "')")
|
||||
public CommonResult<TeamReportRemindRespVO> remind(@Valid @RequestBody TeamReportRemindReqVO reqVO) {
|
||||
return success(teamWorkReportService.remind(reqVO));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.team.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 团队工作报告催办 Request VO")
|
||||
@Data
|
||||
public class TeamReportRemindReqVO {
|
||||
|
||||
@Schema(description = "报告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "weekly")
|
||||
@NotBlank(message = "报告类型不能为空")
|
||||
private String reportType;
|
||||
|
||||
@Schema(description = "周期主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "weekly-2026-06-08-2026-06-14")
|
||||
@NotBlank(message = "周期主键不能为空")
|
||||
private String periodKey;
|
||||
|
||||
@Schema(description = "催办用户 ID 列表;不传则催办全部待提交用户")
|
||||
private List<Long> userIds;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.team.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 团队工作报告催办 Response VO")
|
||||
@Data
|
||||
public class TeamReportRemindRespVO {
|
||||
|
||||
@Schema(description = "实际催办人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "4")
|
||||
private Integer remindedCount;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.team.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 团队工作报告统计 Request VO")
|
||||
@Data
|
||||
public class TeamReportSummaryReqVO {
|
||||
|
||||
@Schema(description = "报告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "weekly")
|
||||
@NotBlank(message = "报告类型不能为空")
|
||||
private String reportType;
|
||||
|
||||
@Schema(description = "周期主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "weekly-2026-06-08-2026-06-14")
|
||||
@NotBlank(message = "周期主键不能为空")
|
||||
private String periodKey;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.workreport.team.vo;
|
||||
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 团队工作报告统计 Response VO")
|
||||
@Data
|
||||
public class TeamReportSummaryRespVO {
|
||||
|
||||
@Schema(description = "应填人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "18")
|
||||
private Integer totalShouldSubmit;
|
||||
|
||||
@Schema(description = "已提交人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
|
||||
private Integer submittedCount;
|
||||
|
||||
@Schema(description = "未提交人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "6")
|
||||
private Integer unsubmittedCount;
|
||||
|
||||
@Schema(description = "待审批人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
private Integer pendingApprovalCount;
|
||||
|
||||
@Schema(description = "未提交人员列表")
|
||||
private List<PendingUser> unsubmittedUsers;
|
||||
|
||||
@Schema(description = "未提交人员")
|
||||
@Data
|
||||
public static class PendingUser {
|
||||
|
||||
@Schema(description = "用户 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2042074259501088770")
|
||||
@com.fasterxml.jackson.databind.annotation.JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "戴坤")
|
||||
private String userNickname;
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,16 @@ 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 WeeklyReportPageReqVO extends WorkReportBasePageReqVO {
|
||||
|
||||
@Schema(description = "团队视角下的填报人用户编号列表")
|
||||
private List<Long> reporterIds;
|
||||
|
||||
@Schema(description = "是否出差")
|
||||
private Boolean isBusinessTrip;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface OvertimeApplicationMapper extends BaseMapperX<OvertimeApplicationDO> {
|
||||
@@ -22,6 +23,22 @@ public interface OvertimeApplicationMapper extends BaseMapperX<OvertimeApplicati
|
||||
return selectPage(reqVO, queryWrapper);
|
||||
}
|
||||
|
||||
default PageResult<OvertimeApplicationDO> selectMyPage(Collection<Long> applicantIds,
|
||||
OvertimeApplicationPageReqVO reqVO,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (applicantIds == null || applicantIds.isEmpty() || (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
|
||||
return new PageResult<>(List.of(), 0L);
|
||||
}
|
||||
LambdaQueryWrapperX<OvertimeApplicationDO> queryWrapper = buildPageQuery(reqVO);
|
||||
queryWrapper.in(OvertimeApplicationDO::getApplicantId, applicantIds);
|
||||
if (allowedStatusCodes != null) {
|
||||
queryWrapper.in(OvertimeApplicationDO::getStatusCode, allowedStatusCodes);
|
||||
}
|
||||
queryWrapper.orderByDesc(OvertimeApplicationDO::getSubmitTime)
|
||||
.orderByDesc(OvertimeApplicationDO::getId);
|
||||
return selectPage(reqVO, queryWrapper);
|
||||
}
|
||||
|
||||
default PageResult<OvertimeApplicationDO> selectApprovalPage(Long approverId, OvertimeApplicationPageReqVO reqVO) {
|
||||
LambdaQueryWrapperX<OvertimeApplicationDO> queryWrapper = buildPageQuery(reqVO);
|
||||
queryWrapper.eq(OvertimeApplicationDO::getApproverId, approverId);
|
||||
|
||||
@@ -75,6 +75,20 @@ public interface ProjectMapper extends BaseMapperX<ProjectDO> {
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
|
||||
default List<ProjectDO> selectListByManagerUserIdsAndStatusCodesNotIn(Collection<Long> managerUserIds,
|
||||
Collection<String> statusCodes) {
|
||||
if (managerUserIds == null || managerUserIds.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
LambdaQueryWrapperX<ProjectDO> queryWrapper = new LambdaQueryWrapperX<ProjectDO>()
|
||||
.in(ProjectDO::getManagerUserId, managerUserIds)
|
||||
.orderByDesc(BaseDO::getCreateTime);
|
||||
if (statusCodes != null && !statusCodes.isEmpty()) {
|
||||
queryWrapper.notIn(ProjectDO::getStatusCode, statusCodes);
|
||||
}
|
||||
return selectList(queryWrapper);
|
||||
}
|
||||
|
||||
default int updateStatusByIdAndStatus(Long id, String fromStatus, String toStatus, String lastStatusReason) {
|
||||
ProjectDO update = new ProjectDO();
|
||||
update.setStatusCode(toStatus);
|
||||
|
||||
@@ -23,6 +23,23 @@ public interface MonthlyReportMapper extends BaseMapperX<MonthlyReportDO> {
|
||||
.eq(MonthlyReportDO::getPeriodKey, periodKey));
|
||||
}
|
||||
|
||||
default List<MonthlyReportDO> selectListByReporterIdsAndPeriodKey(Collection<Long> reporterIds, String periodKey,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty() || !StringUtils.hasText(periodKey)) {
|
||||
return List.of();
|
||||
}
|
||||
LambdaQueryWrapperX<MonthlyReportDO> wrapper = new LambdaQueryWrapperX<MonthlyReportDO>()
|
||||
.in(MonthlyReportDO::getReporterId, reporterIds)
|
||||
.eq(MonthlyReportDO::getPeriodKey, periodKey);
|
||||
if (allowedStatusCodes != null) {
|
||||
if (allowedStatusCodes.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
wrapper.in(MonthlyReportDO::getStatusCode, allowedStatusCodes);
|
||||
}
|
||||
return selectList(wrapper);
|
||||
}
|
||||
|
||||
default PageResult<MonthlyReportDO> selectReporterPage(Long reporterId, MonthlyReportPageReqVO reqVO) {
|
||||
return selectReporterPage(reporterId, reqVO, null);
|
||||
}
|
||||
@@ -42,6 +59,21 @@ public interface MonthlyReportMapper extends BaseMapperX<MonthlyReportDO> {
|
||||
return selectPage(reqVO, wrapper);
|
||||
}
|
||||
|
||||
default PageResult<MonthlyReportDO> selectReporterPage(Collection<Long> reporterIds, MonthlyReportPageReqVO reqVO,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty() || (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
|
||||
return new PageResult<>(List.of(), 0L);
|
||||
}
|
||||
LambdaQueryWrapperX<MonthlyReportDO> wrapper = buildPageQuery(reqVO)
|
||||
.in(MonthlyReportDO::getReporterId, reporterIds)
|
||||
.orderByDesc(MonthlyReportDO::getPeriodStartDate)
|
||||
.orderByDesc(MonthlyReportDO::getId);
|
||||
if (allowedStatusCodes != null) {
|
||||
wrapper.in(MonthlyReportDO::getStatusCode, allowedStatusCodes);
|
||||
}
|
||||
return selectPage(reqVO, wrapper);
|
||||
}
|
||||
|
||||
default PageResult<MonthlyReportDO> selectApprovalPage(Long supervisorUserId, MonthlyReportPageReqVO reqVO) {
|
||||
LambdaQueryWrapperX<MonthlyReportDO> wrapper = buildPageQuery(reqVO)
|
||||
.eq(MonthlyReportDO::getSupervisorUserId, supervisorUserId)
|
||||
|
||||
@@ -29,6 +29,24 @@ public interface ProjectReportMapper extends BaseMapperX<ProjectReportDO> {
|
||||
.eq(ProjectReportDO::getProjectOwnerId, projectOwnerId));
|
||||
}
|
||||
|
||||
default List<ProjectReportDO> selectListByProjectOwnerIdsAndPeriodKey(Collection<Long> projectOwnerIds,
|
||||
String periodKey,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (projectOwnerIds == null || projectOwnerIds.isEmpty() || !StringUtils.hasText(periodKey)) {
|
||||
return List.of();
|
||||
}
|
||||
LambdaQueryWrapperX<ProjectReportDO> wrapper = new LambdaQueryWrapperX<ProjectReportDO>()
|
||||
.in(ProjectReportDO::getProjectOwnerId, projectOwnerIds)
|
||||
.eq(ProjectReportDO::getPeriodKey, periodKey);
|
||||
if (allowedStatusCodes != null) {
|
||||
if (allowedStatusCodes.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
wrapper.in(ProjectReportDO::getStatusCode, allowedStatusCodes);
|
||||
}
|
||||
return selectList(wrapper);
|
||||
}
|
||||
|
||||
default PageResult<ProjectReportDO> selectReporterPage(Long reporterId, ProjectReportPageReqVO reqVO) {
|
||||
return selectReporterPage(reporterId, reqVO, null);
|
||||
}
|
||||
@@ -48,6 +66,21 @@ public interface ProjectReportMapper extends BaseMapperX<ProjectReportDO> {
|
||||
return selectPage(reqVO, wrapper);
|
||||
}
|
||||
|
||||
default PageResult<ProjectReportDO> selectReporterPage(Collection<Long> reporterIds, ProjectReportPageReqVO reqVO,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty() || (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
|
||||
return new PageResult<>(List.of(), 0L);
|
||||
}
|
||||
LambdaQueryWrapperX<ProjectReportDO> wrapper = buildPageQuery(reqVO)
|
||||
.in(ProjectReportDO::getProjectOwnerId, reporterIds)
|
||||
.orderByDesc(ProjectReportDO::getPeriodStartDate)
|
||||
.orderByDesc(ProjectReportDO::getId);
|
||||
if (allowedStatusCodes != null) {
|
||||
wrapper.in(ProjectReportDO::getStatusCode, allowedStatusCodes);
|
||||
}
|
||||
return selectPage(reqVO, wrapper);
|
||||
}
|
||||
|
||||
default PageResult<ProjectReportDO> selectApprovalPage(Long supervisorUserId, ProjectReportPageReqVO reqVO) {
|
||||
LambdaQueryWrapperX<ProjectReportDO> wrapper = buildPageQuery(reqVO)
|
||||
.eq(ProjectReportDO::getSupervisorUserId, supervisorUserId)
|
||||
|
||||
@@ -23,6 +23,23 @@ public interface WeeklyReportMapper extends BaseMapperX<WeeklyReportDO> {
|
||||
.eq(WeeklyReportDO::getPeriodKey, periodKey));
|
||||
}
|
||||
|
||||
default List<WeeklyReportDO> selectListByReporterIdsAndPeriodKey(Collection<Long> reporterIds, String periodKey,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty() || !StringUtils.hasText(periodKey)) {
|
||||
return List.of();
|
||||
}
|
||||
LambdaQueryWrapperX<WeeklyReportDO> wrapper = new LambdaQueryWrapperX<WeeklyReportDO>()
|
||||
.in(WeeklyReportDO::getReporterId, reporterIds)
|
||||
.eq(WeeklyReportDO::getPeriodKey, periodKey);
|
||||
if (allowedStatusCodes != null) {
|
||||
if (allowedStatusCodes.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
wrapper.in(WeeklyReportDO::getStatusCode, allowedStatusCodes);
|
||||
}
|
||||
return selectList(wrapper);
|
||||
}
|
||||
|
||||
default PageResult<WeeklyReportDO> selectReporterPage(Long reporterId, WeeklyReportPageReqVO reqVO) {
|
||||
return selectReporterPage(reporterId, reqVO, null);
|
||||
}
|
||||
@@ -42,6 +59,21 @@ public interface WeeklyReportMapper extends BaseMapperX<WeeklyReportDO> {
|
||||
return selectPage(reqVO, wrapper);
|
||||
}
|
||||
|
||||
default PageResult<WeeklyReportDO> selectReporterPage(Collection<Long> reporterIds, WeeklyReportPageReqVO reqVO,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty() || (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
|
||||
return new PageResult<>(List.of(), 0L);
|
||||
}
|
||||
LambdaQueryWrapperX<WeeklyReportDO> wrapper = buildPageQuery(reqVO)
|
||||
.in(WeeklyReportDO::getReporterId, reporterIds)
|
||||
.orderByDesc(WeeklyReportDO::getPeriodStartDate)
|
||||
.orderByDesc(WeeklyReportDO::getId);
|
||||
if (allowedStatusCodes != null) {
|
||||
wrapper.in(WeeklyReportDO::getStatusCode, allowedStatusCodes);
|
||||
}
|
||||
return selectPage(reqVO, wrapper);
|
||||
}
|
||||
|
||||
default PageResult<WeeklyReportDO> selectApprovalPage(Long supervisorUserId, WeeklyReportPageReqVO reqVO) {
|
||||
LambdaQueryWrapperX<WeeklyReportDO> wrapper = buildPageQuery(reqVO)
|
||||
.eq(WeeklyReportDO::getSupervisorUserId, supervisorUserId)
|
||||
|
||||
@@ -25,4 +25,7 @@ public class NotifyTemplateCodeConstants {
|
||||
/** 逾期提醒-协办人:等级低一等的弱化文案 */
|
||||
public static final String DUE_ALERT_OVERDUE_ASSIGNEE = "due_alert_overdue_assignee";
|
||||
|
||||
/** 工作报告团队催办:主管催办下属提交指定周期工作报告 */
|
||||
public static final String WORK_REPORT_TEAM_REMIND = "work_report_team_remind";
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ 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.enums.ErrorCodeConstants;
|
||||
import com.njcn.rdms.module.project.service.status.StatusActionTextResolver;
|
||||
import com.njcn.rdms.module.project.service.team.TeamDashboardAccessService;
|
||||
import com.njcn.rdms.module.system.api.user.AdminUserApi;
|
||||
import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -46,6 +47,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -55,6 +57,11 @@ import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil
|
||||
@Service
|
||||
public class OvertimeApplicationServiceImpl implements OvertimeApplicationService {
|
||||
|
||||
private static final List<String> TEAM_VISIBLE_STATUS_CODES = List.of(
|
||||
OvertimeApplicationConstants.STATUS_PENDING,
|
||||
OvertimeApplicationConstants.STATUS_APPROVED,
|
||||
OvertimeApplicationConstants.STATUS_REJECTED);
|
||||
|
||||
@Resource
|
||||
private OvertimeApplicationMapper overtimeApplicationMapper;
|
||||
@Resource
|
||||
@@ -71,6 +78,8 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
|
||||
private StatusActionTextResolver statusActionTextResolver;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private TeamDashboardAccessService teamDashboardAccessService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -185,7 +194,13 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
|
||||
@Override
|
||||
public PageResult<OvertimeApplicationRespVO> getMyPage(OvertimeApplicationPageReqVO reqVO) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
PageResult<OvertimeApplicationDO> page = overtimeApplicationMapper.selectMyPage(loginUserId, reqVO);
|
||||
PageResult<OvertimeApplicationDO> page;
|
||||
if (reqVO.getApplicantIds() != null) {
|
||||
List<Long> applicantIds = teamDashboardAccessService.resolveRequestedSubordinateUserIds(reqVO.getApplicantIds());
|
||||
page = overtimeApplicationMapper.selectMyPage(applicantIds, reqVO, TEAM_VISIBLE_STATUS_CODES);
|
||||
} else {
|
||||
page = overtimeApplicationMapper.selectMyPage(loginUserId, reqVO);
|
||||
}
|
||||
return BeanUtils.toBean(page, OvertimeApplicationRespVO.class, this::applyStatusView);
|
||||
}
|
||||
|
||||
@@ -368,7 +383,8 @@ public class OvertimeApplicationServiceImpl implements OvertimeApplicationServic
|
||||
OvertimeApplicationDO application = validateApplicationExists(id);
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
if (!Objects.equals(application.getApplicantId(), loginUserId)
|
||||
&& !Objects.equals(application.getApproverId(), loginUserId)) {
|
||||
&& !Objects.equals(application.getApproverId(), loginUserId)
|
||||
&& !teamDashboardAccessService.canReadSubordinateUser(application.getApplicantId())) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_READ_FORBIDDEN);
|
||||
}
|
||||
return application;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.njcn.rdms.module.project.service.overtime.team;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.team.vo.TeamOvertimeSummaryReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.team.vo.TeamOvertimeSummaryRespVO;
|
||||
|
||||
public interface TeamOvertimeService {
|
||||
|
||||
TeamOvertimeSummaryRespVO getSummary(TeamOvertimeSummaryReqVO reqVO);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.njcn.rdms.module.project.service.overtime.team;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.PageParam;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.module.project.constant.OvertimeApplicationConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.team.vo.TeamOvertimeSummaryReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.team.vo.TeamOvertimeSummaryRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO;
|
||||
import com.njcn.rdms.module.project.service.overtime.OvertimeApplicationService;
|
||||
import com.njcn.rdms.module.project.service.team.TeamDashboardAccessService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.YearMonth;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
|
||||
|
||||
@Service
|
||||
public class TeamOvertimeServiceImpl implements TeamOvertimeService {
|
||||
|
||||
private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||
|
||||
@Resource
|
||||
private TeamDashboardAccessService teamDashboardAccessService;
|
||||
@Resource
|
||||
private OvertimeApplicationService overtimeApplicationService;
|
||||
|
||||
@Override
|
||||
public TeamOvertimeSummaryRespVO getSummary(TeamOvertimeSummaryReqVO reqVO) {
|
||||
teamDashboardAccessService.validateTeamDashboardPermission();
|
||||
List<Long> subordinateIds = teamDashboardAccessService.getAllSubordinateUserIds();
|
||||
YearMonth month = parseMonth(reqVO == null ? null : reqVO.getMonth());
|
||||
|
||||
TeamOvertimeSummaryRespVO respVO = new TeamOvertimeSummaryRespVO();
|
||||
respVO.setMonth(month.format(MONTH_FORMATTER));
|
||||
if (subordinateIds.isEmpty()) {
|
||||
respVO.setTotalApplicationCount(0);
|
||||
respVO.setPendingCount(0);
|
||||
respVO.setApprovedCount(0);
|
||||
respVO.setRejectedCount(0);
|
||||
return respVO;
|
||||
}
|
||||
|
||||
OvertimeApplicationPageReqVO pageReqVO = new OvertimeApplicationPageReqVO();
|
||||
pageReqVO.setApplicantIds(subordinateIds);
|
||||
pageReqVO.setPageNo(1);
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
pageReqVO.setOvertimeDate(new LocalDate[]{month.atDay(1), month.atEndOfMonth()});
|
||||
PageResult<OvertimeApplicationRespVO> page = overtimeApplicationService.getMyPage(pageReqVO);
|
||||
|
||||
int pendingCount = 0;
|
||||
int approvedCount = 0;
|
||||
int rejectedCount = 0;
|
||||
for (OvertimeApplicationRespVO item : page.getList()) {
|
||||
if (OvertimeApplicationConstants.STATUS_PENDING.equals(item.getStatusCode())) {
|
||||
pendingCount++;
|
||||
} else if (OvertimeApplicationConstants.STATUS_APPROVED.equals(item.getStatusCode())) {
|
||||
approvedCount++;
|
||||
} else if (OvertimeApplicationConstants.STATUS_REJECTED.equals(item.getStatusCode())) {
|
||||
rejectedCount++;
|
||||
}
|
||||
}
|
||||
respVO.setTotalApplicationCount(page.getList().size());
|
||||
respVO.setPendingCount(pendingCount);
|
||||
respVO.setApprovedCount(approvedCount);
|
||||
respVO.setRejectedCount(rejectedCount);
|
||||
return respVO;
|
||||
}
|
||||
|
||||
private YearMonth parseMonth(String month) {
|
||||
if (!StringUtils.hasText(month)) {
|
||||
return YearMonth.now();
|
||||
}
|
||||
try {
|
||||
return YearMonth.parse(month, MONTH_FORMATTER);
|
||||
} catch (DateTimeParseException ex) {
|
||||
throw invalidParamException("统计月份格式不正确,应为 yyyy-MM");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.njcn.rdms.module.project.service.team;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface TeamDashboardAccessService {
|
||||
|
||||
/**
|
||||
* 校验当前用户具备团队视角权限。
|
||||
*/
|
||||
void validateTeamDashboardPermission();
|
||||
|
||||
/**
|
||||
* 获取当前登录用户全部有效下属(不含本人)。
|
||||
*
|
||||
* @return 下属 ID 列表
|
||||
*/
|
||||
List<Long> getAllSubordinateUserIds();
|
||||
|
||||
/**
|
||||
* 校验并解析团队查询的目标用户 ID。
|
||||
*
|
||||
* @param candidateUserIds 前端传入的目标用户 ID;为空表示全部下属
|
||||
* @return 校验后的目标用户 ID(不含本人)
|
||||
*/
|
||||
List<Long> resolveRequestedSubordinateUserIds(Collection<Long> candidateUserIds);
|
||||
|
||||
/**
|
||||
* 判断当前登录用户是否可读取指定工作报告/加班申请所属人员的数据。
|
||||
*
|
||||
* @param userId 目标人员 ID
|
||||
* @return 是否可读
|
||||
*/
|
||||
boolean canReadSubordinateUser(Long userId);
|
||||
|
||||
/**
|
||||
* 获取当前登录用户下属集合。
|
||||
*
|
||||
* @return 下属集合
|
||||
*/
|
||||
Set<Long> getSubordinateUserIdSet();
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.njcn.rdms.module.project.service.team;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.security.core.service.SecurityFrameworkService;
|
||||
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.njcn.rdms.module.project.constant.TeamDashboardConstants;
|
||||
import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
|
||||
@Service
|
||||
public class TeamDashboardAccessServiceImpl implements TeamDashboardAccessService {
|
||||
|
||||
@Resource
|
||||
private UserManagementRelationApi userManagementRelationApi;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private SecurityFrameworkService securityFrameworkService;
|
||||
|
||||
@Override
|
||||
public void validateTeamDashboardPermission() {
|
||||
if (!securityFrameworkService.hasPermission(TeamDashboardConstants.PERMISSION)) {
|
||||
throw exception(ErrorCodeConstants.TEAM_DASHBOARD_PERMISSION_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getAllSubordinateUserIds() {
|
||||
return new ArrayList<>(getSubordinateUserIdSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> resolveRequestedSubordinateUserIds(Collection<Long> candidateUserIds) {
|
||||
validateTeamDashboardPermission();
|
||||
Set<Long> allSubordinates = getSubordinateUserIdSet();
|
||||
if (allSubordinates.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (candidateUserIds == null || candidateUserIds.isEmpty()) {
|
||||
return new ArrayList<>(allSubordinates);
|
||||
}
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
LinkedHashSet<Long> resolved = new LinkedHashSet<>();
|
||||
for (Long candidateUserId : candidateUserIds) {
|
||||
if (candidateUserId == null || Objects.equals(candidateUserId, loginUserId)) {
|
||||
continue;
|
||||
}
|
||||
if (!allSubordinates.contains(candidateUserId)) {
|
||||
throw exception(ErrorCodeConstants.TEAM_DASHBOARD_SUBORDINATE_SCOPE_INVALID);
|
||||
}
|
||||
resolved.add(candidateUserId);
|
||||
}
|
||||
return new ArrayList<>(resolved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReadSubordinateUser(Long userId) {
|
||||
if (userId == null) {
|
||||
return false;
|
||||
}
|
||||
if (!securityFrameworkService.hasPermission(TeamDashboardConstants.PERMISSION)) {
|
||||
return false;
|
||||
}
|
||||
return getSubordinateUserIdSet().contains(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> getSubordinateUserIdSet() {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
if (loginUserId == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
CommonResult<Set<Long>> result = userManagementRelationApi.getAllSubordinateUserIds(loginUserId);
|
||||
Set<Long> rawIds = result == null || result.getCheckedData() == null
|
||||
? Collections.emptySet()
|
||||
: result.getCheckedData();
|
||||
if (rawIds.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
LinkedHashSet<Long> uniqueIds = new LinkedHashSet<>(rawIds);
|
||||
uniqueIds.remove(loginUserId);
|
||||
if (uniqueIds.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
CommonResult<List<AdminUserRespDTO>> userResult = adminUserApi.getUserList(uniqueIds);
|
||||
List<AdminUserRespDTO> users = userResult == null || userResult.getCheckedData() == null
|
||||
? Collections.emptyList()
|
||||
: userResult.getCheckedData();
|
||||
LinkedHashSet<Long> availableIds = new LinkedHashSet<>();
|
||||
for (AdminUserRespDTO user : users) {
|
||||
if (user == null || user.getId() == null || !Objects.equals(user.getStatus(), 0)) {
|
||||
continue;
|
||||
}
|
||||
availableIds.add(user.getId());
|
||||
}
|
||||
return availableIds;
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,23 @@ import com.njcn.rdms.framework.common.util.json.JsonUtils;
|
||||
import com.njcn.rdms.framework.security.core.LoginUser;
|
||||
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.PersonalReportPlanItemReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportPlanItemRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportReviewItemReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.common.vo.PersonalReportReviewItemRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.monthly.vo.MonthlyReportDefaultDraftReqVO;
|
||||
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.controller.admin.workreport.project.vo.ProjectReportDefaultDraftReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportItemReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.project.vo.ProjectReportItemRespVO;
|
||||
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.controller.admin.workreport.weekly.vo.WeeklyReportDefaultDraftReqVO;
|
||||
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.controller.admin.workreport.weekly.vo.WeeklyReportTravelSegmentReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.weekly.vo.WeeklyReportTravelSegmentRespVO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.job.JobRunLogDO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.project.ProjectDO;
|
||||
import com.njcn.rdms.module.project.dal.mysql.job.JobRunLogMapper;
|
||||
@@ -20,6 +34,7 @@ import com.njcn.rdms.module.project.dal.mysql.workreport.project.ProjectReportMa
|
||||
import com.njcn.rdms.module.project.dal.mysql.workreport.weekly.WeeklyReportMapper;
|
||||
import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
|
||||
import com.njcn.rdms.module.project.service.workreport.common.WorkReportCommonService;
|
||||
import com.njcn.rdms.module.project.service.workreport.defaultdraft.WorkReportDefaultDraftService;
|
||||
import com.njcn.rdms.module.system.api.dept.DeptApi;
|
||||
import com.njcn.rdms.module.system.api.dept.dto.DeptRespDTO;
|
||||
import com.njcn.rdms.module.system.api.user.AdminUserApi;
|
||||
@@ -73,6 +88,8 @@ public class WorkReportAutoGenerateService {
|
||||
@Resource
|
||||
private WorkReportCommonService workReportCommonService;
|
||||
@Resource
|
||||
private WorkReportDefaultDraftService workReportDefaultDraftService;
|
||||
@Resource
|
||||
private WeeklyReportMapper weeklyReportMapper;
|
||||
@Resource
|
||||
private MonthlyReportMapper monthlyReportMapper;
|
||||
@@ -179,15 +196,7 @@ public class WorkReportAutoGenerateService {
|
||||
result.setSkipCount(result.getSkipCount() + 1);
|
||||
return;
|
||||
}
|
||||
WeeklyReportSaveReqVO reqVO = new WeeklyReportSaveReqVO();
|
||||
reqVO.setPeriodKey(period.getPeriodKey());
|
||||
reqVO.setPeriodLabel(period.getPeriodLabel());
|
||||
reqVO.setPeriodStartDate(period.getPeriodStartDate());
|
||||
reqVO.setPeriodEndDate(period.getPeriodEndDate());
|
||||
reqVO.setIsBusinessTrip(Boolean.FALSE);
|
||||
reqVO.setReviewItems(Collections.emptyList());
|
||||
reqVO.setPlanItems(Collections.emptyList());
|
||||
reqVO.setTravelSegments(Collections.emptyList());
|
||||
WeeklyReportSaveReqVO reqVO = runAs(buildLoginUser(user), () -> buildWeeklySaveReqVOFromDefaultDraft(period));
|
||||
runAs(buildLoginUser(user), () -> workReportCommonService.createWeeklyReport(reqVO, user.getId()));
|
||||
result.setSuccessCount(result.getSuccessCount() + 1);
|
||||
}, () -> "userId=" + user.getId());
|
||||
@@ -200,13 +209,7 @@ public class WorkReportAutoGenerateService {
|
||||
result.setSkipCount(result.getSkipCount() + 1);
|
||||
return;
|
||||
}
|
||||
MonthlyReportSaveReqVO reqVO = new MonthlyReportSaveReqVO();
|
||||
reqVO.setPeriodKey(period.getPeriodKey());
|
||||
reqVO.setPeriodLabel(period.getPeriodLabel());
|
||||
reqVO.setPeriodStartDate(period.getPeriodStartDate());
|
||||
reqVO.setPeriodEndDate(period.getPeriodEndDate());
|
||||
reqVO.setReviewItems(Collections.emptyList());
|
||||
reqVO.setPlanItems(Collections.emptyList());
|
||||
MonthlyReportSaveReqVO reqVO = runAs(buildLoginUser(user), () -> buildMonthlySaveReqVOFromDefaultDraft(period));
|
||||
runAs(buildLoginUser(user), () -> workReportCommonService.createMonthlyReport(reqVO, user.getId()));
|
||||
result.setSuccessCount(result.getSuccessCount() + 1);
|
||||
}, () -> "userId=" + user.getId());
|
||||
@@ -220,20 +223,156 @@ public class WorkReportAutoGenerateService {
|
||||
result.setSkipCount(result.getSkipCount() + 1);
|
||||
return;
|
||||
}
|
||||
ProjectReportSaveReqVO reqVO = new ProjectReportSaveReqVO();
|
||||
reqVO.setProjectId(candidate.projectId());
|
||||
reqVO.setPeriodKey(period.getPeriodKey());
|
||||
reqVO.setPeriodLabel(period.getPeriodLabel());
|
||||
reqVO.setPeriodStartDate(period.getPeriodStartDate());
|
||||
reqVO.setPeriodEndDate(period.getPeriodEndDate());
|
||||
reqVO.setFlag(period.getFlag());
|
||||
reqVO.setCurrentItems(Collections.emptyList());
|
||||
reqVO.setNextItems(Collections.emptyList());
|
||||
ProjectReportSaveReqVO reqVO = runAs(buildLoginUser(candidate.user()),
|
||||
() -> buildProjectSaveReqVOFromDefaultDraft(candidate.projectId(), period));
|
||||
runAs(buildLoginUser(candidate.user()), () -> workReportCommonService.createProjectReport(reqVO, candidate.user().getId()));
|
||||
result.setSuccessCount(result.getSuccessCount() + 1);
|
||||
}, () -> "projectId=" + candidate.projectId() + ", userId=" + candidate.user().getId());
|
||||
}
|
||||
|
||||
private WeeklyReportSaveReqVO buildWeeklySaveReqVOFromDefaultDraft(AutoGenPeriod period) {
|
||||
WeeklyReportDefaultDraftReqVO draftReqVO = new WeeklyReportDefaultDraftReqVO();
|
||||
draftReqVO.setPeriodKey(period.getPeriodKey());
|
||||
draftReqVO.setPeriodLabel(period.getPeriodLabel());
|
||||
draftReqVO.setPeriodStartDate(period.getPeriodStartDate());
|
||||
draftReqVO.setPeriodEndDate(period.getPeriodEndDate());
|
||||
WeeklyReportRespVO draft = workReportDefaultDraftService.previewWeeklyDefaultDraft(draftReqVO);
|
||||
|
||||
WeeklyReportSaveReqVO reqVO = new WeeklyReportSaveReqVO();
|
||||
reqVO.setPeriodKey(draft.getPeriodKey());
|
||||
reqVO.setPeriodLabel(draft.getPeriodLabel());
|
||||
reqVO.setPeriodStartDate(draft.getPeriodStartDate());
|
||||
reqVO.setPeriodEndDate(draft.getPeriodEndDate());
|
||||
reqVO.setIsBusinessTrip(Boolean.TRUE.equals(draft.getIsBusinessTrip()));
|
||||
reqVO.setReviewItems(toReviewItemReqList(draft.getReviewItems()));
|
||||
reqVO.setPlanItems(toPlanItemReqList(draft.getPlanItems()));
|
||||
reqVO.setTravelSegments(toTravelSegmentReqList(draft.getTravelSegments()));
|
||||
return reqVO;
|
||||
}
|
||||
|
||||
private MonthlyReportSaveReqVO buildMonthlySaveReqVOFromDefaultDraft(AutoGenPeriod period) {
|
||||
MonthlyReportDefaultDraftReqVO draftReqVO = new MonthlyReportDefaultDraftReqVO();
|
||||
draftReqVO.setPeriodKey(period.getPeriodKey());
|
||||
draftReqVO.setPeriodLabel(period.getPeriodLabel());
|
||||
draftReqVO.setPeriodStartDate(period.getPeriodStartDate());
|
||||
draftReqVO.setPeriodEndDate(period.getPeriodEndDate());
|
||||
MonthlyReportRespVO draft = workReportDefaultDraftService.previewMonthlyDefaultDraft(draftReqVO);
|
||||
|
||||
MonthlyReportSaveReqVO reqVO = new MonthlyReportSaveReqVO();
|
||||
reqVO.setPeriodKey(draft.getPeriodKey());
|
||||
reqVO.setPeriodLabel(draft.getPeriodLabel());
|
||||
reqVO.setPeriodStartDate(draft.getPeriodStartDate());
|
||||
reqVO.setPeriodEndDate(draft.getPeriodEndDate());
|
||||
reqVO.setReviewItems(toReviewItemReqList(draft.getReviewItems()));
|
||||
reqVO.setPlanItems(toPlanItemReqList(draft.getPlanItems()));
|
||||
return reqVO;
|
||||
}
|
||||
|
||||
private ProjectReportSaveReqVO buildProjectSaveReqVOFromDefaultDraft(Long projectId, AutoGenPeriod period) {
|
||||
ProjectReportDefaultDraftReqVO draftReqVO = new ProjectReportDefaultDraftReqVO();
|
||||
draftReqVO.setPeriodKey(period.getPeriodKey());
|
||||
draftReqVO.setPeriodLabel(period.getPeriodLabel());
|
||||
draftReqVO.setPeriodStartDate(period.getPeriodStartDate());
|
||||
draftReqVO.setPeriodEndDate(period.getPeriodEndDate());
|
||||
draftReqVO.setFlag(period.getFlag());
|
||||
ProjectReportRespVO draft = workReportDefaultDraftService.previewProjectDefaultDraft(projectId, draftReqVO);
|
||||
|
||||
ProjectReportSaveReqVO reqVO = new ProjectReportSaveReqVO();
|
||||
reqVO.setProjectId(projectId);
|
||||
reqVO.setPeriodKey(draft.getPeriodKey());
|
||||
reqVO.setPeriodLabel(draft.getPeriodLabel());
|
||||
reqVO.setPeriodStartDate(draft.getPeriodStartDate());
|
||||
reqVO.setPeriodEndDate(draft.getPeriodEndDate());
|
||||
reqVO.setFlag(draft.getFlag());
|
||||
reqVO.setProjectStatusDesc(draft.getProjectStatusDesc());
|
||||
reqVO.setProjectProgressPlan(draft.getProjectProgressPlan());
|
||||
reqVO.setProjectKeyPoints(draft.getProjectKeyPoints());
|
||||
reqVO.setProjectProblems(draft.getProjectProblems());
|
||||
reqVO.setCurrentItems(toProjectItemReqList(draft.getCurrentItems()));
|
||||
reqVO.setNextItems(toProjectItemReqList(draft.getNextItems()));
|
||||
return reqVO;
|
||||
}
|
||||
|
||||
private List<PersonalReportReviewItemReqVO> toReviewItemReqList(List<PersonalReportReviewItemRespVO> source) {
|
||||
if (source == null || source.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<PersonalReportReviewItemReqVO> result = new ArrayList<>(source.size());
|
||||
for (PersonalReportReviewItemRespVO item : source) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
PersonalReportReviewItemReqVO target = new PersonalReportReviewItemReqVO();
|
||||
target.setItemNumber(item.getItemNumber());
|
||||
target.setItemTitle(item.getItemTitle());
|
||||
target.setWorkHours(item.getWorkHours());
|
||||
target.setContentText(item.getContentText());
|
||||
target.setContentJson(item.getContentJson());
|
||||
target.setReflectionText(item.getReflectionText());
|
||||
result.add(target);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<PersonalReportPlanItemReqVO> toPlanItemReqList(List<PersonalReportPlanItemRespVO> source) {
|
||||
if (source == null || source.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<PersonalReportPlanItemReqVO> result = new ArrayList<>(source.size());
|
||||
for (PersonalReportPlanItemRespVO item : source) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
PersonalReportPlanItemReqVO target = new PersonalReportPlanItemReqVO();
|
||||
target.setItemNumber(item.getItemNumber());
|
||||
target.setItemTitle(item.getItemTitle());
|
||||
target.setTargetText(item.getTargetText());
|
||||
target.setTargetJson(item.getTargetJson());
|
||||
target.setSupportNeed(item.getSupportNeed());
|
||||
result.add(target);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ProjectReportItemReqVO> toProjectItemReqList(List<ProjectReportItemRespVO> source) {
|
||||
if (source == null || source.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ProjectReportItemReqVO> result = new ArrayList<>(source.size());
|
||||
for (ProjectReportItemRespVO item : source) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
ProjectReportItemReqVO target = new ProjectReportItemReqVO();
|
||||
target.setItemTitle(item.getItemTitle());
|
||||
target.setWorkHours(item.getWorkHours());
|
||||
target.setPriorityCode(item.getPriorityCode());
|
||||
target.setProgressRate(item.getProgressRate());
|
||||
result.add(target);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<WeeklyReportTravelSegmentReqVO> toTravelSegmentReqList(List<WeeklyReportTravelSegmentRespVO> source) {
|
||||
if (source == null || source.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<WeeklyReportTravelSegmentReqVO> result = new ArrayList<>(source.size());
|
||||
for (WeeklyReportTravelSegmentRespVO item : source) {
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
WeeklyReportTravelSegmentReqVO target = new WeeklyReportTravelSegmentReqVO();
|
||||
target.setSort(item.getSort());
|
||||
target.setStartDate(item.getStartDate());
|
||||
target.setEndDate(item.getEndDate());
|
||||
target.setTravelDays(item.getTravelDays());
|
||||
target.setLocation(item.getLocation());
|
||||
result.add(target);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void executeWithLock(String reportType, String periodKey, String subjectKey, AutoGenResult result,
|
||||
Runnable action, Supplier<String> failContextSupplier) {
|
||||
RLock lock = redissonClient.getLock(String.format(LOCK_KEY_TEMPLATE, reportType, periodKey, subjectKey));
|
||||
@@ -346,29 +485,31 @@ public class WorkReportAutoGenerateService {
|
||||
private AutoGenPeriod buildWeeklyPeriod(LocalDate today) {
|
||||
LocalDate periodStartDate = today.minusDays(today.getDayOfWeek().getValue() - 1L);
|
||||
LocalDate periodEndDate = periodStartDate.plusDays(6);
|
||||
int week = periodStartDate.get(java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR);
|
||||
int year = periodStartDate.get(java.time.temporal.IsoFields.WEEK_BASED_YEAR);
|
||||
return new AutoGenPeriod()
|
||||
.setPeriodKey(String.format("%d-W%02d", year, week))
|
||||
.setPeriodLabel(String.format("%d 年第 %02d 周", year, week))
|
||||
.setPeriodKey(String.format("weekly-%s-%s", periodStartDate, periodEndDate))
|
||||
.setPeriodLabel(String.format("%s 至 %s", periodStartDate, periodEndDate))
|
||||
.setPeriodStartDate(periodStartDate)
|
||||
.setPeriodEndDate(periodEndDate);
|
||||
}
|
||||
|
||||
private AutoGenPeriod buildMonthlyPeriod(LocalDate today) {
|
||||
LocalDate periodStartDate = today.withDayOfMonth(1);
|
||||
LocalDate periodEndDate = today.withDayOfMonth(today.lengthOfMonth());
|
||||
return new AutoGenPeriod()
|
||||
.setPeriodKey(String.format("%d-%02d", today.getYear(), today.getMonthValue()))
|
||||
.setPeriodLabel(String.format("%d 年 %02d 月", today.getYear(), today.getMonthValue()))
|
||||
.setPeriodStartDate(today.withDayOfMonth(1))
|
||||
.setPeriodEndDate(today.withDayOfMonth(today.lengthOfMonth()));
|
||||
.setPeriodKey(String.format("monthly-%s-%s", periodStartDate, periodEndDate))
|
||||
.setPeriodLabel(String.format("%d-%02d", today.getYear(), today.getMonthValue()))
|
||||
.setPeriodStartDate(periodStartDate)
|
||||
.setPeriodEndDate(periodEndDate);
|
||||
}
|
||||
|
||||
private AutoGenPeriod buildProjectFirstHalfPeriod(LocalDate today) {
|
||||
LocalDate periodStartDate = today.withDayOfMonth(1);
|
||||
LocalDate periodEndDate = today.withDayOfMonth(15);
|
||||
return new AutoGenPeriod()
|
||||
.setPeriodKey(String.format("%d-%02d-01", today.getYear(), today.getMonthValue()))
|
||||
.setPeriodLabel(String.format("%d 年 %02d 月 上半月", today.getYear(), today.getMonthValue()))
|
||||
.setPeriodStartDate(today.withDayOfMonth(1))
|
||||
.setPeriodEndDate(today.withDayOfMonth(15))
|
||||
.setPeriodKey(String.format("project-%s-%s-1", periodStartDate, periodEndDate))
|
||||
.setPeriodLabel(String.format("%d-%02d 上半月", today.getYear(), today.getMonthValue()))
|
||||
.setPeriodStartDate(periodStartDate)
|
||||
.setPeriodEndDate(periodEndDate)
|
||||
.setFlag(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ import com.njcn.rdms.module.project.dal.mysql.workreport.weekly.WeeklyReportMapp
|
||||
import com.njcn.rdms.module.project.dal.mysql.workreport.weekly.WeeklyReportTravelSegmentMapper;
|
||||
import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
|
||||
import com.njcn.rdms.module.project.service.status.StatusActionTextResolver;
|
||||
import com.njcn.rdms.module.project.service.team.TeamDashboardAccessService;
|
||||
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;
|
||||
@@ -77,6 +78,10 @@ public class WorkReportCommonService {
|
||||
private static final List<String> ALLOW_DELETE_STATUSES = List.of(
|
||||
WorkReportConstants.STATUS_DRAFT,
|
||||
WorkReportConstants.STATUS_REJECTED);
|
||||
private static final List<String> TEAM_VISIBLE_STATUS_CODES = List.of(
|
||||
WorkReportConstants.STATUS_PENDING_APPROVAL,
|
||||
WorkReportConstants.STATUS_APPROVED,
|
||||
WorkReportConstants.STATUS_REJECTED);
|
||||
|
||||
@Resource
|
||||
private WeeklyReportMapper weeklyReportMapper;
|
||||
@@ -124,6 +129,8 @@ public class WorkReportCommonService {
|
||||
private ProjectMapper projectMapper;
|
||||
@Resource
|
||||
private UserObjectRoleMapper userObjectRoleMapper;
|
||||
@Resource
|
||||
private TeamDashboardAccessService teamDashboardAccessService;
|
||||
|
||||
public List<WorkReportStatusDictRespVO> getStatusDict() {
|
||||
return objectStatusModelMapper.selectListByObjectTypeEnabled(WorkReportConstants.STATUS_OBJECT_TYPE).stream()
|
||||
@@ -233,8 +240,13 @@ public class WorkReportCommonService {
|
||||
|
||||
public PageResult<WeeklyReportRespVO> getWeeklyReportPage(WeeklyReportPageReqVO reqVO) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
PageResult<WeeklyReportDO> pageResult = weeklyReportMapper.selectReporterPage(loginUserId, reqVO,
|
||||
getEnabledStatusCodes());
|
||||
PageResult<WeeklyReportDO> pageResult;
|
||||
if (reqVO.getReporterIds() != null) {
|
||||
List<Long> reporterIds = teamDashboardAccessService.resolveRequestedSubordinateUserIds(reqVO.getReporterIds());
|
||||
pageResult = weeklyReportMapper.selectReporterPage(reporterIds, reqVO, TEAM_VISIBLE_STATUS_CODES);
|
||||
} else {
|
||||
pageResult = weeklyReportMapper.selectReporterPage(loginUserId, reqVO, getEnabledStatusCodes());
|
||||
}
|
||||
return new PageResult<>(pageResult.getList().stream()
|
||||
.map(report -> toWeeklyRespVO(report, false))
|
||||
.collect(Collectors.toList()), pageResult.getTotal());
|
||||
@@ -392,8 +404,13 @@ public class WorkReportCommonService {
|
||||
|
||||
public PageResult<MonthlyReportRespVO> getMonthlyReportPage(MonthlyReportPageReqVO reqVO) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
PageResult<MonthlyReportDO> pageResult = monthlyReportMapper.selectReporterPage(loginUserId, reqVO,
|
||||
getEnabledStatusCodes());
|
||||
PageResult<MonthlyReportDO> pageResult;
|
||||
if (reqVO.getReporterIds() != null) {
|
||||
List<Long> reporterIds = teamDashboardAccessService.resolveRequestedSubordinateUserIds(reqVO.getReporterIds());
|
||||
pageResult = monthlyReportMapper.selectReporterPage(reporterIds, reqVO, TEAM_VISIBLE_STATUS_CODES);
|
||||
} else {
|
||||
pageResult = monthlyReportMapper.selectReporterPage(loginUserId, reqVO, getEnabledStatusCodes());
|
||||
}
|
||||
return new PageResult<>(pageResult.getList().stream()
|
||||
.map(report -> toMonthlyRespVO(report, false))
|
||||
.collect(Collectors.toList()), pageResult.getTotal());
|
||||
@@ -570,8 +587,13 @@ public class WorkReportCommonService {
|
||||
|
||||
public PageResult<ProjectReportRespVO> getProjectReportPage(ProjectReportPageReqVO reqVO) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
PageResult<ProjectReportDO> pageResult = projectReportMapper.selectReporterPage(loginUserId, reqVO,
|
||||
getEnabledStatusCodes());
|
||||
PageResult<ProjectReportDO> pageResult;
|
||||
if (reqVO.getProjectOwnerIds() != null) {
|
||||
List<Long> projectOwnerIds = teamDashboardAccessService.resolveRequestedSubordinateUserIds(reqVO.getProjectOwnerIds());
|
||||
pageResult = projectReportMapper.selectReporterPage(projectOwnerIds, reqVO, TEAM_VISIBLE_STATUS_CODES);
|
||||
} else {
|
||||
pageResult = projectReportMapper.selectReporterPage(loginUserId, reqVO, getEnabledStatusCodes());
|
||||
}
|
||||
return new PageResult<>(pageResult.getList().stream()
|
||||
.map(report -> toProjectRespVO(report, false))
|
||||
.collect(Collectors.toList()), pageResult.getTotal());
|
||||
@@ -764,7 +786,9 @@ public class WorkReportCommonService {
|
||||
|
||||
private void validateReadable(Long reporterId, Long supervisorUserId) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
if (!Objects.equals(loginUserId, reporterId) && !Objects.equals(loginUserId, supervisorUserId)) {
|
||||
if (!Objects.equals(loginUserId, reporterId)
|
||||
&& !Objects.equals(loginUserId, supervisorUserId)
|
||||
&& !teamDashboardAccessService.canReadSubordinateUser(reporterId)) {
|
||||
throw exception(ErrorCodeConstants.WORK_REPORT_READ_FORBIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.njcn.rdms.module.project.service.workreport.team;
|
||||
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportRemindReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportRemindRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportSummaryReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportSummaryRespVO;
|
||||
|
||||
public interface TeamWorkReportService {
|
||||
|
||||
TeamReportSummaryRespVO getSummary(TeamReportSummaryReqVO reqVO);
|
||||
|
||||
TeamReportRemindRespVO remind(TeamReportRemindReqVO reqVO);
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package com.njcn.rdms.module.project.service.workreport.team;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.njcn.rdms.module.project.constant.WorkReportConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportRemindReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportRemindRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportSummaryReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.workreport.team.vo.TeamReportSummaryRespVO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.project.ProjectDO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.workreport.monthly.MonthlyReportDO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectReportDO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.workreport.weekly.WeeklyReportDO;
|
||||
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.workreport.monthly.MonthlyReportMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.workreport.project.ProjectReportMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.workreport.weekly.WeeklyReportMapper;
|
||||
import com.njcn.rdms.module.project.framework.notify.NotifySendEvent;
|
||||
import com.njcn.rdms.module.project.framework.notify.NotifyTemplateCodeConstants;
|
||||
import com.njcn.rdms.module.project.service.team.TeamDashboardAccessService;
|
||||
import com.njcn.rdms.module.system.api.user.AdminUserApi;
|
||||
import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import com.njcn.rdms.module.system.enums.notify.NotifyMessageLevelConstants;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
|
||||
|
||||
@Service
|
||||
public class TeamWorkReportServiceImpl implements TeamWorkReportService {
|
||||
|
||||
private static final List<String> SUBMITTED_STATUS_CODES = List.of(
|
||||
WorkReportConstants.STATUS_PENDING_APPROVAL,
|
||||
WorkReportConstants.STATUS_APPROVED,
|
||||
WorkReportConstants.STATUS_REJECTED);
|
||||
|
||||
@Resource
|
||||
private TeamDashboardAccessService teamDashboardAccessService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private WeeklyReportMapper weeklyReportMapper;
|
||||
@Resource
|
||||
private MonthlyReportMapper monthlyReportMapper;
|
||||
@Resource
|
||||
private ProjectReportMapper projectReportMapper;
|
||||
@Resource
|
||||
private ProjectMapper projectMapper;
|
||||
@Resource
|
||||
private ObjectStatusModelMapper objectStatusModelMapper;
|
||||
@Resource
|
||||
private ApplicationEventPublisher applicationEventPublisher;
|
||||
|
||||
@Override
|
||||
public TeamReportSummaryRespVO getSummary(TeamReportSummaryReqVO reqVO) {
|
||||
teamDashboardAccessService.validateTeamDashboardPermission();
|
||||
ReportContext context = buildReportContext(normalizeReportType(reqVO.getReportType()), reqVO.getPeriodKey());
|
||||
TeamReportSummaryRespVO respVO = new TeamReportSummaryRespVO();
|
||||
respVO.setTotalShouldSubmit(context.expectedUserIds().size());
|
||||
respVO.setSubmittedCount(context.submittedUserIds().size());
|
||||
respVO.setPendingApprovalCount(context.pendingApprovalUserIds().size());
|
||||
List<TeamReportSummaryRespVO.PendingUser> unsubmittedUsers = buildPendingUsers(context.expectedUserIds(), context.submittedUserIds());
|
||||
respVO.setUnsubmittedUsers(unsubmittedUsers);
|
||||
respVO.setUnsubmittedCount(unsubmittedUsers.size());
|
||||
return respVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public TeamReportRemindRespVO remind(TeamReportRemindReqVO reqVO) {
|
||||
teamDashboardAccessService.validateTeamDashboardPermission();
|
||||
String reportType = normalizeReportType(reqVO.getReportType());
|
||||
ReportContext context = buildReportContext(reportType, reqVO.getPeriodKey());
|
||||
List<Long> remindUserIds = resolveRemindUserIds(reqVO.getUserIds(), context);
|
||||
|
||||
if (!remindUserIds.isEmpty()) {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("reportTypeName", reportTypeDisplayName(reportType));
|
||||
params.put("periodKey", reqVO.getPeriodKey());
|
||||
params.put("managerName", defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
|
||||
applicationEventPublisher.publishEvent(NotifySendEvent.of(remindUserIds,
|
||||
NotifyTemplateCodeConstants.WORK_REPORT_TEAM_REMIND, params, NotifyMessageLevelConstants.REMIND));
|
||||
}
|
||||
|
||||
TeamReportRemindRespVO respVO = new TeamReportRemindRespVO();
|
||||
respVO.setRemindedCount(remindUserIds.size());
|
||||
return respVO;
|
||||
}
|
||||
|
||||
private ReportContext buildReportContext(String reportType, String periodKey) {
|
||||
if (!StringUtils.hasText(periodKey)) {
|
||||
throw invalidParamException("周期主键不能为空");
|
||||
}
|
||||
if (WorkReportConstants.REPORT_TYPE_PROJECT.equals(reportType)) {
|
||||
return buildProjectContext(periodKey);
|
||||
}
|
||||
List<Long> subordinateIds = teamDashboardAccessService.getAllSubordinateUserIds();
|
||||
if (subordinateIds.isEmpty()) {
|
||||
return new ReportContext(Collections.emptyList(), Collections.emptySet(), Collections.emptySet());
|
||||
}
|
||||
if (WorkReportConstants.REPORT_TYPE_WEEKLY.equals(reportType)) {
|
||||
return buildWeeklyContext(periodKey, subordinateIds);
|
||||
}
|
||||
return buildMonthlyContext(periodKey, subordinateIds);
|
||||
}
|
||||
|
||||
private ReportContext buildWeeklyContext(String periodKey, List<Long> subordinateIds) {
|
||||
List<WeeklyReportDO> reports = weeklyReportMapper.selectListByReporterIdsAndPeriodKey(
|
||||
subordinateIds, periodKey, SUBMITTED_STATUS_CODES);
|
||||
Set<Long> submittedUserIds = new LinkedHashSet<>();
|
||||
Set<Long> pendingApprovalUserIds = new LinkedHashSet<>();
|
||||
for (WeeklyReportDO report : reports) {
|
||||
if (report == null || report.getReporterId() == null) {
|
||||
continue;
|
||||
}
|
||||
submittedUserIds.add(report.getReporterId());
|
||||
if (WorkReportConstants.STATUS_PENDING_APPROVAL.equals(report.getStatusCode())) {
|
||||
pendingApprovalUserIds.add(report.getReporterId());
|
||||
}
|
||||
}
|
||||
return new ReportContext(subordinateIds, submittedUserIds, pendingApprovalUserIds);
|
||||
}
|
||||
|
||||
private ReportContext buildMonthlyContext(String periodKey, List<Long> subordinateIds) {
|
||||
List<MonthlyReportDO> reports = monthlyReportMapper.selectListByReporterIdsAndPeriodKey(
|
||||
subordinateIds, periodKey, SUBMITTED_STATUS_CODES);
|
||||
Set<Long> submittedUserIds = new LinkedHashSet<>();
|
||||
Set<Long> pendingApprovalUserIds = new LinkedHashSet<>();
|
||||
for (MonthlyReportDO report : reports) {
|
||||
if (report == null || report.getReporterId() == null) {
|
||||
continue;
|
||||
}
|
||||
submittedUserIds.add(report.getReporterId());
|
||||
if (WorkReportConstants.STATUS_PENDING_APPROVAL.equals(report.getStatusCode())) {
|
||||
pendingApprovalUserIds.add(report.getReporterId());
|
||||
}
|
||||
}
|
||||
return new ReportContext(subordinateIds, submittedUserIds, pendingApprovalUserIds);
|
||||
}
|
||||
|
||||
private ReportContext buildProjectContext(String periodKey) {
|
||||
List<Long> subordinateIds = teamDashboardAccessService.getAllSubordinateUserIds();
|
||||
if (subordinateIds.isEmpty()) {
|
||||
return new ReportContext(Collections.emptyList(), Collections.emptySet(), Collections.emptySet());
|
||||
}
|
||||
List<ProjectDO> activeProjects = loadActiveProjectsForSubordinates(subordinateIds);
|
||||
if (activeProjects.isEmpty()) {
|
||||
return new ReportContext(Collections.emptyList(), Collections.emptySet(), Collections.emptySet());
|
||||
}
|
||||
Map<Long, List<ProjectDO>> projectsByOwner = activeProjects.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(project -> project.getManagerUserId() != null)
|
||||
.collect(Collectors.groupingBy(ProjectDO::getManagerUserId, LinkedHashMap::new, Collectors.toList()));
|
||||
LinkedHashSet<Long> expectedUserIds = new LinkedHashSet<>(projectsByOwner.keySet());
|
||||
List<ProjectReportDO> reports = projectReportMapper.selectListByProjectOwnerIdsAndPeriodKey(
|
||||
expectedUserIds, periodKey, SUBMITTED_STATUS_CODES);
|
||||
Map<Long, Set<Long>> submittedProjectsByOwner = new HashMap<>();
|
||||
Set<Long> submittedUserIds = new LinkedHashSet<>();
|
||||
Set<Long> pendingApprovalUserIds = new LinkedHashSet<>();
|
||||
for (ProjectReportDO report : reports) {
|
||||
if (report == null || report.getProjectOwnerId() == null || report.getProjectId() == null) {
|
||||
continue;
|
||||
}
|
||||
Long ownerId = report.getProjectOwnerId();
|
||||
if (!expectedUserIds.contains(ownerId)) {
|
||||
continue;
|
||||
}
|
||||
if (WorkReportConstants.STATUS_PENDING_APPROVAL.equals(report.getStatusCode())) {
|
||||
pendingApprovalUserIds.add(ownerId);
|
||||
}
|
||||
submittedProjectsByOwner.computeIfAbsent(ownerId, key -> new LinkedHashSet<>()).add(report.getProjectId());
|
||||
}
|
||||
for (Map.Entry<Long, List<ProjectDO>> entry : projectsByOwner.entrySet()) {
|
||||
Long ownerId = entry.getKey();
|
||||
Set<Long> submittedProjectIds = submittedProjectsByOwner.getOrDefault(ownerId, Collections.emptySet());
|
||||
boolean allSubmitted = entry.getValue().stream()
|
||||
.map(ProjectDO::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.allMatch(submittedProjectIds::contains);
|
||||
if (allSubmitted) {
|
||||
submittedUserIds.add(ownerId);
|
||||
}
|
||||
}
|
||||
return new ReportContext(new ArrayList<>(expectedUserIds), submittedUserIds, pendingApprovalUserIds);
|
||||
}
|
||||
|
||||
private List<ProjectDO> loadActiveProjectsForSubordinates(Collection<Long> subordinateIds) {
|
||||
if (subordinateIds == null || subordinateIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> terminalStatusCodes = objectStatusModelMapper
|
||||
.selectTerminalStatusCodesByObjectTypeEnabled(com.njcn.rdms.module.project.constant.ProjectObjectConstants.OBJECT_TYPE);
|
||||
List<ProjectDO> allProjects = projectMapper.selectListByManagerUserIdsAndStatusCodesNotIn(
|
||||
subordinateIds, terminalStatusCodes);
|
||||
if (allProjects.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return allProjects.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(project -> project.getManagerUserId() != null)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<Long> resolveRemindUserIds(List<Long> requestedUserIds, ReportContext context) {
|
||||
LinkedHashSet<Long> unsubmittedUserIds = new LinkedHashSet<>(context.expectedUserIds());
|
||||
unsubmittedUserIds.removeAll(context.submittedUserIds());
|
||||
if (requestedUserIds == null) {
|
||||
return new ArrayList<>(unsubmittedUserIds);
|
||||
}
|
||||
List<Long> validatedIds = teamDashboardAccessService.resolveRequestedSubordinateUserIds(requestedUserIds);
|
||||
return validatedIds.stream()
|
||||
.filter(unsubmittedUserIds::contains)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<TeamReportSummaryRespVO.PendingUser> buildPendingUsers(List<Long> expectedUserIds,
|
||||
Set<Long> submittedUserIds) {
|
||||
LinkedHashSet<Long> pendingIds = new LinkedHashSet<>(expectedUserIds);
|
||||
pendingIds.removeAll(submittedUserIds);
|
||||
if (pendingIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
CommonResult<List<AdminUserRespDTO>> result = adminUserApi.getUserList(pendingIds);
|
||||
List<AdminUserRespDTO> users = result == null ? null : result.getCheckedData();
|
||||
if (users == null || users.isEmpty()) {
|
||||
users = Collections.emptyList();
|
||||
}
|
||||
Map<Long, AdminUserRespDTO> userMap = users.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(user -> user.getId() != null)
|
||||
.collect(Collectors.toMap(AdminUserRespDTO::getId, user -> user, (left, right) -> left, LinkedHashMap::new));
|
||||
List<TeamReportSummaryRespVO.PendingUser> respList = new ArrayList<>();
|
||||
for (Long pendingId : pendingIds) {
|
||||
AdminUserRespDTO user = userMap.get(pendingId);
|
||||
TeamReportSummaryRespVO.PendingUser item = new TeamReportSummaryRespVO.PendingUser();
|
||||
item.setUserId(pendingId);
|
||||
item.setUserNickname(user == null ? "" : defaultText(user.getNickname()));
|
||||
respList.add(item);
|
||||
}
|
||||
return respList;
|
||||
}
|
||||
|
||||
private String normalizeReportType(String reportType) {
|
||||
if (WorkReportConstants.REPORT_TYPE_WEEKLY.equals(reportType)
|
||||
|| WorkReportConstants.REPORT_TYPE_MONTHLY.equals(reportType)
|
||||
|| WorkReportConstants.REPORT_TYPE_PROJECT.equals(reportType)) {
|
||||
return reportType;
|
||||
}
|
||||
throw invalidParamException("报告类型不合法");
|
||||
}
|
||||
|
||||
private String reportTypeDisplayName(String reportType) {
|
||||
return switch (reportType) {
|
||||
case WorkReportConstants.REPORT_TYPE_WEEKLY -> "周报";
|
||||
case WorkReportConstants.REPORT_TYPE_MONTHLY -> "月报";
|
||||
case WorkReportConstants.REPORT_TYPE_PROJECT -> "项目半月报";
|
||||
default -> reportType;
|
||||
};
|
||||
}
|
||||
|
||||
private String defaultText(String text) {
|
||||
return StringUtils.hasText(text) ? text.trim() : "";
|
||||
}
|
||||
|
||||
private record ReportContext(List<Long> expectedUserIds, Set<Long> submittedUserIds, Set<Long> pendingApprovalUserIds) {
|
||||
}
|
||||
}
|
||||
@@ -119,6 +119,6 @@ rdms:
|
||||
enabled: true
|
||||
cron: "0 0 12 1-31 * ?"
|
||||
scope:
|
||||
dept-ids: [101]
|
||||
dept-ids: [100]
|
||||
|
||||
debug: false
|
||||
|
||||
Reference in New Issue
Block a user