表格导出

This commit is contained in:
lzm
2026-04-28 18:52:34 +08:00
parent 7a2260fbf4
commit a319567f65
8 changed files with 2589 additions and 364 deletions

View File

@@ -3,22 +3,20 @@ package cn.iocoder.lyzsys.module.tjt.controller.admin.report.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 项目总览表导出 Request VO")
@Schema(description = "Management Backend - Project overview export Request VO")
@Data
public class ProjectOverviewExportReqVO {
@Schema(description = "年度", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
@NotNull(message = "年度不能为空")
@Schema(description = "Year", example = "2026")
private Integer year;
@Schema(description = "专业编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "water")
@NotBlank(message = "专业编码不能为空")
private String specialtyCode;
@Schema(description = "Office ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "专业不能为空")
private Long officeId;
@Schema(description = "排序方式", example = "output_desc")
@Schema(description = "Sort type", example = "output_desc")
private String sortType;
}

View File

@@ -0,0 +1,211 @@
package cn.iocoder.lyzsys.module.tjt.service.report.builder;
import cn.iocoder.lyzsys.module.tjt.controller.admin.report.vo.EmployeeOutputSummaryExcelRespVO;
import lombok.Data;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.List;
@Component
public class EmployeeOutputSummaryExcelBuilder extends AbstractProjectOutputExcelBuilder {
private static final String SHEET_NAME = "员工汇总";
private static final String TITLE_PREFIX = "7.1.6 ";
private static final String TITLE_SUFFIX = " 年设计中心员工个人考核产值汇总,全公司产值人员汇总表";
private static final String SUBTITLE_SUFFIX = " 年设计中心员工个人考核产值汇总(单位:万元)";
private static final int LAST_COL = 12;
private static final int DETAIL_START_ROW_NUM = 4;
public Workbook build(ExportData data) {
Workbook workbook = createWorkbook();
workbook.setForceFormulaRecalculation(true);
buildSheet(workbook, data);
return workbook;
}
private void buildSheet(Workbook workbook, ExportData data) {
Sheet sheet = createSheet(workbook, SHEET_NAME, SHEET_NAME);
setColumnWidths(sheet, 8, 14, 12, 12, 12, 12, 12, 14, 14, 18, 14, 14, 14);
CellStyle subtitleStyle = createDerivedStyle(workbook, createInfoStyle(workbook),
null, HorizontalAlignment.CENTER, true, (short) 11);
CellStyle headerStyle = createDerivedStyle(workbook, createHeaderStyle(workbook),
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle cellStyle = createDerivedStyle(workbook, createCellStyle(workbook),
null, HorizontalAlignment.CENTER, false, (short) 10);
CellStyle leftCellStyle = createDerivedStyle(workbook, createCellStyle(workbook),
null, HorizontalAlignment.LEFT, false, (short) 10);
CellStyle amountStyle = createAmountStyle(workbook, false, false);
CellStyle formulaStyle = createAmountStyle(workbook, false, false);
CellStyle totalCellStyle = createDerivedStyle(workbook, createHeaderStyle(workbook),
IndexedColors.GOLD, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle totalLeftCellStyle = createDerivedStyle(workbook, totalCellStyle,
IndexedColors.GOLD, HorizontalAlignment.LEFT, true, (short) 10);
CellStyle totalStyle = createAmountStyle(workbook, true, false);
CellStyle totalLeftStyle = createDerivedStyle(workbook, totalStyle,
null, HorizontalAlignment.LEFT, true, (short) 10);
int rowIndex = 0;
rowIndex = writeSubtitleRow(sheet, rowIndex, data, subtitleStyle);
rowIndex = writeHeaderRow(sheet, rowIndex, headerStyle);
List<EmployeeOutputSummaryExcelRespVO> rows = data == null || data.getRows() == null
? Collections.emptyList() : data.getRows();
for (EmployeeOutputSummaryExcelRespVO rowData : rows) {
writeDetailRow(sheet, rowIndex++, rowData, data, cellStyle, leftCellStyle, amountStyle, formulaStyle);
}
writeTotalRow(sheet, rowIndex, rows, data, totalCellStyle, totalLeftCellStyle, totalStyle, totalLeftStyle);
}
private int writeSubtitleRow(Sheet sheet, int rowIndex, ExportData data, CellStyle style) {
Row row = sheet.createRow(rowIndex);
for (int cellIndex = 0; cellIndex <= LAST_COL; cellIndex++) {
setText(row, cellIndex, "", style);
}
setMergedRegionText(sheet, rowIndex, rowIndex, 0, LAST_COL,
safeYear(data) + SUBTITLE_SUFFIX, style);
return rowIndex + 1;
}
private int writeHeaderRow(Sheet sheet, int rowIndex, CellStyle style) {
writeRow(sheet, rowIndex, style,
"序号", "姓名", "第一季度", "第二季度", "第三季度", "第四季度", "年度合计",
"所长 / BIM 考核产值", "年度考核产值合计", "1~12 月份预计发生成本(含预计精神文明奖)",
"基本考核产值", "剩余产值", "预估年底绩效");
return rowIndex + 1;
}
private void writeDetailRow(Sheet sheet, int rowIndex, EmployeeOutputSummaryExcelRespVO rowData, ExportData data,
CellStyle cellStyle, CellStyle leftCellStyle,
CellStyle amountStyle, CellStyle formulaStyle) {
Row row = sheet.createRow(rowIndex);
int excelRowNum = rowIndex + 1;
setText(row, 0, rowData == null || rowData.getSerialNo() == null ? "" : rowData.getSerialNo(), cellStyle);
setText(row, 1, rowData == null ? "" : safeText(rowData.getEmployeeName()), leftCellStyle);
setNumericCell(row, 2, rowData == null ? null : rowData.getQuarterOneAmount(), amountStyle);
setNumericCell(row, 3, rowData == null ? null : rowData.getQuarterTwoAmount(), amountStyle);
setNumericCell(row, 4, rowData == null ? null : rowData.getQuarterThreeAmount(), amountStyle);
setNumericCell(row, 5, rowData == null ? null : rowData.getQuarterFourAmount(), amountStyle);
setNumericCell(row, 6, rowData == null ? null : rowData.getAnnualTotalAmount(), amountStyle);
setNumericCell(row, 7, rowData == null ? null : rowData.getOfficeLeaderOrBimAmount(), amountStyle);
setNumericCell(row, 8, rowData == null ? null : rowData.getTotalAssessmentOutputAmount(), amountStyle);
setNumericCell(row, 9, rowData == null ? null : rowData.getExpectedCostAmount(), amountStyle);
setFormulaCell(row, 10, buildBasicAssessmentFormula(excelRowNum, data), formulaStyle);
setFormulaCell(row, 11, buildRemainingOutputFormula(excelRowNum), formulaStyle);
setFormulaCell(row, 12, buildPerformanceFormula(excelRowNum, data), formulaStyle);
}
private void writeTotalRow(Sheet sheet, int rowIndex, List<EmployeeOutputSummaryExcelRespVO> rows, ExportData data,
CellStyle totalCellStyle, CellStyle totalLeftCellStyle,
CellStyle totalStyle, CellStyle totalLeftStyle) {
Row row = sheet.createRow(rowIndex);
int totalExcelRowNum = rowIndex + 1;
int detailEndRowNum = DETAIL_START_ROW_NUM + rows.size() - 1;
setText(row, 0, "", totalCellStyle);
setText(row, 1, "合计", totalLeftCellStyle);
for (int col = 2; col <= LAST_COL; col++) {
if (rows.isEmpty()) {
setNumericCell(row, col, BigDecimal.ZERO, totalStyle);
continue;
}
setFormulaCell(row, col, "SUM(" + columnName(col) + DETAIL_START_ROW_NUM + ":"
+ columnName(col) + detailEndRowNum + ")", totalStyle);
}
// Ensure total-row formulas survive even if Excel recalculation is disabled.
if (!rows.isEmpty()) {
row.getCell(10).setCellFormula(buildTotalBasicAssessmentFormula(totalExcelRowNum));
row.getCell(11).setCellFormula(buildTotalRemainingOutputFormula(totalExcelRowNum));
row.getCell(12).setCellFormula(buildTotalPerformanceFormula(totalExcelRowNum, data));
}
}
private String buildBasicAssessmentFormula(int excelRowNum, ExportData data) {
BigDecimal kValue = data == null ? BigDecimal.ZERO : ratio(data.getKValue());
if (kValue.compareTo(BigDecimal.ZERO) <= 0) {
return "0";
}
return "ROUND(J" + excelRowNum + "/" + kValue.stripTrailingZeros().toPlainString() + ",2)";
}
private String buildRemainingOutputFormula(int excelRowNum) {
return "ROUND(I" + excelRowNum + "-K" + excelRowNum + ",2)";
}
private String buildPerformanceFormula(int excelRowNum, ExportData data) {
BigDecimal kValue = data == null ? BigDecimal.ZERO : ratio(data.getKValue());
if (kValue.compareTo(BigDecimal.ZERO) <= 0) {
return "0";
}
return "ROUND(L" + excelRowNum + "*" + kValue.stripTrailingZeros().toPlainString() + ",2)";
}
private String buildTotalBasicAssessmentFormula(int excelRowNum) {
return "SUM(K" + DETAIL_START_ROW_NUM + ":K" + (excelRowNum - 1) + ")";
}
private String buildTotalRemainingOutputFormula(int excelRowNum) {
return "SUM(L" + DETAIL_START_ROW_NUM + ":L" + (excelRowNum - 1) + ")";
}
private String buildTotalPerformanceFormula(int excelRowNum, ExportData data) {
return "SUM(M" + DETAIL_START_ROW_NUM + ":M" + (excelRowNum - 1) + ")";
}
private String safeYear(ExportData data) {
return data == null || data.getYear() == null ? "" : String.valueOf(data.getYear());
}
private CellStyle createAmountStyle(Workbook workbook, boolean bold, boolean leftAlign) {
CellStyle base = createCellStyle(workbook);
CellStyle style = createDerivedStyle(workbook, base, null,
leftAlign ? HorizontalAlignment.LEFT : HorizontalAlignment.CENTER, bold, (short) 10);
DataFormat dataFormat = workbook.createDataFormat();
style.setDataFormat(dataFormat.getFormat("0.00"));
return style;
}
private void setNumericCell(Row row, int cellIndex, BigDecimal value, CellStyle style) {
Cell cell = row.createCell(cellIndex);
if (value != null) {
cell.setCellValue(value.setScale(2, RoundingMode.HALF_UP).doubleValue());
}
cell.setCellStyle(style);
}
private void setFormulaCell(Row row, int cellIndex, String formula, CellStyle style) {
Cell cell = row.createCell(cellIndex);
cell.setCellFormula(formula);
cell.setCellStyle(style);
}
private String columnName(int zeroBasedIndex) {
int index = zeroBasedIndex;
StringBuilder builder = new StringBuilder();
while (index >= 0) {
builder.insert(0, (char) ('A' + index % 26));
index = index / 26 - 1;
}
return builder.toString();
}
@Data
public static class ExportData {
private Integer year;
private BigDecimal kValue;
private List<EmployeeOutputSummaryExcelRespVO> rows = Collections.emptyList();
}
}

View File

@@ -22,7 +22,9 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder
private static final String BUDGET_SHEET_TITLE = "附件4 建筑(装饰)工程项目考核产值预算表";
private static final String QUARTER_BUDGET_SHEET_NAME = "项目考核产值年度季度预算计取表";
private static final String QUARTER_BUDGET_SHEET_FALLBACK_NAME = "年度季度预算计取表";
private static final String QUARTER_BUDGET_SHEET_TITLE = "项目考核产值年度/季度预算计取表";
private static final String QUARTER_BUDGET_REMARK = "注:\n"
+ "1、设计阶段考核产值为总考核产值×阶段占比本年度/季度考核产值为阶段考核产值×本年度/季度发放比例。";
private static final String DATE_TEXT = "日期: 年 月 日";
public Workbook build(ExportData data) {
Workbook workbook = createWorkbook();
@@ -154,7 +156,9 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder
Sheet sheet = createSheet(workbook, QUARTER_BUDGET_SHEET_NAME, QUARTER_BUDGET_SHEET_FALLBACK_NAME);
setColumnWidths(sheet, 10, 20, 18, 24, 16, 16, 16, 12, 14, 14, 12, 12, 12, 12, 14, 14, 12, 12, 12, 12, 14, 24);
CellStyle titleStyle = createTitleStyle(workbook);
CellStyle infoStyle = createInfoStyle(workbook);
CellStyle infoValueStyle = createDerivedStyle(workbook, infoStyle,
null, HorizontalAlignment.LEFT, false, (short) 10);
CellStyle baseHeaderStyle = createHeaderStyle(workbook);
CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
@@ -165,7 +169,6 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder
null, HorizontalAlignment.CENTER, false, (short) 10);
int rowIndex = 0;
rowIndex = writeMergedTitleRow(sheet, rowIndex, QUARTER_BUDGET_SHEET_TITLE, titleStyle, 21);
rowIndex = buildQuarterBudgetHeader(sheet, rowIndex, data, headerStyle, centeredCellStyle);
for (QuarterBudgetRow rowData : data.getQuarterBudgetRows()) {
@@ -198,6 +201,26 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder
setText(row, 20, textOrBlank(rowData.getYearTotalAmountWan()), cellStyle);
setText(row, 21, safeText(rowData.getRatioRemark()), cellStyle);
}
Row remarkRow = sheet.createRow(rowIndex++);
setMergedRegionText(sheet, remarkRow.getRowNum(), remarkRow.getRowNum(), 0, 21,
QUARTER_BUDGET_REMARK, infoValueStyle);
Row signRow = sheet.createRow(rowIndex++);
setMergedRegionText(sheet, signRow.getRowNum(), signRow.getRowNum(), 0, 6,
"项目经理/工程负责人:", infoValueStyle);
setMergedRegionText(sheet, signRow.getRowNum(), signRow.getRowNum(), 7, 13,
"设计中心负责人:", infoValueStyle);
setMergedRegionText(sheet, signRow.getRowNum(), signRow.getRowNum(), 14, 21,
"市场运营中心负责人:", infoValueStyle);
Row dateRow = sheet.createRow(rowIndex);
setMergedRegionText(sheet, dateRow.getRowNum(), dateRow.getRowNum(), 0, 6,
DATE_TEXT, infoValueStyle);
setMergedRegionText(sheet, dateRow.getRowNum(), dateRow.getRowNum(), 7, 13,
DATE_TEXT, infoValueStyle);
setMergedRegionText(sheet, dateRow.getRowNum(), dateRow.getRowNum(), 14, 21,
DATE_TEXT, infoValueStyle);
}
private int buildQuarterBudgetHeader(Sheet sheet, int rowIndex, ExportData data,

View File

@@ -2,90 +2,393 @@ package cn.iocoder.lyzsys.module.tjt.service.report.builder;
import lombok.Data;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@Component
public class ProjectLeadQuarterOutputExcelBuilder extends AbstractProjectOutputExcelBuilder {
private static final String SHEET1_NAME = "工作量分配";
private static final String SHEET2_NAME = "季度考核产值";
private static final String SHEET1_TITLE = "项目经理/专业负责人人员工作量分配";
private static final String SHEET2_TITLE = "项目经理/工程负责人年度/季度项目考核产值";
private static final String DEFAULT_PROJECT_MANAGER_HEADER = "项目经理人员";
private static final String DEFAULT_ENGINEERING_HEADER = "工程负责人人员";
private static final String DEFAULT_CENTER_SIGNER = "设计中心相关负责人(签名):";
private static final String DEFAULT_PROJECT_SIGNER = "项目经理/工程负责人(签名):";
private static final List<String> OUTPUT_TYPE_ORDER = Arrays.asList(
"六大专业考核产值",
"专业分包产值",
"内部协作产值",
"其他产值"
);
public Workbook build(ExportData data) {
Workbook workbook = createWorkbook();
Sheet sheet = createSheet(workbook, "项目负责人计取", "项目负责人计取");
setColumnWidths(sheet, 8, 24, 14, 20, 12, 20, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12);
buildSheet1(workbook, data);
buildSheet2(workbook, data);
return workbook;
}
private void buildSheet1(Workbook workbook, ExportData data) {
Sheet sheet = createSheet(workbook, SHEET1_NAME, SHEET1_NAME);
setColumnWidths(sheet, 8, 26, 18, 24, 20, 20);
CellStyle titleStyle = createTitleStyle(workbook);
CellStyle infoStyle = createInfoStyle(workbook);
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle cellStyle = createCellStyle(workbook);
CellStyle baseHeaderStyle = createHeaderStyle(workbook);
CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle cellStyle = createDerivedStyle(workbook, createCellStyle(workbook),
null, HorizontalAlignment.CENTER, false, (short) 10);
CellStyle leftCellStyle = createDerivedStyle(workbook, cellStyle,
null, HorizontalAlignment.LEFT, false, (short) 10);
int rowIndex = 0;
rowIndex = writeMergedTitleRow(sheet, rowIndex, "项目负责人年度/季度计取表", titleStyle, 16);
rowIndex = writeMergedTitleRow(sheet, rowIndex, SHEET1_TITLE, titleStyle, 5);
rowIndex = buildSheet1Header(sheet, rowIndex, data, headerStyle);
Row infoRow = sheet.createRow(rowIndex++);
writeLabelValue(infoRow, 0, 1, "项目名称", data.getProjectName(), infoStyle);
writeLabelValue(infoRow, 4, 5, "年度", data.getYear(), infoStyle);
rowIndex = writeRow(sheet, rowIndex, headerStyle, (Object[]) new String[]{
"序号", "规划内容", "项目经理人员", "项目经理比例", "项目负责人人员", "项目负责人比例",
"项目经理一季度", "项目经理二季度", "项目经理三季度", "项目经理四季度", "项目经理年度合计",
"项目负责人一季度", "项目负责人二季度", "项目负责人三季度", "项目负责人四季度",
"项目负责人年度合计", "项目经理/项目负责人合计"
});
int serialNo = 1;
for (LeadQuarterRow rowData : data.getRows()) {
Row row = sheet.createRow(rowIndex++);
setText(row, 0, serialNo++, cellStyle);
setText(row, 1, rowData.getPlanningContent(), cellStyle);
setText(row, 2, rowData.getProjectManagerNames(), cellStyle);
setText(row, 3, percentText(rowData.getProjectManagerRatio()), cellStyle);
setText(row, 4, rowData.getEngineeringPrincipalNames(), cellStyle);
setText(row, 5, percentText(rowData.getEngineeringPrincipalRatio()), cellStyle);
setText(row, 6, text(rowData.getProjectManagerQuarterOneAmount()), cellStyle);
setText(row, 7, text(rowData.getProjectManagerQuarterTwoAmount()), cellStyle);
setText(row, 8, text(rowData.getProjectManagerQuarterThreeAmount()), cellStyle);
setText(row, 9, text(rowData.getProjectManagerQuarterFourAmount()), cellStyle);
setText(row, 10, text(rowData.getProjectManagerYearTotalAmount()), cellStyle);
setText(row, 11, text(rowData.getEngineeringPrincipalQuarterOneAmount()), cellStyle);
setText(row, 12, text(rowData.getEngineeringPrincipalQuarterTwoAmount()), cellStyle);
setText(row, 13, text(rowData.getEngineeringPrincipalQuarterThreeAmount()), cellStyle);
setText(row, 14, text(rowData.getEngineeringPrincipalQuarterFourAmount()), cellStyle);
setText(row, 15, text(rowData.getEngineeringPrincipalYearTotalAmount()), cellStyle);
setText(row, 16, text(rowData.getTotalAmount()), cellStyle);
List<LeadQuarterRow> rows = resolveDetailRows(data);
int bodyStartRow = rowIndex;
for (LeadQuarterRow rowData : rows) {
writeSheet1DetailRow(sheet, rowIndex++, data, rowData, cellStyle, leftCellStyle);
}
return workbook;
mergeProjectNameColumn(sheet, bodyStartRow, rowIndex - 1, data, cellStyle);
mergeOutputTypeColumn(sheet, bodyStartRow, rows, cellStyle);
}
private void buildSheet2(Workbook workbook, ExportData data) {
Sheet sheet = createSheet(workbook, SHEET2_NAME, SHEET2_NAME);
setColumnWidths(sheet, 8, 26, 18, 24, 12, 12, 12, 12, 14, 12, 12, 12, 12, 14);
CellStyle baseHeaderStyle = createHeaderStyle(workbook);
CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle subtotalStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle cellStyle = createDerivedStyle(workbook, createCellStyle(workbook),
null, HorizontalAlignment.CENTER, false, (short) 10);
CellStyle leftCellStyle = createDerivedStyle(workbook, cellStyle,
null, HorizontalAlignment.LEFT, false, (short) 10);
CellStyle signatureStyle = createDerivedStyle(workbook, createInfoStyle(workbook),
null, HorizontalAlignment.LEFT, false, (short) 10);
int rowIndex = 0;
rowIndex = buildSheet2Header(sheet, rowIndex, data, headerStyle);
List<LeadQuarterRow> rows = resolveSheet2Rows(data);
int bodyStartRow = rowIndex;
for (LeadQuarterRow rowData : rows) {
if (Boolean.TRUE.equals(rowData.getSubtotalRow())) {
writeSheet2SubtotalRow(sheet, rowIndex++, rowData, subtotalStyle);
continue;
}
writeSheet2DetailRow(sheet, rowIndex++, data, rowData, cellStyle, leftCellStyle);
}
mergeProjectNameColumn(sheet, bodyStartRow, rowIndex - 1, data, cellStyle);
mergeOutputTypeColumn(sheet, bodyStartRow, rows, cellStyle);
writeSheet2SignatureRow(sheet, rowIndex, data, signatureStyle);
}
private int buildSheet1Header(Sheet sheet, int rowIndex, ExportData data, CellStyle headerStyle) {
int firstRow = rowIndex;
sheet.createRow(firstRow);
sheet.createRow(firstRow + 1);
setMergedRegionText(sheet, firstRow, firstRow + 1, 0, 0, "序号", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow + 1, 1, 1, "项目名称", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow, 2, 3, "类别", headerStyle);
setText(sheet.getRow(firstRow + 1), 2, "产值类型", headerStyle);
setText(sheet.getRow(firstRow + 1), 3, "设计内容", headerStyle);
setText(sheet.getRow(firstRow), 4,
defaultLabel(resolveProjectManagerNames(data), DEFAULT_PROJECT_MANAGER_HEADER), headerStyle);
setText(sheet.getRow(firstRow), 5,
defaultLabel(resolveEngineeringPrincipalNames(data), DEFAULT_ENGINEERING_HEADER), headerStyle);
setText(sheet.getRow(firstRow + 1), 4, "工作比例", headerStyle);
setText(sheet.getRow(firstRow + 1), 5, "工作比例", headerStyle);
return rowIndex + 2;
}
private int buildSheet2Header(Sheet sheet, int rowIndex, ExportData data, CellStyle headerStyle) {
int firstRow = rowIndex;
sheet.createRow(firstRow);
sheet.createRow(firstRow + 1);
sheet.createRow(firstRow + 2);
setMergedRegionText(sheet, firstRow, firstRow + 2, 0, 0, "序号", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow + 2, 1, 1, "项目名称", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow + 1, 2, 3, "类别", headerStyle);
setText(sheet.getRow(firstRow + 2), 2, "产值类型", headerStyle);
setText(sheet.getRow(firstRow + 2), 3, "设计内容", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow, 4, 13, SHEET2_TITLE, headerStyle);
setMergedRegionText(sheet, firstRow + 1, firstRow + 1, 4, 8,
defaultLabel(resolveProjectManagerNames(data), DEFAULT_PROJECT_MANAGER_HEADER), headerStyle);
setMergedRegionText(sheet, firstRow + 1, firstRow + 1, 9, 13,
defaultLabel(resolveEngineeringPrincipalNames(data), DEFAULT_ENGINEERING_HEADER), headerStyle);
setText(sheet.getRow(firstRow + 2), 4, "一季度", headerStyle);
setText(sheet.getRow(firstRow + 2), 5, "二季度", headerStyle);
setText(sheet.getRow(firstRow + 2), 6, "三季度", headerStyle);
setText(sheet.getRow(firstRow + 2), 7, "四季度", headerStyle);
setText(sheet.getRow(firstRow + 2), 8, "本年度小计", headerStyle);
setText(sheet.getRow(firstRow + 2), 9, "一季度", headerStyle);
setText(sheet.getRow(firstRow + 2), 10, "二季度", headerStyle);
setText(sheet.getRow(firstRow + 2), 11, "三季度", headerStyle);
setText(sheet.getRow(firstRow + 2), 12, "四季度", headerStyle);
setText(sheet.getRow(firstRow + 2), 13, "本年度小计", headerStyle);
return rowIndex + 3;
}
private void writeSheet1DetailRow(Sheet sheet, int rowIndex, ExportData data, LeadQuarterRow rowData,
CellStyle cellStyle, CellStyle leftCellStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, rowData.getSerialNo(), cellStyle);
setText(row, 1, safeText(data.getProjectName()), cellStyle);
setText(row, 2, safeText(rowData.getOutputType()), cellStyle);
setText(row, 3, safeText(rowData.getDesignContent()), leftCellStyle);
setText(row, 4, percentTextOrBlank(rowData.getProjectManagerRatio()), cellStyle);
setText(row, 5, percentTextOrBlank(rowData.getEngineeringPrincipalRatio()), cellStyle);
}
private void writeSheet2DetailRow(Sheet sheet, int rowIndex, ExportData data, LeadQuarterRow rowData,
CellStyle cellStyle, CellStyle leftCellStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, rowData.getSerialNo(), cellStyle);
setText(row, 1, safeText(data.getProjectName()), cellStyle);
setText(row, 2, safeText(rowData.getOutputType()), cellStyle);
setText(row, 3, safeText(rowData.getDesignContent()), leftCellStyle);
setText(row, 4, textOrBlank(rowData.getProjectManagerQuarterOneAmountWan()), cellStyle);
setText(row, 5, textOrBlank(rowData.getProjectManagerQuarterTwoAmountWan()), cellStyle);
setText(row, 6, textOrBlank(rowData.getProjectManagerQuarterThreeAmountWan()), cellStyle);
setText(row, 7, textOrBlank(rowData.getProjectManagerQuarterFourAmountWan()), cellStyle);
setText(row, 8, textOrBlank(rowData.getProjectManagerYearTotalAmountWan()), cellStyle);
setText(row, 9, textOrBlank(rowData.getEngineeringPrincipalQuarterOneAmountWan()), cellStyle);
setText(row, 10, textOrBlank(rowData.getEngineeringPrincipalQuarterTwoAmountWan()), cellStyle);
setText(row, 11, textOrBlank(rowData.getEngineeringPrincipalQuarterThreeAmountWan()), cellStyle);
setText(row, 12, textOrBlank(rowData.getEngineeringPrincipalQuarterFourAmountWan()), cellStyle);
setText(row, 13, textOrBlank(rowData.getEngineeringPrincipalYearTotalAmountWan()), cellStyle);
}
private void writeSheet2SubtotalRow(Sheet sheet, int rowIndex, LeadQuarterRow rowData, CellStyle subtotalStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, "", subtotalStyle);
setText(row, 1, "", subtotalStyle);
setMergedRegionText(sheet, rowIndex, rowIndex, 2, 3, safeText(rowData.getDesignContent()), subtotalStyle);
setText(row, 4, textOrBlank(rowData.getProjectManagerQuarterOneAmountWan()), subtotalStyle);
setText(row, 5, textOrBlank(rowData.getProjectManagerQuarterTwoAmountWan()), subtotalStyle);
setText(row, 6, textOrBlank(rowData.getProjectManagerQuarterThreeAmountWan()), subtotalStyle);
setText(row, 7, textOrBlank(rowData.getProjectManagerQuarterFourAmountWan()), subtotalStyle);
setText(row, 8, textOrBlank(rowData.getProjectManagerYearTotalAmountWan()), subtotalStyle);
setText(row, 9, textOrBlank(rowData.getEngineeringPrincipalQuarterOneAmountWan()), subtotalStyle);
setText(row, 10, textOrBlank(rowData.getEngineeringPrincipalQuarterTwoAmountWan()), subtotalStyle);
setText(row, 11, textOrBlank(rowData.getEngineeringPrincipalQuarterThreeAmountWan()), subtotalStyle);
setText(row, 12, textOrBlank(rowData.getEngineeringPrincipalQuarterFourAmountWan()), subtotalStyle);
setText(row, 13, textOrBlank(rowData.getEngineeringPrincipalYearTotalAmountWan()), subtotalStyle);
}
private void writeSheet2SignatureRow(Sheet sheet, int rowIndex, ExportData data, CellStyle signatureStyle) {
sheet.createRow(rowIndex);
setMergedRegionText(sheet, rowIndex, rowIndex, 0, 6,
defaultLabel(data.getCenterSignerLabel(), DEFAULT_CENTER_SIGNER), signatureStyle);
setMergedRegionText(sheet, rowIndex, rowIndex, 7, 13,
defaultLabel(data.getProjectSignerLabel(), DEFAULT_PROJECT_SIGNER), signatureStyle);
}
private void mergeProjectNameColumn(Sheet sheet, int startRow, int endRow, ExportData data, CellStyle style) {
if (startRow < 0 || endRow < startRow) {
return;
}
setMergedRegionText(sheet, startRow, endRow, 1, 1, safeText(data.getProjectName()), style);
}
private void mergeOutputTypeColumn(Sheet sheet, int bodyStartRow, List<LeadQuarterRow> rows, CellStyle style) {
if (rows == null || rows.isEmpty()) {
return;
}
int groupStartIndex = -1;
String currentOutputType = "";
for (int i = 0; i < rows.size(); i++) {
if (Boolean.TRUE.equals(rows.get(i).getSubtotalRow())) {
if (groupStartIndex >= 0) {
setMergedRegionText(sheet, bodyStartRow + groupStartIndex, bodyStartRow + i - 1,
2, 2, currentOutputType, style);
groupStartIndex = -1;
currentOutputType = "";
}
continue;
}
if (groupStartIndex < 0) {
groupStartIndex = i;
currentOutputType = safeText(rows.get(i).getOutputType());
continue;
}
String nextOutputType = safeText(rows.get(i).getOutputType());
if (!currentOutputType.equals(nextOutputType)) {
setMergedRegionText(sheet, bodyStartRow + groupStartIndex, bodyStartRow + i - 1,
2, 2, currentOutputType, style);
groupStartIndex = i;
currentOutputType = nextOutputType;
}
}
if (groupStartIndex >= 0) {
setMergedRegionText(sheet, bodyStartRow + groupStartIndex, bodyStartRow + rows.size() - 1,
2, 2, currentOutputType, style);
}
}
private List<LeadQuarterRow> resolveDetailRows(ExportData data) {
if (data == null || data.getRows() == null || data.getRows().isEmpty()) {
return new ArrayList<>();
}
List<LeadQuarterRow> rows = new ArrayList<>(data.getRows());
rows.sort(Comparator
.comparingInt((LeadQuarterRow row) -> outputTypeOrder(row.getOutputType()))
.thenComparing(LeadQuarterRow::getSerialNo, Comparator.nullsLast(Integer::compareTo)));
return rows;
}
private List<LeadQuarterRow> resolveSheet2Rows(ExportData data) {
List<LeadQuarterRow> detailRows = resolveDetailRows(data);
if (detailRows.isEmpty()) {
return detailRows;
}
List<LeadQuarterRow> rows = new ArrayList<>(detailRows);
rows.add(buildSheet2SubtotalRow(detailRows));
return rows;
}
private LeadQuarterRow buildSheet2SubtotalRow(List<LeadQuarterRow> groupRows) {
LeadQuarterRow row = new LeadQuarterRow();
row.setOutputType("");
row.setDesignContent("总计");
row.setSubtotalRow(Boolean.TRUE);
for (LeadQuarterRow detailRow : groupRows) {
row.setProjectManagerQuarterOneAmountWan(addAmount(row.getProjectManagerQuarterOneAmountWan(),
detailRow.getProjectManagerQuarterOneAmountWan()));
row.setProjectManagerQuarterTwoAmountWan(addAmount(row.getProjectManagerQuarterTwoAmountWan(),
detailRow.getProjectManagerQuarterTwoAmountWan()));
row.setProjectManagerQuarterThreeAmountWan(addAmount(row.getProjectManagerQuarterThreeAmountWan(),
detailRow.getProjectManagerQuarterThreeAmountWan()));
row.setProjectManagerQuarterFourAmountWan(addAmount(row.getProjectManagerQuarterFourAmountWan(),
detailRow.getProjectManagerQuarterFourAmountWan()));
row.setProjectManagerYearTotalAmountWan(addAmount(row.getProjectManagerYearTotalAmountWan(),
detailRow.getProjectManagerYearTotalAmountWan()));
row.setEngineeringPrincipalQuarterOneAmountWan(addAmount(row.getEngineeringPrincipalQuarterOneAmountWan(),
detailRow.getEngineeringPrincipalQuarterOneAmountWan()));
row.setEngineeringPrincipalQuarterTwoAmountWan(addAmount(row.getEngineeringPrincipalQuarterTwoAmountWan(),
detailRow.getEngineeringPrincipalQuarterTwoAmountWan()));
row.setEngineeringPrincipalQuarterThreeAmountWan(addAmount(row.getEngineeringPrincipalQuarterThreeAmountWan(),
detailRow.getEngineeringPrincipalQuarterThreeAmountWan()));
row.setEngineeringPrincipalQuarterFourAmountWan(addAmount(row.getEngineeringPrincipalQuarterFourAmountWan(),
detailRow.getEngineeringPrincipalQuarterFourAmountWan()));
row.setEngineeringPrincipalYearTotalAmountWan(addAmount(row.getEngineeringPrincipalYearTotalAmountWan(),
detailRow.getEngineeringPrincipalYearTotalAmountWan()));
}
return row;
}
private BigDecimal addAmount(BigDecimal left, BigDecimal right) {
return amount(left).add(amount(right));
}
private int outputTypeOrder(String outputType) {
int index = OUTPUT_TYPE_ORDER.indexOf(safeText(outputType));
return index >= 0 ? index : OUTPUT_TYPE_ORDER.size();
}
private String resolveProjectManagerNames(ExportData data) {
if (data == null) {
return "";
}
String displayName = safeText(data.getProjectManagerNames());
if (!displayName.isEmpty()) {
return displayName;
}
return resolveFallbackNames(data.getRows(), true);
}
private String resolveEngineeringPrincipalNames(ExportData data) {
if (data == null) {
return "";
}
String displayName = safeText(data.getEngineeringPrincipalNames());
if (!displayName.isEmpty()) {
return displayName;
}
return resolveFallbackNames(data.getRows(), false);
}
private String resolveFallbackNames(List<LeadQuarterRow> rows, boolean projectManager) {
if (rows == null || rows.isEmpty()) {
return "";
}
for (LeadQuarterRow row : rows) {
if (row == null) {
continue;
}
String current = projectManager ? row.getProjectManagerNames() : row.getEngineeringPrincipalNames();
if (!safeText(current).isEmpty()) {
return safeText(current);
}
}
return "";
}
private String defaultLabel(String value, String fallback) {
String text = safeText(value);
return text.isEmpty() ? fallback : text;
}
private String percentTextOrBlank(BigDecimal value) {
return value == null ? "" : percentText(value);
}
private String textOrBlank(BigDecimal value) {
return value == null ? "" : text(value);
}
@Data
public static class ExportData {
private String projectName;
private Integer year;
private String projectManagerNames;
private String engineeringPrincipalNames;
private String centerSignerLabel;
private String projectSignerLabel;
private List<LeadQuarterRow> rows;
}
@Data
public static class LeadQuarterRow {
private String planningContent;
private Integer serialNo;
private String outputType;
private String designContent;
private Boolean subtotalRow;
private String projectManagerNames;
private BigDecimal projectManagerRatio;
private String engineeringPrincipalNames;
private BigDecimal engineeringPrincipalRatio;
private BigDecimal projectManagerQuarterOneAmount;
private BigDecimal projectManagerQuarterTwoAmount;
private BigDecimal projectManagerQuarterThreeAmount;
private BigDecimal projectManagerQuarterFourAmount;
private BigDecimal projectManagerYearTotalAmount;
private BigDecimal engineeringPrincipalQuarterOneAmount;
private BigDecimal engineeringPrincipalQuarterTwoAmount;
private BigDecimal engineeringPrincipalQuarterThreeAmount;
private BigDecimal engineeringPrincipalQuarterFourAmount;
private BigDecimal engineeringPrincipalYearTotalAmount;
private BigDecimal totalAmount;
private BigDecimal projectManagerQuarterOneAmountWan;
private BigDecimal projectManagerQuarterTwoAmountWan;
private BigDecimal projectManagerQuarterThreeAmountWan;
private BigDecimal projectManagerQuarterFourAmountWan;
private BigDecimal projectManagerYearTotalAmountWan;
private BigDecimal engineeringPrincipalQuarterOneAmountWan;
private BigDecimal engineeringPrincipalQuarterTwoAmountWan;
private BigDecimal engineeringPrincipalQuarterThreeAmountWan;
private BigDecimal engineeringPrincipalQuarterFourAmountWan;
private BigDecimal engineeringPrincipalYearTotalAmountWan;
}
}

View File

@@ -0,0 +1,268 @@
package cn.iocoder.lyzsys.module.tjt.service.report.builder;
import lombok.Data;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Component
public class ProjectOverviewOutputExcelBuilder extends AbstractProjectOutputExcelBuilder {
private static final String SHEET_NAME = "项目总览表";
private static final String SHEET_FALLBACK_NAME = "项目总览表";
private static final String TITLE = "7.1.5 项目总览表";
public Workbook build(ExportData data) {
Workbook workbook = createWorkbook();
buildSheet(workbook, data);
return workbook;
}
private void buildSheet(Workbook workbook, ExportData data) {
Sheet sheet = createSheet(workbook, SHEET_NAME, SHEET_FALLBACK_NAME);
List<EmployeeColumn> employeeColumns = data.getEmployeeColumns() == null
? Collections.emptyList() : data.getEmployeeColumns();
int lastCol = 8 + employeeColumns.size() * 5;
setColumnWidths(sheet, buildColumnWidths(employeeColumns.size()));
CellStyle subtitleStyle = createDerivedStyle(workbook, createInfoStyle(workbook),
null, HorizontalAlignment.CENTER, true, (short) 11);
CellStyle baseHeaderStyle = createHeaderStyle(workbook);
CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle bodyCenterStyle = createDerivedStyle(workbook, createCellStyle(workbook),
null, HorizontalAlignment.CENTER, false, (short) 10);
CellStyle bodyLeftStyle = createDerivedStyle(workbook, createCellStyle(workbook),
null, HorizontalAlignment.LEFT, false, (short) 10);
CellStyle totalStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.GOLD, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle totalLeftStyle = createDerivedStyle(workbook, totalStyle,
IndexedColors.GOLD, HorizontalAlignment.LEFT, true, (short) 10);
int rowIndex = 0;
rowIndex = writeSubtitleRow(sheet, rowIndex, data, subtitleStyle, lastCol);
rowIndex = writeHeader(sheet, rowIndex, employeeColumns, headerStyle);
if (data.getRows() != null) {
for (ProjectRow rowData : data.getRows()) {
writeDataRow(sheet, rowIndex++, rowData, employeeColumns, bodyCenterStyle, bodyLeftStyle);
}
}
writeMergedTotalRow(sheet, rowIndex, data.getTotalRow(), employeeColumns, totalStyle, totalLeftStyle);
}
private int writeSubtitleRow(Sheet sheet, int rowIndex, ExportData data, CellStyle style, int lastCol) {
Row row = getOrCreateRow(sheet, rowIndex);
for (int cellIndex = 0; cellIndex <= lastCol; cellIndex++) {
setText(row, cellIndex, "", style);
}
if (lastCol <= 0) {
setText(row, 0, buildSubtitle(data), style);
return rowIndex + 1;
}
setMergedRegionText(sheet, rowIndex, rowIndex, 0, lastCol, buildSubtitle(data), style);
return rowIndex + 1;
}
private String buildSubtitle(ExportData data) {
String yearText = data == null || data.getYear() == null ? "" : String.valueOf(data.getYear());
return safeText(yearText) + " 年度 "
+ safeText(data.getOfficeName()) + " 产值汇总(单位:万元)";
}
private int writeHeader(Sheet sheet, int rowIndex, List<EmployeeColumn> employeeColumns, CellStyle headerStyle) {
int topRow = rowIndex;
sheet.createRow(topRow);
sheet.createRow(topRow + 1);
setMergedRegionText(sheet, topRow, topRow + 1, 0, 0, "序号", headerStyle);
setMergedRegionText(sheet, topRow, topRow + 1, 1, 1, "项目名称", headerStyle);
setMergedRegionText(sheet, topRow, topRow + 1, 2, 2, "工程进度情况及其它说明", headerStyle);
setMergedRegionText(sheet, topRow, topRow + 1, 3, 3, "工作阶段", headerStyle);
setMergedRegionText(sheet, topRow, topRow + 1, 4, 4, "本专业+项目总核算总产值(万元)", headerStyle);
setMergedRegionText(sheet, topRow, topRow + 1, 5, 5, "往期已发放百分比", headerStyle);
setMergedRegionText(sheet, topRow, topRow, 6, 7, "本期结算", headerStyle);
setMergedRegionText(sheet, topRow, topRow + 1, 8, 8, "未结算比例", headerStyle);
setText(getOrCreateRow(sheet, topRow + 1), 6, "占比", headerStyle);
setText(getOrCreateRow(sheet, topRow + 1), 7, "考核产值(万元)", headerStyle);
int columnIndex = 9;
for (EmployeeColumn employeeColumn : employeeColumns) {
setMergedRegionText(sheet, topRow, topRow, columnIndex, columnIndex + 4,
safeText(employeeColumn.getEmployeeName()), headerStyle);
Row subRow = getOrCreateRow(sheet, topRow + 1);
setText(subRow, columnIndex, "一季度", headerStyle);
setText(subRow, columnIndex + 1, "二季度", headerStyle);
setText(subRow, columnIndex + 2, "三季度", headerStyle);
setText(subRow, columnIndex + 3, "四季度", headerStyle);
setText(subRow, columnIndex + 4, "本年度小计", headerStyle);
columnIndex += 5;
}
return rowIndex + 2;
}
private void writeDataRow(Sheet sheet, int rowIndex, ProjectRow rowData, List<EmployeeColumn> employeeColumns,
CellStyle bodyCenterStyle, CellStyle bodyLeftStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, rowData.getSerialNo(), bodyCenterStyle);
setText(row, 1, safeText(rowData.getProjectName()), bodyLeftStyle);
setText(row, 2, safeText(rowData.getProgressText()), bodyLeftStyle);
setText(row, 3, safeText(rowData.getWorkStage()), bodyLeftStyle);
setText(row, 4, amountTextOrBlank(rowData.getTotalOutputAmount()), bodyCenterStyle);
setText(row, 5, percentTextOrBlank(rowData.getHistoricalIssuedRatio()), bodyCenterStyle);
setText(row, 6, percentTextOrBlank(rowData.getCurrentSettlementRatio()), bodyCenterStyle);
setText(row, 7, amountTextOrBlank(rowData.getCurrentSettlementAmount()), bodyCenterStyle);
setText(row, 8, percentTextOrBlank(rowData.getPendingRatio()), bodyCenterStyle);
Map<Long, EmployeeAmountValue> employeeAmountMap = rowData.getEmployeeAmountMap() == null
? Collections.emptyMap() : rowData.getEmployeeAmountMap();
int columnIndex = 9;
for (EmployeeColumn employeeColumn : employeeColumns) {
EmployeeAmountValue value = employeeAmountMap.get(employeeColumn.getEmployeeId());
setText(row, columnIndex, amountTextOrBlank(value == null ? null : value.getQuarterOneAmount()), bodyCenterStyle);
setText(row, columnIndex + 1, amountTextOrBlank(value == null ? null : value.getQuarterTwoAmount()), bodyCenterStyle);
setText(row, columnIndex + 2, amountTextOrBlank(value == null ? null : value.getQuarterThreeAmount()), bodyCenterStyle);
setText(row, columnIndex + 3, amountTextOrBlank(value == null ? null : value.getQuarterFourAmount()), bodyCenterStyle);
setText(row, columnIndex + 4, amountTextOrBlank(value == null ? null : value.getAnnualTotalAmount()), bodyCenterStyle);
columnIndex += 5;
}
}
private void writeTotalRow(Sheet sheet, int rowIndex, ProjectRow totalRow, List<EmployeeColumn> employeeColumns,
CellStyle totalStyle, CellStyle totalLeftStyle) {
ProjectRow rowData = totalRow == null ? new ProjectRow() : totalRow;
Row row = sheet.createRow(rowIndex);
setText(row, 0, "", totalStyle);
setText(row, 1, "合计", totalLeftStyle);
setText(row, 2, "", totalStyle);
setText(row, 3, "", totalStyle);
setText(row, 4, amountTextOrBlank(rowData.getTotalOutputAmount()), totalStyle);
setText(row, 5, percentTextOrBlank(rowData.getHistoricalIssuedRatio()), totalStyle);
setText(row, 6, percentTextOrBlank(rowData.getCurrentSettlementRatio()), totalStyle);
setText(row, 7, amountTextOrBlank(rowData.getCurrentSettlementAmount()), totalStyle);
setText(row, 8, percentTextOrBlank(rowData.getPendingRatio()), totalStyle);
Map<Long, EmployeeAmountValue> employeeAmountMap = rowData.getEmployeeAmountMap() == null
? Collections.emptyMap() : rowData.getEmployeeAmountMap();
int columnIndex = 9;
for (EmployeeColumn employeeColumn : employeeColumns) {
EmployeeAmountValue value = employeeAmountMap.get(employeeColumn.getEmployeeId());
setText(row, columnIndex, amountTextOrBlank(value == null ? null : value.getQuarterOneAmount()), totalStyle);
setText(row, columnIndex + 1, amountTextOrBlank(value == null ? null : value.getQuarterTwoAmount()), totalStyle);
setText(row, columnIndex + 2, amountTextOrBlank(value == null ? null : value.getQuarterThreeAmount()), totalStyle);
setText(row, columnIndex + 3, amountTextOrBlank(value == null ? null : value.getQuarterFourAmount()), totalStyle);
setText(row, columnIndex + 4, amountTextOrBlank(value == null ? null : value.getAnnualTotalAmount()), totalStyle);
columnIndex += 5;
}
}
private void writeMergedTotalRow(Sheet sheet, int rowIndex, ProjectRow totalRow, List<EmployeeColumn> employeeColumns,
CellStyle totalStyle, CellStyle totalLeftStyle) {
ProjectRow rowData = totalRow == null ? new ProjectRow() : totalRow;
Row row = sheet.createRow(rowIndex);
setMergedRegionText(sheet, rowIndex, rowIndex, 0, 3, "\u5408\u8ba1", totalStyle);
setText(row, 4, amountTextOrBlank(rowData.getTotalOutputAmount()), totalStyle);
setText(row, 5, "", totalStyle);
setText(row, 6, "", totalStyle);
setText(row, 7, amountTextOrBlank(rowData.getCurrentSettlementAmount()), totalStyle);
setText(row, 8, "", totalStyle);
Map<Long, EmployeeAmountValue> employeeAmountMap = rowData.getEmployeeAmountMap() == null
? Collections.emptyMap() : rowData.getEmployeeAmountMap();
int columnIndex = 9;
for (EmployeeColumn employeeColumn : employeeColumns) {
EmployeeAmountValue value = employeeAmountMap.get(employeeColumn.getEmployeeId());
setText(row, columnIndex, amountTextOrBlank(value == null ? null : value.getQuarterOneAmount()), totalStyle);
setText(row, columnIndex + 1, amountTextOrBlank(value == null ? null : value.getQuarterTwoAmount()), totalStyle);
setText(row, columnIndex + 2, amountTextOrBlank(value == null ? null : value.getQuarterThreeAmount()), totalStyle);
setText(row, columnIndex + 3, amountTextOrBlank(value == null ? null : value.getQuarterFourAmount()), totalStyle);
setText(row, columnIndex + 4, amountTextOrBlank(value == null ? null : value.getAnnualTotalAmount()), totalStyle);
columnIndex += 5;
}
}
private String amountTextOrBlank(BigDecimal value) {
if (value == null || amount(value).compareTo(BigDecimal.ZERO) == 0) {
return "";
}
return amount(value).divide(new BigDecimal("10000"), 2, RoundingMode.HALF_UP).toPlainString();
}
private String percentTextOrBlank(BigDecimal value) {
if (value == null || ratio(value).compareTo(BigDecimal.ZERO) == 0) {
return "";
}
return percentText(value);
}
private int[] buildColumnWidths(int employeeCount) {
int[] widths = new int[9 + employeeCount * 5];
widths[0] = 8;
widths[1] = 24;
widths[2] = 28;
widths[3] = 20;
widths[4] = 18;
widths[5] = 14;
widths[6] = 12;
widths[7] = 14;
widths[8] = 12;
for (int i = 9; i < widths.length; i += 5) {
widths[i] = 12;
widths[i + 1] = 12;
widths[i + 2] = 12;
widths[i + 3] = 12;
widths[i + 4] = 14;
}
return widths;
}
@Data
public static class ExportData {
private Integer year;
private String officeName;
private List<EmployeeColumn> employeeColumns = Collections.emptyList();
private List<ProjectRow> rows = Collections.emptyList();
private ProjectRow totalRow;
}
@Data
public static class EmployeeColumn {
private Long employeeId;
private String employeeName;
}
@Data
public static class ProjectRow {
private Integer serialNo;
private String projectName;
private String progressText;
private String workStage;
private BigDecimal totalOutputAmount;
private BigDecimal historicalIssuedRatio;
private BigDecimal currentSettlementRatio;
private BigDecimal currentSettlementAmount;
private BigDecimal pendingRatio;
private Map<Long, EmployeeAmountValue> employeeAmountMap = new LinkedHashMap<>();
}
@Data
public static class EmployeeAmountValue {
private BigDecimal quarterOneAmount;
private BigDecimal quarterTwoAmount;
private BigDecimal quarterThreeAmount;
private BigDecimal quarterFourAmount;
private BigDecimal annualTotalAmount;
}
}

View File

@@ -11,28 +11,78 @@ import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Component
public class ProjectQuarterOutputExcelBuilder extends AbstractProjectOutputExcelBuilder {
private static final String SHEET_NAME = "专业间项目考核产值年度季度计取表";
private static final String SHEET_FALLBACK_NAME = "项目级年度季度计取";
private static final String SHEET_TITLE = "专业间项目考核产值年度/季度计取表";
private static final String SHEET1_NAME = "专业间项目考核产值年度季度计取表";
private static final String SHEET1_FALLBACK_NAME = "项目级年度季度计取";
private static final String SHEET2_NAME = "专业维度统计表";
private static final String SHEET2_FALLBACK_NAME = "专业维度统计";
private static final String TOTAL_LABEL = "合计";
private static final String SUMMARY_PLACEHOLDER = "/";
private static final int SHEET1_SPECIALTY_RATIO_START_COL = 23;
private static final int SHEET1_SPECIALTY_AMOUNT_START_COL = 30;
private static final int SHEET2_BASE_COL_COUNT = 4;
private static final int SHEET2_SPECIALTY_BLOCK_WIDTH = 5;
private static final List<String> OUTPUT_TYPE_ORDER = Arrays.asList(
"六大专业考核产值",
"专业分包产值",
"内部协作产值",
"其他产值"
);
private static final List<String> FIXED_OUTPUT_TYPES = Arrays.asList(
"六大专业考核产值",
"专业分包产值",
"内部协作产值"
);
private static final List<String> SPECIALTY_NAMES = Arrays.asList(
"建筑",
"精装",
"结构",
"给排水",
"暖通",
"电气",
"数字化设计"
);
private static final List<String> SHEET2_SIGNATURE_LABELS = Arrays.asList(
"建筑专业负责人(签名)",
"精装专业负责人(签名)",
"结构专业负责人(签名)",
"给排水专业负责人(签名)",
"暖通专业负责人(签名)",
"电气专业负责人(签名)",
"数字化设计专业负责人(签名)"
);
public Workbook build(ExportData data) {
Workbook workbook = createWorkbook();
Sheet sheet = createSheet(workbook, SHEET_NAME, SHEET_FALLBACK_NAME);
buildSheet1(workbook, data);
buildSheet2(workbook, data);
return workbook;
}
private void buildSheet1(Workbook workbook, ExportData data) {
Sheet sheet = createSheet(workbook, SHEET1_NAME, SHEET1_FALLBACK_NAME);
setColumnWidths(sheet,
10, 24, 16, 20, 12, 12, 12, 12, 14,
10, 14, 12, 12, 12, 12, 14,
10, 14, 12, 12, 12, 12, 14,
10, 10, 10, 10, 10, 10,
12, 12, 12, 12, 12, 12);
10, 10, 10, 10, 10, 10, 10,
12, 12, 12, 12, 12, 12, 12);
CellStyle titleStyle = createTitleStyle(workbook);
CellStyle baseHeaderStyle = createHeaderStyle(workbook);
CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
@@ -48,41 +98,34 @@ public class ProjectQuarterOutputExcelBuilder extends AbstractProjectOutputExcel
null, HorizontalAlignment.LEFT, false, (short) 10);
int rowIndex = 0;
rowIndex = writeMergedTitleRow(sheet, rowIndex, SHEET_TITLE, titleStyle, 34);
rowIndex = buildHeader(sheet, rowIndex, data, headerStyle, cellStyle);
rowIndex = buildSheet1Header(sheet, rowIndex, data, headerStyle, cellStyle);
if (data.getRows() != null) {
for (QuarterRow rowData : data.getRows()) {
if (rowData == null) {
continue;
}
if (rowData.isTotalRow()) {
writeTotalRow(sheet, rowIndex++, data, rowData, totalStyle, totalLeftStyle);
continue;
}
writeDetailRow(sheet, rowIndex++, data, rowData, cellStyle, leftCellStyle);
int bodyStartRow = rowIndex;
List<OutputTypeGroup> groups = buildOutputTypeGroups(data);
for (OutputTypeGroup group : groups) {
int groupStartRow = rowIndex;
for (QuarterRow rowData : group.getRows()) {
writeSheet1DetailRow(sheet, rowIndex++, data, rowData, cellStyle, leftCellStyle);
}
mergeOutputTypeColumn(sheet, groupStartRow, rowIndex - 1, group.getOutputType(), cellStyle);
}
writeSignatureRow(sheet, rowIndex, data, signatureStyle);
return workbook;
QuarterRow totalRow = resolveProjectTotalRow(data);
int totalRowIndex = -1;
if (totalRow != null) {
totalRowIndex = rowIndex;
writeSheet1TotalRow(sheet, rowIndex++, data, totalRow, totalStyle, totalLeftStyle);
}
mergeProjectNameColumn(sheet, bodyStartRow, totalRowIndex >= 0 ? totalRowIndex : rowIndex - 1,
safeText(data.getProjectName()), cellStyle);
writeSheet1SignatureRow(sheet, rowIndex, data, signatureStyle);
}
private int buildHeader(Sheet sheet, int rowIndex, ExportData data,
CellStyle headerStyle, CellStyle valueStyle) {
int firstRow = rowIndex;
sheet.createRow(firstRow);
sheet.createRow(firstRow + 1);
sheet.createRow(firstRow + 2);
setMergedRegionText(sheet, firstRow, firstRow + 1, 0, 0, "工程编号", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow + 1, 1, 1, safeText(data.getProjectCode()), valueStyle);
setText(sheet.getRow(firstRow + 2), 0, "序号", headerStyle);
setText(sheet.getRow(firstRow + 2), 1, "项目名称", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow + 1, 2, 3, "类别", headerStyle);
setText(sheet.getRow(firstRow + 2), 2, "产值类型", headerStyle);
setText(sheet.getRow(firstRow + 2), 3, "设计内容", headerStyle);
private int buildSheet1Header(Sheet sheet, int rowIndex, ExportData data,
CellStyle headerStyle, CellStyle valueStyle) {
int firstRow = buildCommonLeftHeader(sheet, rowIndex, data, headerStyle, valueStyle);
setMergedRegionText(sheet, firstRow, firstRow + 1, 4, 8, "本年度项目考核产值(万元)", headerStyle);
setText(sheet.getRow(firstRow + 2), 4, "一季度", headerStyle);
@@ -113,26 +156,23 @@ public class ProjectQuarterOutputExcelBuilder extends AbstractProjectOutputExcel
setText(sheet.getRow(firstRow + 2), 21, "四季度", headerStyle);
setText(sheet.getRow(firstRow + 2), 22, "本年度总计", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow, 23, 34, "各专业年度 / 季度项目考核产值", headerStyle);
setMergedRegionText(sheet, firstRow + 1, firstRow + 1, 23, 28, "各专业考核产值占比", headerStyle);
setMergedRegionText(sheet, firstRow + 1, firstRow + 1, 29, 34, "各专业考核产值", headerStyle);
setText(sheet.getRow(firstRow + 2), 23, "建筑", headerStyle);
setText(sheet.getRow(firstRow + 2), 24, "精装", headerStyle);
setText(sheet.getRow(firstRow + 2), 25, "结构", headerStyle);
setText(sheet.getRow(firstRow + 2), 26, "给排水", headerStyle);
setText(sheet.getRow(firstRow + 2), 27, "暖通", headerStyle);
setText(sheet.getRow(firstRow + 2), 28, "电气", headerStyle);
setText(sheet.getRow(firstRow + 2), 29, "建筑", headerStyle);
setText(sheet.getRow(firstRow + 2), 30, "精装", headerStyle);
setText(sheet.getRow(firstRow + 2), 31, "结构", headerStyle);
setText(sheet.getRow(firstRow + 2), 32, "给排水", headerStyle);
setText(sheet.getRow(firstRow + 2), 33, "暖通", headerStyle);
setText(sheet.getRow(firstRow + 2), 34, "电气", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow, SHEET1_SPECIALTY_RATIO_START_COL, sheet1LastCol(),
"各专业年度 / 季度项目考核产值", headerStyle);
setMergedRegionText(sheet, firstRow + 1, firstRow + 1,
SHEET1_SPECIALTY_RATIO_START_COL, SHEET1_SPECIALTY_AMOUNT_START_COL - 1,
"各专业考核产值占比", headerStyle);
setMergedRegionText(sheet, firstRow + 1, firstRow + 1,
SHEET1_SPECIALTY_AMOUNT_START_COL, sheet1LastCol(),
"各专业考核产值", headerStyle);
for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
setText(sheet.getRow(firstRow + 2), SHEET1_SPECIALTY_RATIO_START_COL + i, SPECIALTY_NAMES.get(i), headerStyle);
setText(sheet.getRow(firstRow + 2), SHEET1_SPECIALTY_AMOUNT_START_COL + i, SPECIALTY_NAMES.get(i), headerStyle);
}
return rowIndex + 3;
}
private void writeDetailRow(Sheet sheet, int rowIndex, ExportData data, QuarterRow rowData,
CellStyle cellStyle, CellStyle leftCellStyle) {
private void writeSheet1DetailRow(Sheet sheet, int rowIndex, ExportData data, QuarterRow rowData,
CellStyle cellStyle, CellStyle leftCellStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, rowData.getSerialNo(), cellStyle);
setText(row, 1, safeText(data.getProjectName()), cellStyle);
@@ -157,22 +197,16 @@ public class ProjectQuarterOutputExcelBuilder extends AbstractProjectOutputExcel
setText(row, 20, textOrBlank(rowData.getOfficeQuarterThreeAmountWan()), cellStyle);
setText(row, 21, textOrBlank(rowData.getOfficeQuarterFourAmountWan()), cellStyle);
setText(row, 22, textOrBlank(rowData.getOfficeYearTotalAmountWan()), cellStyle);
setText(row, 23, percentTextOrBlank(rowData.getArchRatio()), cellStyle);
setText(row, 24, percentTextOrBlank(rowData.getDecorRatio()), cellStyle);
setText(row, 25, percentTextOrBlank(rowData.getStructRatio()), cellStyle);
setText(row, 26, percentTextOrBlank(rowData.getWaterRatio()), cellStyle);
setText(row, 27, percentTextOrBlank(rowData.getHvacRatio()), cellStyle);
setText(row, 28, percentTextOrBlank(rowData.getElecRatio()), cellStyle);
setText(row, 29, textOrBlank(rowData.getArchAssessmentOutputWan()), cellStyle);
setText(row, 30, textOrBlank(rowData.getDecorAssessmentOutputWan()), cellStyle);
setText(row, 31, textOrBlank(rowData.getStructAssessmentOutputWan()), cellStyle);
setText(row, 32, textOrBlank(rowData.getWaterAssessmentOutputWan()), cellStyle);
setText(row, 33, textOrBlank(rowData.getHvacAssessmentOutputWan()), cellStyle);
setText(row, 34, textOrBlank(rowData.getElecAssessmentOutputWan()), cellStyle);
for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
setText(row, SHEET1_SPECIALTY_RATIO_START_COL + i,
percentTextOrBlank(resolveSpecialtyRatio(rowData, i)), cellStyle);
setText(row, SHEET1_SPECIALTY_AMOUNT_START_COL + i,
textOrBlank(resolveSpecialtyAssessmentOutputWan(rowData, i)), cellStyle);
}
}
private void writeTotalRow(Sheet sheet, int rowIndex, ExportData data, QuarterRow rowData,
CellStyle totalStyle, CellStyle totalLeftStyle) {
private void writeSheet1TotalRow(Sheet sheet, int rowIndex, ExportData data, QuarterRow rowData,
CellStyle totalStyle, CellStyle totalLeftStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, "", totalStyle);
setText(row, 1, safeText(data.getProjectName()), totalStyle);
@@ -182,44 +216,400 @@ public class ProjectQuarterOutputExcelBuilder extends AbstractProjectOutputExcel
setText(row, 6, textOrBlank(rowData.getQuarterThreeAmountWan()), totalStyle);
setText(row, 7, textOrBlank(rowData.getQuarterFourAmountWan()), totalStyle);
setText(row, 8, textOrBlank(rowData.getYearTotalAmountWan()), totalStyle);
setText(row, 9, percentTextOrBlank(rowData.getProjectLeadRatio()), totalStyle);
setText(row, 9, "", totalStyle);
setText(row, 10, textOrBlank(rowData.getProjectLeadAssessmentOutputWan()), totalStyle);
setText(row, 11, textOrBlank(rowData.getProjectLeadQuarterOneAmountWan()), totalStyle);
setText(row, 12, textOrBlank(rowData.getProjectLeadQuarterTwoAmountWan()), totalStyle);
setText(row, 13, textOrBlank(rowData.getProjectLeadQuarterThreeAmountWan()), totalStyle);
setText(row, 14, textOrBlank(rowData.getProjectLeadQuarterFourAmountWan()), totalStyle);
setText(row, 15, textOrBlank(rowData.getProjectLeadYearTotalAmountWan()), totalStyle);
setText(row, 16, percentTextOrBlank(rowData.getOfficeRatio()), totalStyle);
setText(row, 16, "", totalStyle);
setText(row, 17, textOrBlank(rowData.getOfficeAssessmentOutputWan()), totalStyle);
setText(row, 18, textOrBlank(rowData.getOfficeQuarterOneAmountWan()), totalStyle);
setText(row, 19, textOrBlank(rowData.getOfficeQuarterTwoAmountWan()), totalStyle);
setText(row, 20, textOrBlank(rowData.getOfficeQuarterThreeAmountWan()), totalStyle);
setText(row, 21, textOrBlank(rowData.getOfficeQuarterFourAmountWan()), totalStyle);
setText(row, 22, textOrBlank(rowData.getOfficeYearTotalAmountWan()), totalStyle);
setMergedRegionText(sheet, rowIndex, rowIndex, 23, 28, SUMMARY_PLACEHOLDER, totalLeftStyle);
setText(row, 29, textOrBlank(rowData.getArchAssessmentOutputWan()), totalStyle);
setText(row, 30, textOrBlank(rowData.getDecorAssessmentOutputWan()), totalStyle);
setText(row, 31, textOrBlank(rowData.getStructAssessmentOutputWan()), totalStyle);
setText(row, 32, textOrBlank(rowData.getWaterAssessmentOutputWan()), totalStyle);
setText(row, 33, textOrBlank(rowData.getHvacAssessmentOutputWan()), totalStyle);
setText(row, 34, textOrBlank(rowData.getElecAssessmentOutputWan()), totalStyle);
setMergedRegionText(sheet, rowIndex, rowIndex,
SHEET1_SPECIALTY_RATIO_START_COL, SHEET1_SPECIALTY_AMOUNT_START_COL - 1,
"", totalLeftStyle);
for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
setText(row, SHEET1_SPECIALTY_AMOUNT_START_COL + i,
textOrBlank(resolveSpecialtyAssessmentOutputWan(rowData, i)), totalStyle);
}
}
private void writeSignatureRow(Sheet sheet, int rowIndex, ExportData data, CellStyle signatureStyle) {
Row row = sheet.createRow(rowIndex);
setMergedRegionText(sheet, rowIndex, rowIndex, 0, 16,
private void writeSheet1SignatureRow(Sheet sheet, int rowIndex, ExportData data, CellStyle signatureStyle) {
sheet.createRow(rowIndex);
int totalColumnCount = sheet1LastCol() + 1;
int leftEndCol = (totalColumnCount / 2) - 1;
setMergedRegionText(sheet, rowIndex, rowIndex, 0, leftEndCol,
safeText(defaultSignatureLabel(data.getCenterSignerLabel(), "设计中心相关负责人(签名):")),
signatureStyle);
setMergedRegionText(sheet, rowIndex, rowIndex, 17, 34,
setMergedRegionText(sheet, rowIndex, rowIndex, leftEndCol + 1, sheet1LastCol(),
safeText(defaultSignatureLabel(data.getProjectSignerLabel(), "项目经理/工程负责人(签名):")),
signatureStyle);
}
private void buildSheet2(Workbook workbook, ExportData data) {
Sheet sheet = createSheet(workbook, SHEET2_NAME, SHEET2_FALLBACK_NAME);
setColumnWidths(sheet, buildSheet2ColumnWidths());
CellStyle baseHeaderStyle = createHeaderStyle(workbook);
CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle cellStyle = createDerivedStyle(workbook, createCellStyle(workbook),
null, HorizontalAlignment.CENTER, false, (short) 10);
CellStyle leftCellStyle = createDerivedStyle(workbook, cellStyle,
null, HorizontalAlignment.LEFT, false, (short) 10);
CellStyle totalStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.GOLD, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle signatureStyle = createDerivedStyle(workbook, createInfoStyle(workbook),
null, HorizontalAlignment.CENTER, false, (short) 10);
int rowIndex = 0;
rowIndex = buildSheet2Header(sheet, rowIndex, data, headerStyle, cellStyle);
int bodyStartRow = rowIndex;
List<OutputTypeGroup> groups = buildOutputTypeGroups(data);
for (OutputTypeGroup group : groups) {
int groupStartRow = rowIndex;
for (QuarterRow detailRow : group.getRows()) {
writeSheet2DetailRow(sheet, rowIndex++, data, detailRow, cellStyle, leftCellStyle);
}
mergeOutputTypeColumn(sheet, groupStartRow, rowIndex - 1, group.getOutputType(), cellStyle);
}
int totalRowIndex = rowIndex;
writeSheet2ProjectTotalRow(sheet, rowIndex++, data,
summarizeSheet2SpecialtyBlocks(extractDetailRows(data)), totalStyle);
mergeProjectNameColumn(sheet, bodyStartRow, totalRowIndex, safeText(data.getProjectName()), cellStyle);
writeSheet2SignatureRow(sheet, rowIndex, signatureStyle);
}
private int buildSheet2Header(Sheet sheet, int rowIndex, ExportData data,
CellStyle headerStyle, CellStyle valueStyle) {
int firstRow = buildCommonLeftHeader(sheet, rowIndex, data, headerStyle, valueStyle);
int startCol = SHEET2_BASE_COL_COUNT;
int endCol = sheet2LastCol();
setMergedRegionText(sheet, firstRow, firstRow, startCol, endCol,
"各专业年度 / 季度项目考核产值", headerStyle);
for (String specialtyName : SPECIALTY_NAMES) {
setMergedRegionText(sheet, firstRow + 1, firstRow + 1, startCol, startCol + SHEET2_SPECIALTY_BLOCK_WIDTH - 1,
specialtyName + "专业项目考核产值(万元)", headerStyle);
setText(sheet.getRow(firstRow + 2), startCol, "一季度", headerStyle);
setText(sheet.getRow(firstRow + 2), startCol + 1, "二季度", headerStyle);
setText(sheet.getRow(firstRow + 2), startCol + 2, "三季度", headerStyle);
setText(sheet.getRow(firstRow + 2), startCol + 3, "四季度", headerStyle);
setText(sheet.getRow(firstRow + 2), startCol + 4, "本年度总计", headerStyle);
startCol += SHEET2_SPECIALTY_BLOCK_WIDTH;
}
return rowIndex + 3;
}
private void writeSheet2DetailRow(Sheet sheet, int rowIndex, ExportData data, QuarterRow rowData,
CellStyle cellStyle, CellStyle leftCellStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, rowData.getSerialNo(), cellStyle);
setText(row, 1, safeText(data.getProjectName()), cellStyle);
setText(row, 2, safeText(rowData.getOutputType()), cellStyle);
setText(row, 3, safeText(rowData.getDesignContent()), leftCellStyle);
writeSheet2SpecialtyBlocks(row, buildSheet2SpecialtyBlocks(rowData), cellStyle);
}
private void writeSheet2ProjectTotalRow(Sheet sheet, int rowIndex, ExportData data,
List<Sheet2SpecialtyBlock> specialtyBlocks,
CellStyle totalStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, "", totalStyle);
setText(row, 1, safeText(data.getProjectName()), totalStyle);
setMergedRegionText(sheet, rowIndex, rowIndex, 2, 3, TOTAL_LABEL, totalStyle);
writeSheet2SpecialtyBlocks(sheet.getRow(rowIndex), specialtyBlocks, totalStyle);
}
private void writeSheet2SignatureRow(Sheet sheet, int rowIndex, CellStyle signatureStyle) {
sheet.createRow(rowIndex);
setMergedRegionText(sheet, rowIndex, rowIndex, 0, SHEET2_BASE_COL_COUNT - 1, "", signatureStyle);
int startCol = SHEET2_BASE_COL_COUNT;
for (String label : SHEET2_SIGNATURE_LABELS) {
int endCol = startCol + SHEET2_SPECIALTY_BLOCK_WIDTH - 1;
setMergedRegionText(sheet, rowIndex, rowIndex, startCol, endCol, label, signatureStyle);
startCol = endCol + 1;
}
}
private void writeSheet2SpecialtyBlocks(Row row, List<Sheet2SpecialtyBlock> specialtyBlocks, CellStyle cellStyle) {
int startCol = SHEET2_BASE_COL_COUNT;
for (Sheet2SpecialtyBlock block : specialtyBlocks) {
setText(row, startCol, textOrBlank(block.getQuarterOneAmountWan()), cellStyle);
setText(row, startCol + 1, textOrBlank(block.getQuarterTwoAmountWan()), cellStyle);
setText(row, startCol + 2, textOrBlank(block.getQuarterThreeAmountWan()), cellStyle);
setText(row, startCol + 3, textOrBlank(block.getQuarterFourAmountWan()), cellStyle);
setText(row, startCol + 4, textOrBlank(block.getYearTotalAmountWan()), cellStyle);
startCol += SHEET2_SPECIALTY_BLOCK_WIDTH;
}
}
private List<Sheet2SpecialtyBlock> summarizeSheet2SpecialtyBlocks(List<QuarterRow> rows) {
List<Sheet2SpecialtyBlock> result = buildEmptySheet2SpecialtyBlocks();
for (QuarterRow row : rows) {
if (row == null) {
continue;
}
List<Sheet2SpecialtyBlock> currentBlocks = buildSheet2SpecialtyBlocks(row);
for (int i = 0; i < currentBlocks.size(); i++) {
Sheet2SpecialtyBlock totalBlock = result.get(i);
Sheet2SpecialtyBlock currentBlock = currentBlocks.get(i);
totalBlock.setQuarterOneAmountWan(addAmount(totalBlock.getQuarterOneAmountWan(), currentBlock.getQuarterOneAmountWan()));
totalBlock.setQuarterTwoAmountWan(addAmount(totalBlock.getQuarterTwoAmountWan(), currentBlock.getQuarterTwoAmountWan()));
totalBlock.setQuarterThreeAmountWan(addAmount(totalBlock.getQuarterThreeAmountWan(), currentBlock.getQuarterThreeAmountWan()));
totalBlock.setQuarterFourAmountWan(addAmount(totalBlock.getQuarterFourAmountWan(), currentBlock.getQuarterFourAmountWan()));
totalBlock.setYearTotalAmountWan(addAmount(totalBlock.getYearTotalAmountWan(), currentBlock.getYearTotalAmountWan()));
}
}
return result;
}
private List<Sheet2SpecialtyBlock> buildSheet2SpecialtyBlocks(QuarterRow row) {
if (row != null && row.isPlaceholderRow()) {
return buildBlankSheet2SpecialtyBlocks();
}
List<Sheet2SpecialtyBlock> result = new ArrayList<>();
for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
BigDecimal specialtyRatio = resolveSpecialtyRatio(row, i);
Sheet2SpecialtyBlock block = new Sheet2SpecialtyBlock();
block.setQuarterOneAmountWan(multiplyAmountByRatio(row.getOfficeQuarterOneAmountWan(), specialtyRatio));
block.setQuarterTwoAmountWan(multiplyAmountByRatio(row.getOfficeQuarterTwoAmountWan(), specialtyRatio));
block.setQuarterThreeAmountWan(multiplyAmountByRatio(row.getOfficeQuarterThreeAmountWan(), specialtyRatio));
block.setQuarterFourAmountWan(multiplyAmountByRatio(row.getOfficeQuarterFourAmountWan(), specialtyRatio));
block.setYearTotalAmountWan(addAmount(block.getQuarterOneAmountWan(),
addAmount(block.getQuarterTwoAmountWan(),
addAmount(block.getQuarterThreeAmountWan(), block.getQuarterFourAmountWan()))));
result.add(block);
}
return result;
}
private List<Sheet2SpecialtyBlock> buildBlankSheet2SpecialtyBlocks() {
List<Sheet2SpecialtyBlock> result = new ArrayList<>();
for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
result.add(new Sheet2SpecialtyBlock());
}
return result;
}
private List<Sheet2SpecialtyBlock> buildEmptySheet2SpecialtyBlocks() {
List<Sheet2SpecialtyBlock> result = new ArrayList<>();
for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
Sheet2SpecialtyBlock block = new Sheet2SpecialtyBlock();
block.setQuarterOneAmountWan(zeroAmount());
block.setQuarterTwoAmountWan(zeroAmount());
block.setQuarterThreeAmountWan(zeroAmount());
block.setQuarterFourAmountWan(zeroAmount());
block.setYearTotalAmountWan(zeroAmount());
result.add(block);
}
return result;
}
private int outputTypeOrder(String outputType) {
int index = OUTPUT_TYPE_ORDER.indexOf(safeText(outputType));
return index >= 0 ? index : OUTPUT_TYPE_ORDER.size();
}
private int buildCommonLeftHeader(Sheet sheet, int rowIndex, ExportData data,
CellStyle headerStyle, CellStyle valueStyle) {
int firstRow = rowIndex;
sheet.createRow(firstRow);
sheet.createRow(firstRow + 1);
sheet.createRow(firstRow + 2);
String projectCode = data == null ? "" : safeText(data.getProjectCode());
setMergedRegionText(sheet, firstRow, firstRow + 1, 0, 0, "工程编号", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow + 1, 1, 1, projectCode, valueStyle);
setMergedRegionText(sheet, firstRow, firstRow + 1, 2, 3, "类别", headerStyle);
setText(sheet.getRow(firstRow + 2), 0, "序号", headerStyle);
setText(sheet.getRow(firstRow + 2), 1, "项目名称", headerStyle);
setText(sheet.getRow(firstRow + 2), 2, "产值类型", headerStyle);
setText(sheet.getRow(firstRow + 2), 3, "设计内容", headerStyle);
return firstRow;
}
private void mergeProjectNameColumn(Sheet sheet, int startRow, int endRow, String projectName, CellStyle style) {
if (startRow < 0 || endRow < startRow) {
return;
}
setMergedRegionText(sheet, startRow, endRow, 1, 1, safeText(projectName), style);
}
private void mergeOutputTypeColumn(Sheet sheet, int startRow, int endRow, String outputType, CellStyle style) {
if (startRow < 0 || endRow < startRow) {
return;
}
setMergedRegionText(sheet, startRow, endRow, 2, 2, safeText(outputType), style);
}
private List<QuarterRow> extractDetailRows(ExportData data) {
if (data == null || data.getRows() == null || data.getRows().isEmpty()) {
return Collections.emptyList();
}
List<QuarterRow> detailRows = new ArrayList<>();
for (QuarterRow row : data.getRows()) {
if (row != null && !row.isTotalRow()) {
detailRows.add(row);
}
}
detailRows.sort(Comparator
.comparingInt((QuarterRow row) -> outputTypeOrder(row.getOutputType()))
.thenComparing(QuarterRow::getSerialNo, Comparator.nullsLast(Integer::compareTo)));
return detailRows;
}
private QuarterRow resolveProjectTotalRow(ExportData data) {
if (data == null || data.getRows() == null) {
return null;
}
for (QuarterRow row : data.getRows()) {
if (row != null && row.isTotalRow()) {
return row;
}
}
return null;
}
private List<OutputTypeGroup> buildOutputTypeGroups(ExportData data) {
List<QuarterRow> detailRows = extractDetailRows(data);
int nextSerialNo = detailRows.stream()
.map(QuarterRow::getSerialNo)
.filter(item -> item != null)
.max(Integer::compareTo)
.orElse(0) + 1;
Map<String, List<QuarterRow>> groupedMap = new LinkedHashMap<>();
for (QuarterRow row : detailRows) {
groupedMap.computeIfAbsent(safeText(row.getOutputType()), key -> new ArrayList<>()).add(row);
}
List<OutputTypeGroup> result = new ArrayList<>();
for (String outputType : FIXED_OUTPUT_TYPES) {
List<QuarterRow> rows = groupedMap.remove(outputType);
if (rows == null || rows.isEmpty()) {
rows = new ArrayList<>();
rows.add(buildPlaceholderRow(outputType, nextSerialNo++));
}
OutputTypeGroup group = new OutputTypeGroup();
group.setOutputType(outputType);
group.setRows(rows);
result.add(group);
}
for (Map.Entry<String, List<QuarterRow>> entry : groupedMap.entrySet()) {
OutputTypeGroup group = new OutputTypeGroup();
group.setOutputType(entry.getKey());
group.setRows(entry.getValue());
result.add(group);
}
return result;
}
private QuarterRow buildPlaceholderRow(String outputType, Integer serialNo) {
QuarterRow row = new QuarterRow();
row.setSerialNo(serialNo);
row.setOutputType(outputType);
row.setDesignContent("");
row.setPlaceholderRow(true);
return row;
}
private String defaultSignatureLabel(String value, String fallback) {
String text = safeText(value);
return text.isEmpty() ? fallback : text;
}
private BigDecimal addAmount(BigDecimal left, BigDecimal right) {
return amountOrZero(left).add(amountOrZero(right)).setScale(2, RoundingMode.HALF_UP);
}
private BigDecimal amountOrZero(BigDecimal value) {
return value == null ? zeroAmount() : value.setScale(2, RoundingMode.HALF_UP);
}
private BigDecimal zeroAmount() {
return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
}
private BigDecimal multiplyAmountByRatio(BigDecimal amount, BigDecimal ratioValue) {
return amountOrZero(amount).multiply(ratio(ratioValue)).setScale(2, RoundingMode.HALF_UP);
}
private BigDecimal resolveSpecialtyRatio(QuarterRow row, int index) {
switch (index) {
case 0:
return row.getArchRatio();
case 1:
return row.getDecorRatio();
case 2:
return row.getStructRatio();
case 3:
return row.getWaterRatio();
case 4:
return row.getHvacRatio();
case 5:
return row.getElecRatio();
case 6:
return row.getDigitalRatio();
default:
return null;
}
}
private BigDecimal resolveSpecialtyAssessmentOutputWan(QuarterRow row, int index) {
switch (index) {
case 0:
return row.getArchAssessmentOutputWan();
case 1:
return row.getDecorAssessmentOutputWan();
case 2:
return row.getStructAssessmentOutputWan();
case 3:
return row.getWaterAssessmentOutputWan();
case 4:
return row.getHvacAssessmentOutputWan();
case 5:
return row.getElecAssessmentOutputWan();
case 6:
return row.getDigitalAssessmentOutputWan();
default:
return null;
}
}
private int[] buildSheet2ColumnWidths() {
int[] widths = new int[SHEET2_BASE_COL_COUNT + SPECIALTY_NAMES.size() * SHEET2_SPECIALTY_BLOCK_WIDTH];
widths[0] = 10;
widths[1] = 22;
widths[2] = 16;
widths[3] = 16;
int index = SHEET2_BASE_COL_COUNT;
for (int i = 0; i < SPECIALTY_NAMES.size(); i++) {
widths[index++] = 12;
widths[index++] = 12;
widths[index++] = 12;
widths[index++] = 12;
widths[index++] = 14;
}
return widths;
}
private int sheet1LastCol() {
return SHEET1_SPECIALTY_AMOUNT_START_COL + SPECIALTY_NAMES.size() - 1;
}
private int sheet2LastCol() {
return SHEET2_BASE_COL_COUNT + SPECIALTY_NAMES.size() * SHEET2_SPECIALTY_BLOCK_WIDTH - 1;
}
private String textOrBlank(BigDecimal value) {
if (value == null) {
return "";
@@ -245,6 +635,7 @@ public class ProjectQuarterOutputExcelBuilder extends AbstractProjectOutputExcel
public static class QuarterRow {
private Integer serialNo;
private boolean totalRow;
private boolean placeholderRow;
private String outputType;
private String designContent;
private BigDecimal quarterOneAmountWan;
@@ -272,12 +663,29 @@ public class ProjectQuarterOutputExcelBuilder extends AbstractProjectOutputExcel
private BigDecimal waterRatio;
private BigDecimal hvacRatio;
private BigDecimal elecRatio;
private BigDecimal digitalRatio;
private BigDecimal archAssessmentOutputWan;
private BigDecimal decorAssessmentOutputWan;
private BigDecimal structAssessmentOutputWan;
private BigDecimal waterAssessmentOutputWan;
private BigDecimal hvacAssessmentOutputWan;
private BigDecimal elecAssessmentOutputWan;
private BigDecimal digitalAssessmentOutputWan;
}
@Data
private static class OutputTypeGroup {
private String outputType;
private List<QuarterRow> rows;
}
@Data
private static class Sheet2SpecialtyBlock {
private BigDecimal quarterOneAmountWan;
private BigDecimal quarterTwoAmountWan;
private BigDecimal quarterThreeAmountWan;
private BigDecimal quarterFourAmountWan;
private BigDecimal yearTotalAmountWan;
}
}

View File

@@ -1,81 +1,653 @@
package cn.iocoder.lyzsys.module.tjt.service.report.builder;
import cn.iocoder.lyzsys.module.tjt.enums.OutputSplitBizConstants;
import lombok.Data;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Component
public class SpecialtyPersonOutputExcelBuilder extends AbstractProjectOutputExcelBuilder {
private static final String SHEET1_NAME = "工作量分配";
private static final String SHEET2_NAME = "季度考核产值";
private static final String SHEET1_TITLE = "7.1.4 专业内人员项目考核产值年度 / 季度预算计取表(以一个专业作为参考,各专业相同)";
private static final String SHEET2_TITLE = "专业内人员年度 / 季度项目考核产值(万元)";
private static final String SHEET1_SECTION_TITLE = "专业内人员工作量分配";
private static final String SHEET2_SECTION_TITLE = "专业项目考核产值(万元)";
private static final String SHEET2_PERSON_SECTION_TITLE = "专业内人员年度 / 季度项目考核产值(万元)";
private static final String DEFAULT_CENTER_SIGNER = "设计中心相关负责人(签名):";
private static final String DEFAULT_PROJECT_SIGNER = "项目经理 / 工程负责人(签名):";
private static final String DEFAULT_SPECIALTY_SIGNER = "专业负责人(签名):";
private static final String DEFAULT_DESIGNER_SIGNER = "参与设计人员(签名):";
private static final int SHEET1_FIXED_COL_COUNT = 4;
private static final int SHEET1_ROLE_RATIO_WIDTH = 1;
private static final int SHEET1_PERSON_RATIO_WIDTH = 2;
private static final int SHEET1_ADJUSTED_PERSON_WIDTH = 1;
private static final int SHEET1_REMARK_COL_COUNT = 1;
private static final int SHEET2_FIXED_COL_COUNT = 4;
private static final int SHEET2_SPECIALTY_BLOCK_WIDTH = 5;
private static final int SHEET2_PERSON_BLOCK_WIDTH = 5;
private static final List<String> ROLE_ORDER = Arrays.asList(
OutputSplitBizConstants.ROLE_DIRECTOR,
OutputSplitBizConstants.ROLE_CHECK,
OutputSplitBizConstants.ROLE_DESIGN,
OutputSplitBizConstants.ROLE_REVIEW,
OutputSplitBizConstants.ROLE_APPROVE
);
public Workbook build(ExportData data) {
Workbook workbook = createWorkbook();
Sheet sheet = createSheet(workbook, "专业内人员计取", "专业内人员计取");
setColumnWidths(sheet, 8, 20, 14, 16, 12, 12, 12, 12, 12, 12);
buildSheet1(workbook, data);
buildSheet2(workbook, data);
return workbook;
}
CellStyle titleStyle = createTitleStyle(workbook);
CellStyle infoStyle = createInfoStyle(workbook);
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle cellStyle = createCellStyle(workbook);
private void buildSheet1(Workbook workbook, ExportData data) {
List<RoleGroup> roleGroups = buildRoleGroups(data);
List<PersonAmountModule> adjustedPersonModules = resolvePersonAmountModules(data);
Sheet sheet = createSheet(workbook, SHEET1_NAME, SHEET1_NAME);
setColumnWidths(sheet, buildSheet1ColumnWidths(roleGroups, adjustedPersonModules));
CellStyle baseHeaderStyle = createHeaderStyle(workbook);
CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle headerValueStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, false, (short) 10);
CellStyle cellStyle = createDerivedStyle(workbook, createCellStyle(workbook),
null, HorizontalAlignment.CENTER, false, (short) 10);
CellStyle leftCellStyle = createDerivedStyle(workbook, cellStyle,
null, HorizontalAlignment.LEFT, false, (short) 10);
int rowIndex = 0;
rowIndex = writeMergedTitleRow(sheet, rowIndex, "专业内人员项目考核产值年度/季度计取表", titleStyle, 9);
rowIndex = buildSheet1Header(sheet, rowIndex, data, roleGroups, adjustedPersonModules,
headerStyle, headerValueStyle);
Row infoRowOne = sheet.createRow(rowIndex++);
writeLabelValue(infoRowOne, 0, 1, "项目名称", data.getProjectName(), infoStyle);
writeLabelValue(infoRowOne, 4, 5, "规划内容", data.getPlanningContent(), infoStyle);
Row infoRowTwo = sheet.createRow(rowIndex++);
writeLabelValue(infoRowTwo, 0, 1, "专业", data.getSpecialtyName(), infoStyle);
writeLabelValue(infoRowTwo, 4, 5, "年度", data.getYear(), infoStyle);
rowIndex = writeRow(sheet, rowIndex, headerStyle, (Object[]) new String[]{
"序号", "角色", "员工姓名", "角色比例", "人员比例",
"一季度", "二季度", "三季度", "四季度", "年度合计"
});
int serialNo = 1;
for (PersonQuarterRow rowData : data.getRows()) {
Row row = sheet.createRow(rowIndex++);
setText(row, 0, serialNo++, cellStyle);
setText(row, 1, rowData.getRoleName(), cellStyle);
setText(row, 2, rowData.getEmployeeName(), cellStyle);
setText(row, 3, percentText(rowData.getRoleRatio()), cellStyle);
setText(row, 4, percentText(rowData.getPersonRatio()), cellStyle);
setText(row, 5, text(rowData.getQuarterOneAmount()), cellStyle);
setText(row, 6, text(rowData.getQuarterTwoAmount()), cellStyle);
setText(row, 7, text(rowData.getQuarterThreeAmount()), cellStyle);
setText(row, 8, text(rowData.getQuarterFourAmount()), cellStyle);
setText(row, 9, text(rowData.getYearTotalAmount()), cellStyle);
List<SpecialtyPlanningRow> rows = resolveDetailRows(data);
int bodyStartRow = rowIndex;
for (SpecialtyPlanningRow rowData : rows) {
writeSheet1DetailRow(sheet, rowIndex++, rowData, roleGroups, adjustedPersonModules,
cellStyle, leftCellStyle);
}
return workbook;
mergeSheet1Body(sheet, bodyStartRow, rowIndex - 1, rows, cellStyle);
}
private void buildSheet2(Workbook workbook, ExportData data) {
List<PersonAmountModule> personModules = resolvePersonAmountModules(data);
int lastCol = sheet2LastCol(personModules);
Sheet sheet = createSheet(workbook, SHEET2_NAME, SHEET2_NAME);
setColumnWidths(sheet, buildSheet2ColumnWidths(personModules));
CellStyle baseHeaderStyle = createHeaderStyle(workbook);
CellStyle headerStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.LEMON_CHIFFON, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle cellStyle = createDerivedStyle(workbook, createCellStyle(workbook),
null, HorizontalAlignment.CENTER, false, (short) 10);
CellStyle leftCellStyle = createDerivedStyle(workbook, cellStyle,
null, HorizontalAlignment.LEFT, false, (short) 10);
CellStyle totalStyle = createDerivedStyle(workbook, baseHeaderStyle,
IndexedColors.GOLD, HorizontalAlignment.CENTER, true, (short) 10);
CellStyle signatureStyle = createDerivedStyle(workbook, createInfoStyle(workbook),
null, HorizontalAlignment.LEFT, false, (short) 10);
int rowIndex = 0;
rowIndex = buildSheet2Header(sheet, rowIndex, data, personModules, headerStyle);
List<SpecialtyPlanningRow> rows = resolveSheet2Rows(data);
int bodyStartRow = rowIndex;
for (SpecialtyPlanningRow rowData : rows) {
if (Boolean.TRUE.equals(rowData.getTotalRow())) {
writeSheet2TotalRow(sheet, rowIndex++, rowData, personModules, totalStyle);
continue;
}
writeSheet2DetailRow(sheet, rowIndex++, rowData, personModules, cellStyle, leftCellStyle);
}
mergeSheet2Body(sheet, bodyStartRow, rowIndex - 1, rows, cellStyle);
writeSheet2SignatureRow(sheet, rowIndex, lastCol, signatureStyle);
}
private int buildSheet1Header(Sheet sheet, int rowIndex, ExportData data, List<RoleGroup> roleGroups,
List<PersonAmountModule> adjustedPersonModules,
CellStyle headerStyle, CellStyle headerValueStyle) {
int firstRow = rowIndex;
sheet.createRow(firstRow);
sheet.createRow(firstRow + 1);
sheet.createRow(firstRow + 2);
sheet.createRow(firstRow + 3);
setText(sheet.getRow(firstRow), 0, "工程编号", headerStyle);
setText(sheet.getRow(firstRow), 1, safeText(data == null ? "" : data.getProjectCode()), headerValueStyle);
setText(sheet.getRow(firstRow), 2, "工程负责人", headerStyle);
setText(sheet.getRow(firstRow), 3, safeText(data == null ? "" : data.getEngineeringPrincipalNames()), headerValueStyle);
int remarkCol = sheet1LastCol(roleGroups, adjustedPersonModules);
int adjustedStartCol = sheet1AdjustedStartCol(roleGroups);
int adjustedEndCol = remarkCol - 1;
setMergedRegionText(sheet, firstRow, firstRow, 4, adjustedEndCol, SHEET1_SECTION_TITLE, headerStyle);
setMergedRegionText(sheet, firstRow, firstRow + 3, remarkCol, remarkCol, "备注", headerStyle);
setText(sheet.getRow(firstRow + 1), 0, "专业", headerStyle);
setMergedRegionText(sheet, firstRow + 1, firstRow + 1, 1, 3,
safeText(data == null ? "" : data.getSpecialtyName()), headerValueStyle);
setMergedRegionText(sheet, firstRow + 2, firstRow + 3, 0, 0, "序号", headerStyle);
setMergedRegionText(sheet, firstRow + 2, firstRow + 3, 1, 1, "项目名称", headerStyle);
setMergedRegionText(sheet, firstRow + 2, firstRow + 2, 2, 3, "类别", headerStyle);
setText(sheet.getRow(firstRow + 3), 2, "产值类型", headerStyle);
setText(sheet.getRow(firstRow + 3), 3, "设计内容", headerStyle);
int colIndex = SHEET1_FIXED_COL_COUNT;
for (RoleGroup roleGroup : roleGroups) {
int roleWidth = sheet1RoleWidth(roleGroup);
setMergedRegionText(sheet, firstRow + 1, firstRow + 1, colIndex, colIndex + roleWidth - 1,
roleGroup.getRoleName(), headerStyle);
setMergedRegionText(sheet, firstRow + 2, firstRow + 3, colIndex, colIndex,
"岗位整体占专业比例", headerStyle);
colIndex++;
for (RolePersonModule module : roleGroup.getModules()) {
setMergedRegionText(sheet, firstRow + 2, firstRow + 2, colIndex, colIndex + 1,
safeText(module.getEmployeeName()), headerStyle);
setText(sheet.getRow(firstRow + 3), colIndex, "工作量比例", headerStyle);
setText(sheet.getRow(firstRow + 3), colIndex + 1, "占专业比例", headerStyle);
colIndex += SHEET1_PERSON_RATIO_WIDTH;
}
}
setMergedRegionText(sheet, firstRow + 1, firstRow + 1, adjustedStartCol, adjustedEndCol,
"调整后人员比例", headerStyle);
for (PersonAmountModule module : adjustedPersonModules) {
setMergedRegionText(sheet, firstRow + 2, firstRow + 3, colIndex, colIndex,
safeText(module.getEmployeeName()), headerStyle);
colIndex += SHEET1_ADJUSTED_PERSON_WIDTH;
}
return rowIndex + 4;
}
private void writeSheet1DetailRow(Sheet sheet, int rowIndex, SpecialtyPlanningRow rowData,
List<RoleGroup> roleGroups, List<PersonAmountModule> adjustedPersonModules,
CellStyle cellStyle, CellStyle leftCellStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, rowData.getSerialNo(), cellStyle);
setText(row, 1, safeText(rowData.getProjectName()), cellStyle);
setText(row, 2, safeText(rowData.getOutputType()), cellStyle);
setText(row, 3, safeText(rowData.getDesignContent()), leftCellStyle);
int colIndex = SHEET1_FIXED_COL_COUNT;
for (RoleGroup roleGroup : roleGroups) {
setText(row, colIndex++, percentTextOrBlank(rowData.getRoleRatioMap().get(roleGroup.getRoleCode())), cellStyle);
for (RolePersonModule module : roleGroup.getModules()) {
RolePersonRatioValue value = rowData.getRolePersonValueMap().get(module.getKey());
setText(row, colIndex, value == null ? "" : percentTextOrBlank(value.getWorkRatio()), cellStyle);
setText(row, colIndex + 1, value == null ? "" : percentTextOrBlank(value.getSpecialtyRatio()), cellStyle);
colIndex += SHEET1_PERSON_RATIO_WIDTH;
}
}
for (PersonAmountModule module : adjustedPersonModules) {
setText(row, colIndex++, percentTextOrBlank(rowData.getAdjustedPersonRatioMap().get(module.getKey())), cellStyle);
}
setText(row, colIndex, safeText(rowData.getRemark()), leftCellStyle);
}
private int buildSheet2Header(Sheet sheet, int rowIndex, ExportData data,
List<PersonAmountModule> personModules, CellStyle headerStyle) {
int firstRow = rowIndex;
sheet.createRow(firstRow);
sheet.createRow(firstRow + 1);
sheet.createRow(firstRow + 2);
sheet.createRow(firstRow + 3);
setMergedRegionText(sheet, firstRow, firstRow + 3, 0, 0, "序号", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow + 3, 1, 1, "项目名称", headerStyle);
setMergedRegionText(sheet, firstRow, firstRow + 2, 2, 3, "类别", headerStyle);
setText(sheet.getRow(firstRow + 3), 2, "产值类型", headerStyle);
setText(sheet.getRow(firstRow + 3), 3, "设计内容", headerStyle);
int specialtyStartCol = SHEET2_FIXED_COL_COUNT;
setMergedRegionText(sheet, firstRow, firstRow + 2, specialtyStartCol, specialtyStartCol,
safeText(data == null ? "" : data.getSpecialtyName()), headerStyle);
setMergedRegionText(sheet, firstRow, firstRow + 2, specialtyStartCol + 1,
specialtyStartCol + SHEET2_SPECIALTY_BLOCK_WIDTH - 1, SHEET2_SECTION_TITLE, headerStyle);
writeQuarterHeader(sheet.getRow(firstRow + 3), specialtyStartCol, headerStyle);
int personStartCol = specialtyStartCol + SHEET2_SPECIALTY_BLOCK_WIDTH;
int lastCol = sheet2LastCol(personModules);
setMergedRegionText(sheet, firstRow, firstRow, personStartCol, lastCol, SHEET2_PERSON_SECTION_TITLE, headerStyle);
int colIndex = personStartCol;
for (PersonAmountModule module : personModules) {
setMergedRegionText(sheet, firstRow + 1, firstRow + 2, colIndex, colIndex + SHEET2_PERSON_BLOCK_WIDTH - 1,
safeText(module.getEmployeeName()), headerStyle);
writeQuarterHeader(sheet.getRow(firstRow + 3), colIndex, headerStyle);
colIndex += SHEET2_PERSON_BLOCK_WIDTH;
}
return rowIndex + 4;
}
private void writeSheet2DetailRow(Sheet sheet, int rowIndex, SpecialtyPlanningRow rowData,
List<PersonAmountModule> personModules, CellStyle cellStyle,
CellStyle leftCellStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, rowData.getSerialNo(), cellStyle);
setText(row, 1, safeText(rowData.getProjectName()), cellStyle);
setText(row, 2, safeText(rowData.getOutputType()), cellStyle);
setText(row, 3, safeText(rowData.getDesignContent()), leftCellStyle);
int colIndex = SHEET2_FIXED_COL_COUNT;
setText(row, colIndex, textOrBlank(rowData.getSpecialtyQuarterOneAmountWan()), cellStyle);
setText(row, colIndex + 1, textOrBlank(rowData.getSpecialtyQuarterTwoAmountWan()), cellStyle);
setText(row, colIndex + 2, textOrBlank(rowData.getSpecialtyQuarterThreeAmountWan()), cellStyle);
setText(row, colIndex + 3, textOrBlank(rowData.getSpecialtyQuarterFourAmountWan()), cellStyle);
setText(row, colIndex + 4, textOrBlank(rowData.getSpecialtyYearTotalAmountWan()), cellStyle);
colIndex += SHEET2_SPECIALTY_BLOCK_WIDTH;
for (PersonAmountModule module : personModules) {
PersonAmountValue value = rowData.getPersonAmountMap().get(module.getKey());
setText(row, colIndex, value == null ? "" : textOrBlank(value.getQuarterOneAmountWan()), cellStyle);
setText(row, colIndex + 1, value == null ? "" : textOrBlank(value.getQuarterTwoAmountWan()), cellStyle);
setText(row, colIndex + 2, value == null ? "" : textOrBlank(value.getQuarterThreeAmountWan()), cellStyle);
setText(row, colIndex + 3, value == null ? "" : textOrBlank(value.getQuarterFourAmountWan()), cellStyle);
setText(row, colIndex + 4, value == null ? "" : textOrBlank(value.getYearTotalAmountWan()), cellStyle);
colIndex += SHEET2_PERSON_BLOCK_WIDTH;
}
}
private void writeSheet2TotalRow(Sheet sheet, int rowIndex, SpecialtyPlanningRow rowData,
List<PersonAmountModule> personModules, CellStyle totalStyle) {
Row row = sheet.createRow(rowIndex);
setText(row, 0, "", totalStyle);
setText(row, 1, "", totalStyle);
setMergedRegionText(sheet, rowIndex, rowIndex, 2, 3, "总计", totalStyle);
int colIndex = SHEET2_FIXED_COL_COUNT;
setText(row, colIndex, textOrBlank(rowData.getSpecialtyQuarterOneAmountWan()), totalStyle);
setText(row, colIndex + 1, textOrBlank(rowData.getSpecialtyQuarterTwoAmountWan()), totalStyle);
setText(row, colIndex + 2, textOrBlank(rowData.getSpecialtyQuarterThreeAmountWan()), totalStyle);
setText(row, colIndex + 3, textOrBlank(rowData.getSpecialtyQuarterFourAmountWan()), totalStyle);
setText(row, colIndex + 4, textOrBlank(rowData.getSpecialtyYearTotalAmountWan()), totalStyle);
colIndex += SHEET2_SPECIALTY_BLOCK_WIDTH;
for (PersonAmountModule module : personModules) {
PersonAmountValue value = rowData.getPersonAmountMap().get(module.getKey());
setText(row, colIndex, value == null ? "" : textOrBlank(value.getQuarterOneAmountWan()), totalStyle);
setText(row, colIndex + 1, value == null ? "" : textOrBlank(value.getQuarterTwoAmountWan()), totalStyle);
setText(row, colIndex + 2, value == null ? "" : textOrBlank(value.getQuarterThreeAmountWan()), totalStyle);
setText(row, colIndex + 3, value == null ? "" : textOrBlank(value.getQuarterFourAmountWan()), totalStyle);
setText(row, colIndex + 4, value == null ? "" : textOrBlank(value.getYearTotalAmountWan()), totalStyle);
colIndex += SHEET2_PERSON_BLOCK_WIDTH;
}
}
private void writeSheet2SignatureRow(Sheet sheet, int rowIndex, int lastCol, CellStyle signatureStyle) {
sheet.createRow(rowIndex);
int totalColCount = lastCol + 1;
int segmentWidth = Math.max(1, totalColCount / 4);
int col1End = Math.min(lastCol, segmentWidth - 1);
int col2End = Math.min(lastCol, col1End + segmentWidth);
int col3End = Math.min(lastCol, col2End + segmentWidth);
setMergedRegionText(sheet, rowIndex, rowIndex, 0, col1End, DEFAULT_CENTER_SIGNER, signatureStyle);
setMergedRegionText(sheet, rowIndex, rowIndex, col1End + 1, col2End, DEFAULT_PROJECT_SIGNER, signatureStyle);
setMergedRegionText(sheet, rowIndex, rowIndex, col2End + 1, col3End, DEFAULT_SPECIALTY_SIGNER, signatureStyle);
setMergedRegionText(sheet, rowIndex, rowIndex, col3End + 1, lastCol, DEFAULT_DESIGNER_SIGNER, signatureStyle);
}
private void mergeSheet1Body(Sheet sheet, int startRow, int endRow, List<SpecialtyPlanningRow> rows, CellStyle style) {
if (startRow < 0 || endRow < startRow || rows.isEmpty()) {
return;
}
setMergedRegionText(sheet, startRow, endRow, 1, 1, safeText(rows.get(0).getProjectName()), style);
mergeOutputTypeColumn(sheet, startRow, rows, style);
}
private void mergeSheet2Body(Sheet sheet, int startRow, int endRow, List<SpecialtyPlanningRow> rows,
CellStyle style) {
if (startRow < 0 || endRow < startRow || rows.isEmpty()) {
return;
}
int detailEndIndex = lastDetailIndex(rows);
if (detailEndIndex < 0) {
return;
}
setMergedRegionText(sheet, startRow, endRow, 1, 1, safeText(rows.get(0).getProjectName()), style);
mergeOutputTypeColumn(sheet, startRow, rows.subList(0, detailEndIndex + 1), style);
}
private void mergeOutputTypeColumn(Sheet sheet, int bodyStartRow, List<SpecialtyPlanningRow> rows, CellStyle style) {
if (rows == null || rows.isEmpty()) {
return;
}
int groupStartIndex = 0;
String currentOutputType = safeText(rows.get(0).getOutputType());
for (int i = 1; i < rows.size(); i++) {
String nextOutputType = safeText(rows.get(i).getOutputType());
if (!Objects.equals(currentOutputType, nextOutputType)) {
setMergedRegionText(sheet, bodyStartRow + groupStartIndex, bodyStartRow + i - 1,
2, 2, currentOutputType, style);
groupStartIndex = i;
currentOutputType = nextOutputType;
}
}
setMergedRegionText(sheet, bodyStartRow + groupStartIndex, bodyStartRow + rows.size() - 1,
2, 2, currentOutputType, style);
}
private List<RoleGroup> buildRoleGroups(ExportData data) {
Map<String, List<RolePersonModule>> groupedMap = new LinkedHashMap<>();
if (data != null && data.getRolePersonModules() != null) {
for (RolePersonModule module : data.getRolePersonModules()) {
if (module == null) {
continue;
}
groupedMap.computeIfAbsent(safeText(module.getRoleCode()), key -> new ArrayList<>()).add(module);
}
}
List<RoleGroup> result = new ArrayList<>();
for (String roleCode : ROLE_ORDER) {
List<RolePersonModule> modules = groupedMap.get(roleCode);
if (modules == null || modules.isEmpty()) {
modules = new ArrayList<>();
RolePersonModule placeholder = new RolePersonModule();
placeholder.setKey(roleCode + "#placeholder");
placeholder.setRoleCode(roleCode);
placeholder.setRoleName(OutputSplitBizConstants.getRoleName(roleCode));
placeholder.setEmployeeName("");
placeholder.setPlaceholder(Boolean.TRUE);
modules.add(placeholder);
}
RoleGroup group = new RoleGroup();
group.setRoleCode(roleCode);
group.setRoleName(resolveRoleName(modules, roleCode));
group.setModules(modules);
result.add(group);
}
return result;
}
private List<PersonAmountModule> resolvePersonAmountModules(ExportData data) {
if (data == null || data.getPersonAmountModules() == null || data.getPersonAmountModules().isEmpty()) {
PersonAmountModule placeholder = new PersonAmountModule();
placeholder.setKey("person#placeholder");
placeholder.setEmployeeName("");
placeholder.setPlaceholder(Boolean.TRUE);
return Collections.singletonList(placeholder);
}
return data.getPersonAmountModules().stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private List<SpecialtyPlanningRow> resolveDetailRows(ExportData data) {
if (data == null || data.getRows() == null) {
return Collections.emptyList();
}
List<SpecialtyPlanningRow> rows = data.getRows().stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
rows.sort(Comparator
.comparingInt((SpecialtyPlanningRow row) -> outputTypeOrder(row.getOutputType()))
.thenComparing(SpecialtyPlanningRow::getSerialNo, Comparator.nullsLast(Integer::compareTo)));
return rows;
}
private List<SpecialtyPlanningRow> resolveSheet2Rows(ExportData data) {
List<SpecialtyPlanningRow> detailRows = resolveDetailRows(data);
if (detailRows.isEmpty()) {
return detailRows;
}
List<SpecialtyPlanningRow> rows = new ArrayList<>(detailRows);
rows.add(buildSheet2TotalRow(detailRows));
return rows;
}
private SpecialtyPlanningRow buildSheet2TotalRow(List<SpecialtyPlanningRow> detailRows) {
SpecialtyPlanningRow row = new SpecialtyPlanningRow();
row.setTotalRow(Boolean.TRUE);
row.setDesignContent("总计");
for (SpecialtyPlanningRow detailRow : detailRows) {
row.setSpecialtyQuarterOneAmountWan(addAmount(row.getSpecialtyQuarterOneAmountWan(),
detailRow.getSpecialtyQuarterOneAmountWan()));
row.setSpecialtyQuarterTwoAmountWan(addAmount(row.getSpecialtyQuarterTwoAmountWan(),
detailRow.getSpecialtyQuarterTwoAmountWan()));
row.setSpecialtyQuarterThreeAmountWan(addAmount(row.getSpecialtyQuarterThreeAmountWan(),
detailRow.getSpecialtyQuarterThreeAmountWan()));
row.setSpecialtyQuarterFourAmountWan(addAmount(row.getSpecialtyQuarterFourAmountWan(),
detailRow.getSpecialtyQuarterFourAmountWan()));
row.setSpecialtyYearTotalAmountWan(addAmount(row.getSpecialtyYearTotalAmountWan(),
detailRow.getSpecialtyYearTotalAmountWan()));
for (Map.Entry<String, PersonAmountValue> entry : detailRow.getPersonAmountMap().entrySet()) {
PersonAmountValue totalValue = row.getPersonAmountMap().computeIfAbsent(entry.getKey(),
key -> new PersonAmountValue());
PersonAmountValue currentValue = entry.getValue();
totalValue.setQuarterOneAmountWan(addAmount(totalValue.getQuarterOneAmountWan(),
currentValue == null ? null : currentValue.getQuarterOneAmountWan()));
totalValue.setQuarterTwoAmountWan(addAmount(totalValue.getQuarterTwoAmountWan(),
currentValue == null ? null : currentValue.getQuarterTwoAmountWan()));
totalValue.setQuarterThreeAmountWan(addAmount(totalValue.getQuarterThreeAmountWan(),
currentValue == null ? null : currentValue.getQuarterThreeAmountWan()));
totalValue.setQuarterFourAmountWan(addAmount(totalValue.getQuarterFourAmountWan(),
currentValue == null ? null : currentValue.getQuarterFourAmountWan()));
totalValue.setYearTotalAmountWan(addAmount(totalValue.getYearTotalAmountWan(),
currentValue == null ? null : currentValue.getYearTotalAmountWan()));
}
}
return row;
}
private int[] buildSheet1ColumnWidths(List<RoleGroup> roleGroups, List<PersonAmountModule> adjustedPersonModules) {
List<Integer> widths = new ArrayList<>();
widths.add(8);
widths.add(24);
widths.add(16);
widths.add(24);
for (RoleGroup roleGroup : roleGroups) {
widths.add(16);
for (int i = 0; i < roleGroup.getModules().size(); i++) {
widths.add(12);
widths.add(12);
}
}
for (int i = 0; i < adjustedPersonModules.size(); i++) {
widths.add(14);
}
widths.add(20);
return widths.stream().mapToInt(Integer::intValue).toArray();
}
private int[] buildSheet2ColumnWidths(List<PersonAmountModule> personModules) {
List<Integer> widths = new ArrayList<>();
widths.add(8);
widths.add(24);
widths.add(16);
widths.add(24);
appendQuarterBlockWidths(widths);
for (int i = 0; i < personModules.size(); i++) {
appendQuarterBlockWidths(widths);
}
return widths.stream().mapToInt(Integer::intValue).toArray();
}
private void appendQuarterBlockWidths(List<Integer> widths) {
widths.add(12);
widths.add(12);
widths.add(12);
widths.add(12);
widths.add(14);
}
private void writeQuarterHeader(Row row, int startCol, CellStyle style) {
setText(row, startCol, "一季度", style);
setText(row, startCol + 1, "二季度", style);
setText(row, startCol + 2, "三季度", style);
setText(row, startCol + 3, "四季度", style);
setText(row, startCol + 4, "本年度小计", style);
}
private int sheet1LastCol(List<RoleGroup> roleGroups, List<PersonAmountModule> adjustedPersonModules) {
int totalColumns = SHEET1_FIXED_COL_COUNT + SHEET1_REMARK_COL_COUNT
+ adjustedPersonModules.size() * SHEET1_ADJUSTED_PERSON_WIDTH;
for (RoleGroup roleGroup : roleGroups) {
totalColumns += sheet1RoleWidth(roleGroup);
}
return totalColumns - 1;
}
private int sheet1AdjustedStartCol(List<RoleGroup> roleGroups) {
int col = SHEET1_FIXED_COL_COUNT;
for (RoleGroup roleGroup : roleGroups) {
col += sheet1RoleWidth(roleGroup);
}
return col;
}
private int sheet1RoleWidth(RoleGroup roleGroup) {
int personCount = roleGroup == null || roleGroup.getModules() == null ? 1 : roleGroup.getModules().size();
return SHEET1_ROLE_RATIO_WIDTH + personCount * SHEET1_PERSON_RATIO_WIDTH;
}
private int sheet2LastCol(List<PersonAmountModule> personModules) {
return SHEET2_FIXED_COL_COUNT + SHEET2_SPECIALTY_BLOCK_WIDTH
+ personModules.size() * SHEET2_PERSON_BLOCK_WIDTH - 1;
}
private int outputTypeOrder(String outputType) {
String current = safeText(outputType);
if (Objects.equals(current, "六大专业考核产值")) {
return 1;
}
if (Objects.equals(current, "专业分包产值")) {
return 2;
}
if (Objects.equals(current, "内部协作产值")) {
return 3;
}
return 9;
}
private int lastDetailIndex(List<SpecialtyPlanningRow> rows) {
for (int i = rows.size() - 1; i >= 0; i--) {
if (!Boolean.TRUE.equals(rows.get(i).getTotalRow())) {
return i;
}
}
return -1;
}
private BigDecimal addAmount(BigDecimal left, BigDecimal right) {
return amount(left).add(amount(right)).setScale(2, RoundingMode.HALF_UP);
}
private String resolveRoleName(List<RolePersonModule> modules, String roleCode) {
if (modules != null) {
for (RolePersonModule module : modules) {
if (module != null && !safeText(module.getRoleName()).isEmpty()) {
return safeText(module.getRoleName());
}
}
}
return OutputSplitBizConstants.getRoleName(roleCode);
}
private String textOrBlank(BigDecimal value) {
return value == null ? "" : value.setScale(2, RoundingMode.HALF_UP).toPlainString();
}
private String percentTextOrBlank(BigDecimal value) {
return value == null ? "" : percentText(value);
}
@Data
public static class ExportData {
private String projectCode;
private String projectName;
private String planningContent;
private String specialtyName;
private String projectManagerNames;
private String engineeringPrincipalNames;
private BigDecimal projectManagerRatio;
private BigDecimal engineeringPrincipalRatio;
private Integer year;
private List<PersonQuarterRow> rows;
private String centerSignerLabel;
private String projectSignerLabel;
private List<RolePersonModule> rolePersonModules;
private List<PersonAmountModule> personAmountModules;
private List<SpecialtyPlanningRow> rows;
}
@Data
public static class PersonQuarterRow {
public static class RolePersonModule {
private String key;
private String roleCode;
private String roleName;
private String employeeName;
private BigDecimal roleRatio;
private BigDecimal personRatio;
private BigDecimal quarterOneAmount;
private BigDecimal quarterTwoAmount;
private BigDecimal quarterThreeAmount;
private BigDecimal quarterFourAmount;
private BigDecimal yearTotalAmount;
private Boolean placeholder;
}
@Data
public static class PersonAmountModule {
private String key;
private String employeeName;
private Boolean placeholder;
}
@Data
public static class SpecialtyPlanningRow {
private Integer serialNo;
private String projectName;
private String outputType;
private String designContent;
private BigDecimal specialtyRatio;
private BigDecimal specialtyQuarterOneAmountWan;
private BigDecimal specialtyQuarterTwoAmountWan;
private BigDecimal specialtyQuarterThreeAmountWan;
private BigDecimal specialtyQuarterFourAmountWan;
private BigDecimal specialtyYearTotalAmountWan;
private BigDecimal adjustedPersonRatio;
private String remark;
private Boolean totalRow;
private Map<String, BigDecimal> roleRatioMap = new LinkedHashMap<>();
private Map<String, RolePersonRatioValue> rolePersonValueMap = new LinkedHashMap<>();
private Map<String, BigDecimal> adjustedPersonRatioMap = new LinkedHashMap<>();
private Map<String, PersonAmountValue> personAmountMap = new LinkedHashMap<>();
}
@Data
public static class RolePersonRatioValue {
private BigDecimal workRatio;
private BigDecimal specialtyRatio;
}
@Data
public static class PersonAmountValue {
private BigDecimal quarterOneAmountWan;
private BigDecimal quarterTwoAmountWan;
private BigDecimal quarterThreeAmountWan;
private BigDecimal quarterFourAmountWan;
private BigDecimal yearTotalAmountWan;
}
@Data
private static class RoleGroup {
private String roleCode;
private String roleName;
private List<RolePersonModule> modules;
}
}