feat(performance): 实现绩效表格预填充功能并优化配置管理

This commit is contained in:
dk
2026-07-08 19:59:42 +08:00
parent 0a2d5ba86c
commit b6c2311749
10 changed files with 166 additions and 37 deletions

View File

@@ -0,0 +1,28 @@
package com.njcn.rdms.module.project.constant;
import java.util.List;
/**
* 绩效 Excel 单元格常量。
*/
public final class PerformanceExcelCellConstants {
private PerformanceExcelCellConstants() {
}
public static final String ACTUAL_SCORE_TOTAL_CELL = "J10";
public static final String BASE_SCORE_TOTAL_CELL = "K10";
public static final String EXTRA_SCORE_TOTAL_CELL = "L10";
public static final List<String> RESULT_DESCRIPTION_CELLS = List.of("I6", "I7", "I8", "I9");
public static final List<String> ACTUAL_SCORE_CELLS = List.of("J6", "J7", "J8", "J9", "J10");
public static final List<String> BASE_SCORE_CELLS = List.of("K6", "K7", "K8", "K9", "K10");
public static final List<String> EXTRA_SCORE_CELLS = List.of("L6", "L7", "L8", "L9", "L10");
public static final List<String> PREFILL_CELL_ADDRESSES = List.of(
"I6", "I7", "I8", "I9",
"J6", "J7", "J8", "J9", "J10",
"K6", "K7", "K8", "K9", "K10",
"L6", "L7", "L8", "L9", "L10"
);
}

View File

@@ -9,6 +9,7 @@ import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceS
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetCreateReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetCreateReqVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetExcelUpdateReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetExcelUpdateReqVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetPageReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetPageReqVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetPrefillRespVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetRespVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetRespVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetResponseRecordRespVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetResponseRecordRespVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetStatusActionReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetStatusActionReqVO;
@@ -162,6 +163,14 @@ public class PerformanceSheetController {
return success(performanceSheetService.getResponseRecords(id)); return success(performanceSheetService.getResponseRecords(id));
} }
@GetMapping("/prefill")
@Operation(summary = "获取绩效表上一份预填充数据")
@PreAuthorize("@ss.hasPermission('" + PerformanceConstants.PERMISSION_SHEET_CREATE + "')")
public CommonResult<PerformanceSheetPrefillRespVO> prefill(@RequestParam("employeeId") Long employeeId,
@RequestParam("periodMonth") String periodMonth) {
return success(performanceSheetService.getPrefillData(employeeId, periodMonth));
}
@GetMapping("/monthly-result") @GetMapping("/monthly-result")
@Operation(summary = "获取月报审批绩效结果") @Operation(summary = "获取月报审批绩效结果")
@PreAuthorize("@ss.hasPermission('" + PerformanceConstants.PERMISSION_SHEET_QUERY + "')") @PreAuthorize("@ss.hasPermission('" + PerformanceConstants.PERMISSION_SHEET_QUERY + "')")

View File

@@ -3,6 +3,8 @@ package com.njcn.rdms.module.project.controller.admin.performance.vo;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 绩效分数单元格映射 Response VO") @Schema(description = "管理后台 - 绩效分数单元格映射 Response VO")
@Data @Data
public class PerformanceScoreCellMappingRespVO { public class PerformanceScoreCellMappingRespVO {
@@ -15,4 +17,16 @@ public class PerformanceScoreCellMappingRespVO {
@Schema(description = "附加得分总计单元格", example = "L10") @Schema(description = "附加得分总计单元格", example = "L10")
private String extraScoreTotalCell; private String extraScoreTotalCell;
@Schema(description = "结果描述预填充单元格", example = "[\"I6\",\"I7\",\"I8\",\"I9\"]")
private List<String> resultDescriptionCells;
@Schema(description = "实际得分预填充单元格", example = "[\"J6\",\"J7\",\"J8\",\"J9\",\"J10\"]")
private List<String> actualScoreCells;
@Schema(description = "基础得分预填充单元格", example = "[\"K6\",\"K7\",\"K8\",\"K9\",\"K10\"]")
private List<String> baseScoreCells;
@Schema(description = "附加得分预填充单元格", example = "[\"L6\",\"L7\",\"L8\",\"L9\",\"L10\"]")
private List<String> extraScoreCells;
} }

View File

@@ -0,0 +1,21 @@
package com.njcn.rdms.module.project.controller.admin.performance.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
@Schema(description = "管理后台 - 绩效表上一份预填充 Response VO")
@Data
public class PerformanceSheetPrefillRespVO {
@Schema(description = "来源绩效表 ID", example = "2042074259501088770")
private Long sourceSheetId;
@Schema(description = "来源绩效月份", example = "2026-06")
private String sourcePeriodMonth;
@Schema(description = "按单元格地址返回的预填充值,例如 {\"I6\":\"结果描述\",\"J10\":\"13.00\"}")
private Map<String, String> cellValues = new LinkedHashMap<>();
}

View File

@@ -20,6 +20,16 @@ public interface PerformanceSheetMapper extends BaseMapperX<PerformanceSheetDO>
.eq(PerformanceSheetDO::getPeriodMonth, periodMonth)); .eq(PerformanceSheetDO::getPeriodMonth, periodMonth));
} }
default PerformanceSheetDO selectLatestHistoryWithFile(Long employeeId, String beforePeriodMonth) {
return selectOne(new LambdaQueryWrapperX<PerformanceSheetDO>()
.eq(PerformanceSheetDO::getEmployeeId, employeeId)
.lt(PerformanceSheetDO::getPeriodMonth, beforePeriodMonth)
.isNotNull(PerformanceSheetDO::getFileId)
.orderByDesc(PerformanceSheetDO::getPeriodMonth)
.orderByDesc(PerformanceSheetDO::getId)
.last("LIMIT 1"));
}
default PageResult<PerformanceSheetDO> selectEmployeePage(Long employeeId, PerformanceSheetPageReqVO reqVO) { default PageResult<PerformanceSheetDO> selectEmployeePage(Long employeeId, PerformanceSheetPageReqVO reqVO) {
LambdaQueryWrapperX<PerformanceSheetDO> wrapper = buildPageQuery(reqVO) LambdaQueryWrapperX<PerformanceSheetDO> wrapper = buildPageQuery(reqVO)
.eq(PerformanceSheetDO::getEmployeeId, employeeId) .eq(PerformanceSheetDO::getEmployeeId, employeeId)

View File

@@ -1,26 +0,0 @@
package com.njcn.rdms.module.project.service.performance;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 绩效配置。
*/
@Component
@ConfigurationProperties(prefix = "rdms.performance")
@Data
public class PerformanceProperties {
private ScoreCellMapping scoreCellMapping = new ScoreCellMapping();
@Data
public static class ScoreCellMapping {
private String actualScoreTotal = "J10";
private String baseScoreTotal = "K10";
private String extraScoreTotal = "L10";
}
}

View File

@@ -5,6 +5,7 @@ import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceM
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetCreateReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetCreateReqVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetExcelUpdateReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetExcelUpdateReqVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetPageReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetPageReqVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetPrefillRespVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetRespVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetRespVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetResponseRecordRespVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetResponseRecordRespVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetStatusActionReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetStatusActionReqVO;
@@ -45,6 +46,8 @@ public interface PerformanceSheetService {
List<PerformanceSheetResponseRecordRespVO> getResponseRecords(Long id); List<PerformanceSheetResponseRecordRespVO> getResponseRecords(Long id);
PerformanceSheetPrefillRespVO getPrefillData(Long employeeId, String periodMonth);
PerformanceMonthlyResultRespVO getMonthlyResult(Long employeeId, String periodMonth); PerformanceMonthlyResultRespVO getMonthlyResult(Long employeeId, String periodMonth);
List<PerformanceSheetStatusDictRespVO> getStatusDict(); List<PerformanceSheetStatusDictRespVO> getStatusDict();

View File

@@ -4,11 +4,13 @@ import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.framework.common.pojo.PageResult; import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.framework.common.util.object.BeanUtils; import com.njcn.rdms.framework.common.util.object.BeanUtils;
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils; import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.rdms.module.project.constant.PerformanceExcelCellConstants;
import com.njcn.rdms.module.project.constant.PerformanceConstants; import com.njcn.rdms.module.project.constant.PerformanceConstants;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceMonthlyResultRespVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceMonthlyResultRespVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetCreateReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetCreateReqVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetExcelUpdateReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetExcelUpdateReqVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetPageReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetPageReqVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetPrefillRespVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetRespVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetRespVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetResponseRecordRespVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetResponseRecordRespVO;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetStatusActionReqVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceSheetStatusActionReqVO;
@@ -47,9 +49,11 @@ import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.ClientAnchor; import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Comment; import org.apache.poi.ss.usermodel.Comment;
import org.apache.poi.ss.usermodel.CreationHelper; import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.DataFormat; import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Drawing; import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.PageMargin; import org.apache.poi.ss.usermodel.PageMargin;
import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Sheet;
@@ -57,6 +61,7 @@ import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory; import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellAddress; import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.PaneInformation; import org.apache.poi.ss.util.PaneInformation;
import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor; import org.apache.poi.xssf.usermodel.XSSFColor;
@@ -67,6 +72,7 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@@ -95,6 +101,7 @@ import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
@Service @Service
@Slf4j
public class PerformanceSheetServiceImpl implements PerformanceSheetService { public class PerformanceSheetServiceImpl implements PerformanceSheetService {
private static final String EXCEL_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; private static final String EXCEL_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
@@ -292,6 +299,34 @@ public class PerformanceSheetServiceImpl implements PerformanceSheetService {
PerformanceSheetResponseRecordRespVO.class); PerformanceSheetResponseRecordRespVO.class);
} }
@Override
public PerformanceSheetPrefillRespVO getPrefillData(Long employeeId, String periodMonth) {
String normalizedMonth = normalizePeriodMonth(periodMonth);
AdminUserRespDTO employee = validateActiveUser(employeeId);
AdminUserRespDTO manager = loadDirectManager(employee.getId());
validateManagerOnly(manager.getId());
PerformanceSheetPrefillRespVO respVO = new PerformanceSheetPrefillRespVO();
PerformanceSheetDO previousSheet = performanceSheetMapper.selectLatestHistoryWithFile(employeeId, normalizedMonth);
if (previousSheet == null) {
return respVO;
}
try {
Map<String, String> cellValues = readPrefillCellValues(previousSheet);
if (cellValues.isEmpty()) {
return respVO;
}
respVO.setSourceSheetId(previousSheet.getId());
respVO.setSourcePeriodMonth(previousSheet.getPeriodMonth());
respVO.setCellValues(cellValues);
} catch (Exception ex) {
log.warn("读取上一份绩效表预填充数据失败sourceSheetId={}, employeeId={}, periodMonth={}",
previousSheet.getId(), employeeId, normalizedMonth, ex);
}
return respVO;
}
@Override @Override
public PerformanceMonthlyResultRespVO getMonthlyResult(Long employeeId, String periodMonth) { public PerformanceMonthlyResultRespVO getMonthlyResult(Long employeeId, String periodMonth) {
String normalizedMonth = normalizePeriodMonth(periodMonth); String normalizedMonth = normalizePeriodMonth(periodMonth);
@@ -913,6 +948,44 @@ public class PerformanceSheetServiceImpl implements PerformanceSheetService {
return filename.substring(0, dotIndex) + "_" + count + filename.substring(dotIndex); return filename.substring(0, dotIndex) + "_" + count + filename.substring(dotIndex);
} }
private Map<String, String> readPrefillCellValues(PerformanceSheetDO sheet) throws Exception {
byte[] content = readFileContent(sheet);
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(content);
Workbook workbook = WorkbookFactory.create(inputStream)) {
if (workbook.getNumberOfSheets() <= 0) {
return Map.of();
}
Sheet sourceSheet = workbook.getSheetAt(0);
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
DataFormatter formatter = new DataFormatter();
Map<String, String> cellValues = new LinkedHashMap<>();
for (String cellAddress : PerformanceExcelCellConstants.PREFILL_CELL_ADDRESSES) {
String value = readCellDisplayValue(sourceSheet, evaluator, formatter, cellAddress);
if (value != null) {
cellValues.put(cellAddress, value);
}
}
return cellValues;
}
}
private String readCellDisplayValue(Sheet sheet,
FormulaEvaluator evaluator,
DataFormatter formatter,
String cellAddress) {
CellReference reference = new CellReference(cellAddress);
Row row = sheet.getRow(reference.getRow());
if (row == null) {
return null;
}
Cell cell = row.getCell(reference.getCol());
if (cell == null) {
return null;
}
String value = formatter.formatCellValue(cell, evaluator);
return StringUtils.hasText(value) ? value.trim() : null;
}
private PerformanceSheetDO merge(PerformanceSheetDO source, PerformanceSheetDO update) { private PerformanceSheetDO merge(PerformanceSheetDO source, PerformanceSheetDO update) {
PerformanceSheetDO merged = BeanUtils.toBean(source, PerformanceSheetDO.class); PerformanceSheetDO merged = BeanUtils.toBean(source, PerformanceSheetDO.class);
if (update.getStatusCode() != null) { if (update.getStatusCode() != null) {

View File

@@ -2,6 +2,7 @@ package com.njcn.rdms.module.project.service.performance;
import com.njcn.rdms.framework.common.pojo.PageResult; import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.framework.common.util.object.BeanUtils; import com.njcn.rdms.framework.common.util.object.BeanUtils;
import com.njcn.rdms.module.project.constant.PerformanceExcelCellConstants;
import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX; import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils; import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceScoreCellMappingRespVO; import com.njcn.rdms.module.project.controller.admin.performance.vo.PerformanceScoreCellMappingRespVO;
@@ -25,8 +26,6 @@ public class PerformanceTemplateServiceImpl implements PerformanceTemplateServic
@Resource @Resource
private PerformanceTemplateMapper performanceTemplateMapper; private PerformanceTemplateMapper performanceTemplateMapper;
@Resource
private PerformanceProperties performanceProperties;
@Override @Override
public PerformanceTemplateRespVO getCurrentTemplate() { public PerformanceTemplateRespVO getCurrentTemplate() {
@@ -98,11 +97,14 @@ public class PerformanceTemplateServiceImpl implements PerformanceTemplateServic
} }
private PerformanceScoreCellMappingRespVO scoreCellMapping() { private PerformanceScoreCellMappingRespVO scoreCellMapping() {
PerformanceProperties.ScoreCellMapping mapping = performanceProperties.getScoreCellMapping();
PerformanceScoreCellMappingRespVO respVO = new PerformanceScoreCellMappingRespVO(); PerformanceScoreCellMappingRespVO respVO = new PerformanceScoreCellMappingRespVO();
respVO.setActualScoreTotalCell(mapping.getActualScoreTotal()); respVO.setActualScoreTotalCell(PerformanceExcelCellConstants.ACTUAL_SCORE_TOTAL_CELL);
respVO.setBaseScoreTotalCell(mapping.getBaseScoreTotal()); respVO.setBaseScoreTotalCell(PerformanceExcelCellConstants.BASE_SCORE_TOTAL_CELL);
respVO.setExtraScoreTotalCell(mapping.getExtraScoreTotal()); respVO.setExtraScoreTotalCell(PerformanceExcelCellConstants.EXTRA_SCORE_TOTAL_CELL);
respVO.setResultDescriptionCells(PerformanceExcelCellConstants.RESULT_DESCRIPTION_CELLS);
respVO.setActualScoreCells(PerformanceExcelCellConstants.ACTUAL_SCORE_CELLS);
respVO.setBaseScoreCells(PerformanceExcelCellConstants.BASE_SCORE_CELLS);
respVO.setExtraScoreCells(PerformanceExcelCellConstants.EXTRA_SCORE_CELLS);
return respVO; return respVO;
} }

View File

@@ -132,10 +132,5 @@ rdms:
cron: "0 0 12 1-31 * ?" cron: "0 0 12 1-31 * ?"
scope: scope:
dept-ids: [100] dept-ids: [100]
performance:
score-cell-mapping:
actual-score-total: J10
base-score-total: K10
extra-score-total: L10
debug: false debug: false