fix(加班申请、工作报告、我的绩效): 修复一系列bug、对不合理的地方进行调整。
This commit is contained in:
@@ -2,11 +2,21 @@ package com.njcn.rdms.module.project.controller.admin.overtime.team.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
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 TeamOvertimeSummaryReqVO {
|
||||
|
||||
@Schema(description = "统计月份,不传默认当前月", example = "2026-06")
|
||||
private String month;
|
||||
@Schema(description = "加班日期区间起始(不传默认当月第一天)", example = "2026-06-01")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate overtimeDateStart;
|
||||
|
||||
@Schema(description = "加班日期区间结束(不传默认当月最后一天)", example = "2026-06-30")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate overtimeDateEnd;
|
||||
}
|
||||
|
||||
@@ -3,12 +3,17 @@ package com.njcn.rdms.module.project.controller.admin.overtime.team.vo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@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)
|
||||
private LocalDate overtimeDateStart;
|
||||
|
||||
@Schema(description = "实际查询加班日期区间结束", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDate overtimeDateEnd;
|
||||
|
||||
@Schema(description = "申请总数", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
|
||||
private Integer totalApplicationCount;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
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;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public final class WorkReportExportResponseUtils {
|
||||
|
||||
@@ -12,7 +13,8 @@ public final class WorkReportExportResponseUtils {
|
||||
}
|
||||
|
||||
public static void write(HttpServletResponse response, WorkReportExportFile file) throws IOException {
|
||||
response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(file.filename()));
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''"
|
||||
+ URLEncoder.encode(file.filename(), StandardCharsets.UTF_8).replace("+", "%20"));
|
||||
response.setContentType(file.contentType());
|
||||
response.setContentLength(file.content().length);
|
||||
response.getOutputStream().write(file.content());
|
||||
|
||||
@@ -3,6 +3,11 @@ 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 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
|
||||
@@ -12,7 +17,14 @@ public class TeamReportSummaryReqVO {
|
||||
@NotBlank(message = "报告类型不能为空")
|
||||
private String reportType;
|
||||
|
||||
@Schema(description = "周期主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "weekly-2026-06-08-2026-06-14")
|
||||
@NotBlank(message = "周期主键不能为空")
|
||||
@Schema(description = "周期主键(单周期查询时使用,与日期区间二选一)", example = "weekly-2026-06-08-2026-06-14")
|
||||
private String periodKey;
|
||||
|
||||
@Schema(description = "周期起始日期(区间查询时使用,与 periodKey 二选一)", example = "2026-06-15")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@Schema(description = "周期结束日期(区间查询时使用,与 periodKey 二选一)", example = "2026-06-28")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate periodEndDate;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,19 @@ import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 团队工作报告统计 Response VO")
|
||||
@Data
|
||||
public class TeamReportSummaryRespVO {
|
||||
|
||||
@Schema(description = "实际查询周期起始日期")
|
||||
private LocalDate periodStartDate;
|
||||
|
||||
@Schema(description = "实际查询周期结束日期")
|
||||
private LocalDate periodEndDate;
|
||||
|
||||
@Schema(description = "应填人数", requiredMode = Schema.RequiredMode.REQUIRED, example = "18")
|
||||
private Integer totalShouldSubmit;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.njcn.rdms.module.project.dal.dataobject.workreport.monthly.MonthlyRep
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -24,7 +25,7 @@ public interface MonthlyReportMapper extends BaseMapperX<MonthlyReportDO> {
|
||||
}
|
||||
|
||||
default List<MonthlyReportDO> selectListByReporterIdsAndPeriodKey(Collection<Long> reporterIds, String periodKey,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty() || !StringUtils.hasText(periodKey)) {
|
||||
return List.of();
|
||||
}
|
||||
@@ -40,12 +41,34 @@ public interface MonthlyReportMapper extends BaseMapperX<MonthlyReportDO> {
|
||||
return selectList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按报告人 ID 列表与周期日期区间查询月报(periodStartDate 落在 [startDate, endDate] 内)。
|
||||
*/
|
||||
default List<MonthlyReportDO> selectListByReporterIdsAndPeriodDateRange(Collection<Long> reporterIds,
|
||||
LocalDate startDate, LocalDate endDate,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
LambdaQueryWrapperX<MonthlyReportDO> wrapper = new LambdaQueryWrapperX<MonthlyReportDO>()
|
||||
.in(MonthlyReportDO::getReporterId, reporterIds)
|
||||
.geIfPresent(MonthlyReportDO::getPeriodStartDate, startDate)
|
||||
.leIfPresent(MonthlyReportDO::getPeriodStartDate, endDate);
|
||||
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);
|
||||
}
|
||||
|
||||
default PageResult<MonthlyReportDO> selectReporterPage(Long reporterId, MonthlyReportPageReqVO reqVO,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (allowedStatusCodes != null && allowedStatusCodes.isEmpty()) {
|
||||
return new PageResult<>(List.of(), 0L);
|
||||
}
|
||||
@@ -60,8 +83,9 @@ public interface MonthlyReportMapper extends BaseMapperX<MonthlyReportDO> {
|
||||
}
|
||||
|
||||
default PageResult<MonthlyReportDO> selectReporterPage(Collection<Long> reporterIds, MonthlyReportPageReqVO reqVO,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty() || (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty()
|
||||
|| (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
|
||||
return new PageResult<>(List.of(), 0L);
|
||||
}
|
||||
LambdaQueryWrapperX<MonthlyReportDO> wrapper = buildPageQuery(reqVO)
|
||||
@@ -90,9 +114,9 @@ public interface MonthlyReportMapper extends BaseMapperX<MonthlyReportDO> {
|
||||
}
|
||||
|
||||
default int updateSubmitFieldsByIdAndStatus(Long id, String fromStatus, String reporterDeptName,
|
||||
String reporterPostName, Long supervisorUserId,
|
||||
String supervisorName, String toStatus, LocalDateTime submitTime,
|
||||
String updater) {
|
||||
String reporterPostName, Long supervisorUserId,
|
||||
String supervisorName, String toStatus, LocalDateTime submitTime,
|
||||
String updater) {
|
||||
return update(null, new LambdaUpdateWrapper<MonthlyReportDO>()
|
||||
.set(MonthlyReportDO::getReporterDeptName, reporterDeptName)
|
||||
.set(MonthlyReportDO::getReporterPostName, reporterPostName)
|
||||
@@ -110,8 +134,8 @@ public interface MonthlyReportMapper extends BaseMapperX<MonthlyReportDO> {
|
||||
}
|
||||
|
||||
default int updateApprovalFieldsByIdAndStatus(Long id, String fromStatus, String toStatus,
|
||||
LocalDateTime approvalTime, String approvalComment,
|
||||
String lastStatusReason, String updater) {
|
||||
LocalDateTime approvalTime, String approvalComment,
|
||||
String lastStatusReason, String updater) {
|
||||
return update(null, new LambdaUpdateWrapper<MonthlyReportDO>()
|
||||
.set(MonthlyReportDO::getStatusCode, toStatus)
|
||||
.set(MonthlyReportDO::getApprovalTime, approvalTime)
|
||||
@@ -124,7 +148,7 @@ public interface MonthlyReportMapper extends BaseMapperX<MonthlyReportDO> {
|
||||
}
|
||||
|
||||
default int updateByIdAndStatusesAndReporterId(MonthlyReportDO update, Long id, Collection<String> statuses,
|
||||
Long reporterId) {
|
||||
Long reporterId) {
|
||||
return update(update, new LambdaQueryWrapperX<MonthlyReportDO>()
|
||||
.eq(MonthlyReportDO::getId, id)
|
||||
.eq(MonthlyReportDO::getReporterId, reporterId)
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.njcn.rdms.module.project.dal.dataobject.workreport.project.ProjectRep
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -22,7 +23,7 @@ public interface ProjectReportMapper extends BaseMapperX<ProjectReportDO> {
|
||||
String JACKSON_TYPE_HANDLER_MAPPING = "typeHandler=" + JacksonTypeHandler.class.getCanonicalName();
|
||||
|
||||
default ProjectReportDO selectByProjectIdAndPeriodKeyAndProjectOwnerId(Long projectId, String periodKey,
|
||||
Long projectOwnerId) {
|
||||
Long projectOwnerId) {
|
||||
return selectOne(new LambdaQueryWrapperX<ProjectReportDO>()
|
||||
.eq(ProjectReportDO::getProjectId, projectId)
|
||||
.eq(ProjectReportDO::getPeriodKey, periodKey)
|
||||
@@ -30,8 +31,8 @@ public interface ProjectReportMapper extends BaseMapperX<ProjectReportDO> {
|
||||
}
|
||||
|
||||
default List<ProjectReportDO> selectListByProjectOwnerIdsAndPeriodKey(Collection<Long> projectOwnerIds,
|
||||
String periodKey,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
String periodKey,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (projectOwnerIds == null || projectOwnerIds.isEmpty() || !StringUtils.hasText(periodKey)) {
|
||||
return List.of();
|
||||
}
|
||||
@@ -47,12 +48,34 @@ public interface ProjectReportMapper extends BaseMapperX<ProjectReportDO> {
|
||||
return selectList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按项目负责人 ID 列表与周期日期区间查询项目半月报(periodStartDate 落在 [startDate, endDate] 内)。
|
||||
*/
|
||||
default List<ProjectReportDO> selectListByProjectOwnerIdsAndPeriodDateRange(Collection<Long> projectOwnerIds,
|
||||
LocalDate startDate, LocalDate endDate,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (projectOwnerIds == null || projectOwnerIds.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
LambdaQueryWrapperX<ProjectReportDO> wrapper = new LambdaQueryWrapperX<ProjectReportDO>()
|
||||
.in(ProjectReportDO::getProjectOwnerId, projectOwnerIds)
|
||||
.geIfPresent(ProjectReportDO::getPeriodStartDate, startDate)
|
||||
.leIfPresent(ProjectReportDO::getPeriodStartDate, endDate);
|
||||
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);
|
||||
}
|
||||
|
||||
default PageResult<ProjectReportDO> selectReporterPage(Long reporterId, ProjectReportPageReqVO reqVO,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (allowedStatusCodes != null && allowedStatusCodes.isEmpty()) {
|
||||
return new PageResult<>(List.of(), 0L);
|
||||
}
|
||||
@@ -67,8 +90,9 @@ public interface ProjectReportMapper extends BaseMapperX<ProjectReportDO> {
|
||||
}
|
||||
|
||||
default PageResult<ProjectReportDO> selectReporterPage(Collection<Long> reporterIds, ProjectReportPageReqVO reqVO,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty() || (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty()
|
||||
|| (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
|
||||
return new PageResult<>(List.of(), 0L);
|
||||
}
|
||||
LambdaQueryWrapperX<ProjectReportDO> wrapper = buildPageQuery(reqVO)
|
||||
@@ -97,10 +121,10 @@ public interface ProjectReportMapper extends BaseMapperX<ProjectReportDO> {
|
||||
}
|
||||
|
||||
default int updateSubmitFieldsByIdAndStatus(Long id, String fromStatus, String projectName,
|
||||
Long projectOwnerId, String projectOwnerName,
|
||||
List<WorkReportMemberSnapshotItem> projectMemberSnapshot,
|
||||
Long supervisorUserId, String supervisorName, String toStatus,
|
||||
LocalDateTime submitTime, String updater) {
|
||||
Long projectOwnerId, String projectOwnerName,
|
||||
List<WorkReportMemberSnapshotItem> projectMemberSnapshot,
|
||||
Long supervisorUserId, String supervisorName, String toStatus,
|
||||
LocalDateTime submitTime, String updater) {
|
||||
return update(null, new LambdaUpdateWrapper<ProjectReportDO>()
|
||||
.set(ProjectReportDO::getProjectName, projectName)
|
||||
.set(ProjectReportDO::getProjectOwnerId, projectOwnerId)
|
||||
@@ -120,8 +144,8 @@ public interface ProjectReportMapper extends BaseMapperX<ProjectReportDO> {
|
||||
}
|
||||
|
||||
default int updateApprovalFieldsByIdAndStatus(Long id, String fromStatus, String toStatus,
|
||||
LocalDateTime approvalTime, String approvalComment,
|
||||
String lastStatusReason, String updater) {
|
||||
LocalDateTime approvalTime, String approvalComment,
|
||||
String lastStatusReason, String updater) {
|
||||
return update(null, new LambdaUpdateWrapper<ProjectReportDO>()
|
||||
.set(ProjectReportDO::getStatusCode, toStatus)
|
||||
.set(ProjectReportDO::getApprovalTime, approvalTime)
|
||||
@@ -134,7 +158,7 @@ public interface ProjectReportMapper extends BaseMapperX<ProjectReportDO> {
|
||||
}
|
||||
|
||||
default int updateByIdAndStatusesAndReporterId(ProjectReportDO update, Long id, Collection<String> statuses,
|
||||
Long reporterId) {
|
||||
Long reporterId) {
|
||||
return update(update, new LambdaQueryWrapperX<ProjectReportDO>()
|
||||
.eq(ProjectReportDO::getId, id)
|
||||
.eq(ProjectReportDO::getProjectOwnerId, reporterId)
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.njcn.rdms.module.project.dal.dataobject.workreport.weekly.WeeklyRepor
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -24,7 +25,7 @@ public interface WeeklyReportMapper extends BaseMapperX<WeeklyReportDO> {
|
||||
}
|
||||
|
||||
default List<WeeklyReportDO> selectListByReporterIdsAndPeriodKey(Collection<Long> reporterIds, String periodKey,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty() || !StringUtils.hasText(periodKey)) {
|
||||
return List.of();
|
||||
}
|
||||
@@ -40,12 +41,34 @@ public interface WeeklyReportMapper extends BaseMapperX<WeeklyReportDO> {
|
||||
return selectList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按报告人 ID 列表与周期日期区间查询周报(periodStartDate 落在 [startDate, endDate] 内)。
|
||||
*/
|
||||
default List<WeeklyReportDO> selectListByReporterIdsAndPeriodDateRange(Collection<Long> reporterIds,
|
||||
LocalDate startDate, LocalDate endDate,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
LambdaQueryWrapperX<WeeklyReportDO> wrapper = new LambdaQueryWrapperX<WeeklyReportDO>()
|
||||
.in(WeeklyReportDO::getReporterId, reporterIds)
|
||||
.geIfPresent(WeeklyReportDO::getPeriodStartDate, startDate)
|
||||
.leIfPresent(WeeklyReportDO::getPeriodStartDate, endDate);
|
||||
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);
|
||||
}
|
||||
|
||||
default PageResult<WeeklyReportDO> selectReporterPage(Long reporterId, WeeklyReportPageReqVO reqVO,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (allowedStatusCodes != null && allowedStatusCodes.isEmpty()) {
|
||||
return new PageResult<>(List.of(), 0L);
|
||||
}
|
||||
@@ -60,8 +83,9 @@ public interface WeeklyReportMapper extends BaseMapperX<WeeklyReportDO> {
|
||||
}
|
||||
|
||||
default PageResult<WeeklyReportDO> selectReporterPage(Collection<Long> reporterIds, WeeklyReportPageReqVO reqVO,
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty() || (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
|
||||
Collection<String> allowedStatusCodes) {
|
||||
if (reporterIds == null || reporterIds.isEmpty()
|
||||
|| (allowedStatusCodes != null && allowedStatusCodes.isEmpty())) {
|
||||
return new PageResult<>(List.of(), 0L);
|
||||
}
|
||||
LambdaQueryWrapperX<WeeklyReportDO> wrapper = buildPageQuery(reqVO)
|
||||
@@ -90,9 +114,9 @@ public interface WeeklyReportMapper extends BaseMapperX<WeeklyReportDO> {
|
||||
}
|
||||
|
||||
default int updateSubmitFieldsByIdAndStatus(Long id, String fromStatus, String reporterDeptName,
|
||||
String reporterPostName, Long supervisorUserId,
|
||||
String supervisorName, String toStatus, LocalDateTime submitTime,
|
||||
String updater) {
|
||||
String reporterPostName, Long supervisorUserId,
|
||||
String supervisorName, String toStatus, LocalDateTime submitTime,
|
||||
String updater) {
|
||||
return update(null, new LambdaUpdateWrapper<WeeklyReportDO>()
|
||||
.set(WeeklyReportDO::getReporterDeptName, reporterDeptName)
|
||||
.set(WeeklyReportDO::getReporterPostName, reporterPostName)
|
||||
@@ -110,8 +134,8 @@ public interface WeeklyReportMapper extends BaseMapperX<WeeklyReportDO> {
|
||||
}
|
||||
|
||||
default int updateApprovalFieldsByIdAndStatus(Long id, String fromStatus, String toStatus,
|
||||
LocalDateTime approvalTime, String approvalComment,
|
||||
String lastStatusReason, String updater) {
|
||||
LocalDateTime approvalTime, String approvalComment,
|
||||
String lastStatusReason, String updater) {
|
||||
return update(null, new LambdaUpdateWrapper<WeeklyReportDO>()
|
||||
.set(WeeklyReportDO::getStatusCode, toStatus)
|
||||
.set(WeeklyReportDO::getApprovalTime, approvalTime)
|
||||
@@ -124,7 +148,7 @@ public interface WeeklyReportMapper extends BaseMapperX<WeeklyReportDO> {
|
||||
}
|
||||
|
||||
default int updateByIdAndStatusesAndReporterId(WeeklyReportDO update, Long id, Collection<String> statuses,
|
||||
Long reporterId) {
|
||||
Long reporterId) {
|
||||
return update(update, new LambdaQueryWrapperX<WeeklyReportDO>()
|
||||
.eq(WeeklyReportDO::getId, id)
|
||||
.eq(WeeklyReportDO::getReporterId, reporterId)
|
||||
|
||||
@@ -11,21 +11,14 @@ 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
|
||||
@@ -36,10 +29,15 @@ public class TeamOvertimeServiceImpl implements TeamOvertimeService {
|
||||
teamDashboardAccessService.validateTeamDashboardPermission(
|
||||
OvertimeApplicationConstants.PERMISSION_TEAM_DASHBOARD);
|
||||
List<Long> subordinateIds = teamDashboardAccessService.getAllSubordinateUserIds();
|
||||
YearMonth month = parseMonth(reqVO == null ? null : reqVO.getMonth());
|
||||
LocalDate[] dateRange = normalizeDateRange(
|
||||
reqVO == null ? null : reqVO.getOvertimeDateStart(),
|
||||
reqVO == null ? null : reqVO.getOvertimeDateEnd());
|
||||
LocalDate startDate = dateRange[0];
|
||||
LocalDate endDate = dateRange[1];
|
||||
|
||||
TeamOvertimeSummaryRespVO respVO = new TeamOvertimeSummaryRespVO();
|
||||
respVO.setMonth(month.format(MONTH_FORMATTER));
|
||||
respVO.setOvertimeDateStart(startDate);
|
||||
respVO.setOvertimeDateEnd(endDate);
|
||||
if (subordinateIds.isEmpty()) {
|
||||
respVO.setTotalApplicationCount(0);
|
||||
respVO.setPendingCount(0);
|
||||
@@ -52,7 +50,7 @@ public class TeamOvertimeServiceImpl implements TeamOvertimeService {
|
||||
pageReqVO.setApplicantIds(subordinateIds);
|
||||
pageReqVO.setPageNo(1);
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
pageReqVO.setOvertimeDate(new LocalDate[]{month.atDay(1), month.atEndOfMonth()});
|
||||
pageReqVO.setOvertimeDate(new LocalDate[] { startDate, endDate });
|
||||
PageResult<OvertimeApplicationRespVO> page = overtimeApplicationService.getMyPage(pageReqVO);
|
||||
|
||||
int pendingCount = 0;
|
||||
@@ -74,14 +72,20 @@ public class TeamOvertimeServiceImpl implements TeamOvertimeService {
|
||||
return respVO;
|
||||
}
|
||||
|
||||
private YearMonth parseMonth(String month) {
|
||||
if (!StringUtils.hasText(month)) {
|
||||
return YearMonth.now();
|
||||
/**
|
||||
* 规范化日期区间:两者都不传时默认当月 [月初, 月末];支持半开区间。
|
||||
*/
|
||||
private LocalDate[] normalizeDateRange(LocalDate start, LocalDate end) {
|
||||
if (start == null && end == null) {
|
||||
YearMonth currentMonth = YearMonth.now();
|
||||
return new LocalDate[] { currentMonth.atDay(1), currentMonth.atEndOfMonth() };
|
||||
}
|
||||
try {
|
||||
return YearMonth.parse(month, MONTH_FORMATTER);
|
||||
} catch (DateTimeParseException ex) {
|
||||
throw invalidParamException("统计月份格式不正确,应为 yyyy-MM");
|
||||
if (start == null) {
|
||||
start = end.withDayOfMonth(1);
|
||||
}
|
||||
if (end == null) {
|
||||
end = start.withDayOfMonth(start.lengthOfMonth());
|
||||
}
|
||||
return new LocalDate[] { start, end };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -70,12 +71,28 @@ public class TeamWorkReportServiceImpl implements TeamWorkReportService {
|
||||
@Override
|
||||
public TeamReportSummaryRespVO getSummary(TeamReportSummaryReqVO reqVO) {
|
||||
teamDashboardAccessService.validateTeamDashboardPermission(WorkReportConstants.PERMISSION_TEAM_DASHBOARD);
|
||||
ReportContext context = buildReportContext(normalizeReportType(reqVO.getReportType()), reqVO.getPeriodKey());
|
||||
String reportType = normalizeReportType(reqVO.getReportType());
|
||||
boolean useDateRange = reqVO.getPeriodStartDate() != null || reqVO.getPeriodEndDate() != null;
|
||||
ReportContext context;
|
||||
LocalDate respStartDate;
|
||||
LocalDate respEndDate;
|
||||
if (useDateRange) {
|
||||
respStartDate = reqVO.getPeriodStartDate();
|
||||
respEndDate = reqVO.getPeriodEndDate();
|
||||
context = buildReportContextByDateRange(reportType, respStartDate, respEndDate);
|
||||
} else {
|
||||
context = buildReportContext(reportType, reqVO.getPeriodKey());
|
||||
respStartDate = null;
|
||||
respEndDate = null;
|
||||
}
|
||||
TeamReportSummaryRespVO respVO = new TeamReportSummaryRespVO();
|
||||
respVO.setPeriodStartDate(respStartDate);
|
||||
respVO.setPeriodEndDate(respEndDate);
|
||||
respVO.setTotalShouldSubmit(context.expectedUserIds().size());
|
||||
respVO.setSubmittedCount(context.submittedUserIds().size());
|
||||
respVO.setPendingApprovalCount(context.pendingApprovalUserIds().size());
|
||||
List<TeamReportSummaryRespVO.PendingUser> unsubmittedUsers = buildPendingUsers(context.expectedUserIds(), context.submittedUserIds());
|
||||
List<TeamReportSummaryRespVO.PendingUser> unsubmittedUsers = buildPendingUsers(context.expectedUserIds(),
|
||||
context.submittedUserIds());
|
||||
respVO.setUnsubmittedUsers(unsubmittedUsers);
|
||||
respVO.setUnsubmittedCount(unsubmittedUsers.size());
|
||||
return respVO;
|
||||
@@ -205,7 +222,8 @@ public class TeamWorkReportServiceImpl implements TeamWorkReportService {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> terminalStatusCodes = objectStatusModelMapper
|
||||
.selectTerminalStatusCodesByObjectTypeEnabled(com.njcn.rdms.module.project.constant.ProjectObjectConstants.OBJECT_TYPE);
|
||||
.selectTerminalStatusCodesByObjectTypeEnabled(
|
||||
com.njcn.rdms.module.project.constant.ProjectObjectConstants.OBJECT_TYPE);
|
||||
List<ProjectDO> allProjects = projectMapper.selectListByManagerUserIdsAndStatusCodesNotIn(
|
||||
subordinateIds, terminalStatusCodes);
|
||||
if (allProjects.isEmpty()) {
|
||||
@@ -231,7 +249,7 @@ public class TeamWorkReportServiceImpl implements TeamWorkReportService {
|
||||
}
|
||||
|
||||
private List<TeamReportSummaryRespVO.PendingUser> buildPendingUsers(List<Long> expectedUserIds,
|
||||
Set<Long> submittedUserIds) {
|
||||
Set<Long> submittedUserIds) {
|
||||
LinkedHashSet<Long> pendingIds = new LinkedHashSet<>(expectedUserIds);
|
||||
pendingIds.removeAll(submittedUserIds);
|
||||
if (pendingIds.isEmpty()) {
|
||||
@@ -245,7 +263,8 @@ public class TeamWorkReportServiceImpl implements TeamWorkReportService {
|
||||
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));
|
||||
.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);
|
||||
@@ -279,6 +298,103 @@ public class TeamWorkReportServiceImpl implements TeamWorkReportService {
|
||||
return StringUtils.hasText(text) ? text.trim() : "";
|
||||
}
|
||||
|
||||
private record ReportContext(List<Long> expectedUserIds, Set<Long> submittedUserIds, Set<Long> pendingApprovalUserIds) {
|
||||
private ReportContext buildReportContextByDateRange(String reportType, LocalDate startDate, LocalDate endDate) {
|
||||
if (WorkReportConstants.REPORT_TYPE_PROJECT.equals(reportType)) {
|
||||
return buildProjectContextByDateRange(startDate, endDate);
|
||||
}
|
||||
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 buildWeeklyContextByDateRange(startDate, endDate, subordinateIds);
|
||||
}
|
||||
return buildMonthlyContextByDateRange(startDate, endDate, subordinateIds);
|
||||
}
|
||||
|
||||
private ReportContext buildWeeklyContextByDateRange(LocalDate startDate, LocalDate endDate,
|
||||
List<Long> subordinateIds) {
|
||||
List<WeeklyReportDO> reports = weeklyReportMapper.selectListByReporterIdsAndPeriodDateRange(
|
||||
subordinateIds, startDate, endDate, 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 buildMonthlyContextByDateRange(LocalDate startDate, LocalDate endDate,
|
||||
List<Long> subordinateIds) {
|
||||
List<MonthlyReportDO> reports = monthlyReportMapper.selectListByReporterIdsAndPeriodDateRange(
|
||||
subordinateIds, startDate, endDate, 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 buildProjectContextByDateRange(LocalDate startDate, LocalDate endDate) {
|
||||
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.selectListByProjectOwnerIdsAndPeriodDateRange(
|
||||
expectedUserIds, startDate, endDate, 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 record ReportContext(List<Long> expectedUserIds, Set<Long> submittedUserIds,
|
||||
Set<Long> pendingApprovalUserIds) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@@ -90,4 +94,24 @@ public class DeptController {
|
||||
return success(BeanUtils.toBean(dept, DeptRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/list-self-and-children")
|
||||
@Operation(summary = "获取部门自身及全部子部门")
|
||||
@Parameter(name = "id", description = "部门编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('system:dept:query')")
|
||||
public CommonResult<List<DeptSimpleRespVO>> getDeptSelfAndChildren(@RequestParam("id") Long id) {
|
||||
DeptDO self = deptService.getDept(id);
|
||||
if (self == null) {
|
||||
return success(List.of());
|
||||
}
|
||||
List<DeptDO> combined = new ArrayList<>();
|
||||
combined.add(self);
|
||||
combined.addAll(deptService.getChildDeptList(id));
|
||||
Map<Long, DeptDO> distinctMap = new LinkedHashMap<>();
|
||||
combined.forEach(dept -> distinctMap.putIfAbsent(dept.getId(), dept));
|
||||
List<DeptDO> result = new ArrayList<>(distinctMap.values());
|
||||
result.sort(Comparator.comparing(DeptDO::getSort, Comparator.nullsLast(Integer::compareTo))
|
||||
.thenComparing(DeptDO::getId, Comparator.nullsLast(Long::compareTo)));
|
||||
return success(BeanUtils.toBean(result, DeptSimpleRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.njcn.rdms.module.system.dal.dataobject.dept.DeptDO;
|
||||
import com.njcn.rdms.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.njcn.rdms.module.system.dal.dataobject.user.UserManagementRelationDO;
|
||||
import com.njcn.rdms.module.system.service.dept.DeptService;
|
||||
import com.njcn.rdms.module.system.service.user.AdminUserService;
|
||||
import com.njcn.rdms.module.system.service.user.UserManagementRelationService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -27,9 +28,13 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
@@ -58,6 +63,9 @@ public class UserManagementRelationController {
|
||||
@Resource
|
||||
private UserManagementRelationService userManagementRelationService;
|
||||
|
||||
@Resource
|
||||
private AdminUserService adminUserService;
|
||||
|
||||
/**
|
||||
* 创建用户管理链路
|
||||
*
|
||||
@@ -192,6 +200,47 @@ public class UserManagementRelationController {
|
||||
return success(UserConvert.INSTANCE.convertSimpleList(users, deptMap));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某用户当前生效的直属下级列表。
|
||||
*
|
||||
* 口径:只返回直接下级,不递归孙级;仅返回当前生效的管理链路。
|
||||
*/
|
||||
@GetMapping("/direct-subordinates")
|
||||
@Operation(summary = "获取某用户当前生效的直属下级列表")
|
||||
@Parameter(name = "userId", description = "用户ID", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('system:user-management-relation:query')")
|
||||
public CommonResult<List<UserSimpleRespVO>> getDirectSubordinates(@RequestParam("userId") Long userId) {
|
||||
List<UserManagementRelationDO> relations = userManagementRelationService.getRelationListByManagerUserId(userId);
|
||||
if (relations.isEmpty()) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Set<Long> subordinateIds = new LinkedHashSet<>();
|
||||
relations.stream()
|
||||
.filter(relation -> relation.getSubordinateUserId() != null)
|
||||
.filter(relation -> relation.getEffectiveFrom() == null || !relation.getEffectiveFrom().isAfter(now))
|
||||
.filter(relation -> relation.getEffectiveUntil() == null || relation.getEffectiveUntil().isAfter(now))
|
||||
.map(UserManagementRelationDO::getSubordinateUserId)
|
||||
.forEach(subordinateIds::add);
|
||||
if (subordinateIds.isEmpty()) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
List<AdminUserDO> users = adminUserService.getUserList(subordinateIds).stream()
|
||||
.filter(adminUserService::isUserAvailable)
|
||||
.sorted(Comparator.comparing(AdminUserDO::getSort, Comparator.nullsLast(Integer::compareTo))
|
||||
.thenComparing(AdminUserDO::getId, Comparator.nullsLast(Long::compareTo)))
|
||||
.toList();
|
||||
if (users.isEmpty()) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
Map<Long, DeptDO> deptMap = deptService.getDeptMap(convertList(users, AdminUserDO::getDeptId));
|
||||
List<UserSimpleRespVO> result = UserConvert.INSTANCE.convertSimpleList(users, deptMap).stream()
|
||||
.sorted(Comparator.comparing(UserSimpleRespVO::getSort, Comparator.nullsLast(Integer::compareTo))
|
||||
.thenComparing(UserSimpleRespVO::getId, Comparator.nullsLast(Long::compareTo)))
|
||||
.toList();
|
||||
return success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户管理链路树形结构
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user