汇总结果表检查表的金额

This commit is contained in:
2026-05-27 12:41:09 +08:00
parent 0258079d49
commit 48c5b2a856

View File

@@ -34,12 +34,7 @@ import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.*;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* 当日用户交易报表 Excel 导出POI 样式与写入逻辑)。 * 当日用户交易报表 Excel 导出POI 样式与写入逻辑)。
@@ -60,7 +55,9 @@ public final class LbDailyUserTradeReportExportSupport {
private static final int RESULT_SHEET_INDEX = 6; private static final int RESULT_SHEET_INDEX = 6;
private static final int RESULT_CHECK_SHEET_INDEX = 7; private static final int RESULT_CHECK_SHEET_INDEX = 7;
/** 结果表检查表MO 留白P 用户ID、Q 昵称、R 最终收付款;其后为买卖明细/汇总。 */ /** 结果表检查表M/N 先按 K 列、再按 I 列补写 L 合计M 已有值不覆盖O 留白P 起为买卖检查列。 */
private static final int RESULT_CHECK_COL_IK_NICKNAME_L_SUM = 12;
private static final int RESULT_CHECK_COL_CURRENT_NICKNAME = 13;
private static final int RESULT_CHECK_COL_USER_ID = 15; private static final int RESULT_CHECK_COL_USER_ID = 15;
private static final int RESULT_CHECK_COL_NICKNAME = 16; private static final int RESULT_CHECK_COL_NICKNAME = 16;
private static final int RESULT_CHECK_COL_FINAL_SETTLEMENT = 17; private static final int RESULT_CHECK_COL_FINAL_SETTLEMENT = 17;
@@ -411,7 +408,7 @@ public final class LbDailyUserTradeReportExportSupport {
autoSizeColumns(resultSheet, RESULT_LAST_COL); autoSizeColumns(resultSheet, RESULT_LAST_COL);
} }
/** 结果表 / 结果表检查表L 列(匹配金额)求和,写入最后一行。 */ /** 结果表L 列(匹配金额)求和,写入最后一行。 */
private static void writeResultMatchColumnLSum(Sheet sheet, SheetStyles styles) { private static void writeResultMatchColumnLSum(Sheet sheet, SheetStyles styles) {
int lastRow = sheet.getLastRowNum(); int lastRow = sheet.getLastRowNum();
if (lastRow < 1) { if (lastRow < 1) {
@@ -463,7 +460,7 @@ public final class LbDailyUserTradeReportExportSupport {
Map<String, BigDecimal> sellTotalBySellerId, Map<String, BigDecimal> sellTotalBySellerId,
Map<String, String> sellDetailBySellerId, Map<String, String> sellDetailBySellerId,
SheetStyles styles) { SheetStyles styles) {
copyResultSheetContent(resultSource, target); copyResultSheetContent(resultSource, target, true);
writeResultCheckExtraHeaders(target, styles); writeResultCheckExtraHeaders(target, styles);
Map<String, String> nicknameToUserId = userIdByNickname == null ? Map.of() : userIdByNickname; Map<String, String> nicknameToUserId = userIdByNickname == null ? Map.of() : userIdByNickname;
Map<String, BigDecimal> buyTotalMap = buyTotalByBuyerId == null ? Map.of() : buyTotalByBuyerId; Map<String, BigDecimal> buyTotalMap = buyTotalByBuyerId == null ? Map.of() : buyTotalByBuyerId;
@@ -477,7 +474,7 @@ public final class LbDailyUserTradeReportExportSupport {
for (int rowIdx = 1; rowIdx <= lastRow; rowIdx++) { for (int rowIdx = 1; rowIdx <= lastRow; rowIdx++) {
Row row = target.getRow(rowIdx); Row row = target.getRow(rowIdx);
if (row == null) { if (row == null || isResultMatchSumLabelRow(row)) {
continue; continue;
} }
String iName = normalizeResultCheckNickname( String iName = normalizeResultCheckNickname(
@@ -519,7 +516,7 @@ public final class LbDailyUserTradeReportExportSupport {
for (int rowIdx = 1; rowIdx <= lastRow; rowIdx++) { for (int rowIdx = 1; rowIdx <= lastRow; rowIdx++) {
Row row = target.getRow(rowIdx); Row row = target.getRow(rowIdx);
if (row == null) { if (row == null || isResultMatchSumLabelRow(row)) {
continue; continue;
} }
String kName = normalizeResultCheckNickname( String kName = normalizeResultCheckNickname(
@@ -542,10 +539,247 @@ public final class LbDailyUserTradeReportExportSupport {
lastRow = Math.max(lastRow, summaryRow); lastRow = Math.max(lastRow, summaryRow);
} }
writeResultMatchColumnLSum(target, styles); fillResultCheckColumnsMandNByIandKNickname(target, styles);
writeResultCheckColumnLAndMSum(target, styles);
autoSizeColumns(target, RESULT_CHECK_LAST_COL); autoSizeColumns(target, RESULT_CHECK_LAST_COL);
} }
/** 结果表检查表:末行追加 L、M 列数据合计。 */
private static void writeResultCheckColumnLAndMSum(Sheet sheet, SheetStyles styles) {
int lastRow = sheet.getLastRowNum();
if (lastRow < 1) {
return;
}
int dataEndRow = lastRow;
Row trailingRow = sheet.getRow(lastRow);
if (trailingRow != null && isResultMatchSumLabelRow(trailingRow)) {
dataEndRow = lastRow - 1;
}
if (dataEndRow < 1) {
return;
}
BigDecimal sumL = BigDecimal.ZERO;
BigDecimal sumM = BigDecimal.ZERO;
for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) {
Row row = sheet.getRow(rowIdx);
if (row == null || isResultMatchSumLabelRow(row)) {
continue;
}
sumL = sumL.add(readNumericCellValue(row.getCell(RESULT_MATCH_AMT)));
if (!isResultCheckColumnMEmpty(row)) {
sumM = sumM.add(readNumericCellValue(row.getCell(RESULT_CHECK_COL_IK_NICKNAME_L_SUM)));
}
}
int sumRowIdx = dataEndRow + 1;
Row sumRow = sheet.getRow(sumRowIdx);
if (sumRow == null) {
sumRow = sheet.createRow(sumRowIdx);
applyRowHeight(sumRow, styles);
}
createTextCell(sumRow, RESULT_MATCH_NEG_NAME, "求和", styles.summaryTextStyle);
createPaymentActualCell(sumRow, RESULT_MATCH_AMT, sumL, styles.summaryRoundedAmountStyle);
createPaymentActualCell(sumRow, RESULT_CHECK_COL_IK_NICKNAME_L_SUM, sumM, styles.summaryRoundedAmountStyle);
}
/**
* 结果表检查表:先按 K 列昵称汇总 L 写入 M、N再按 I 列昵称汇总 L 补写 M、NM 已有值不覆盖)。
*/
private static void fillResultCheckColumnsMandNByIandKNickname(Sheet sheet, SheetStyles styles) {
int dataEndRow = resolveResultCheckDataEndRow(sheet);
if (dataEndRow < 1) {
return;
}
fillResultCheckColumnsMandNByKNickname(sheet, dataEndRow, styles);
fillResultCheckColumnsMandNByINicknameIfMEmpty(sheet, dataEndRow, styles);
}
private static int resolveResultCheckDataEndRow(Sheet sheet) {
int lastRow = sheet.getLastRowNum();
if (lastRow < 1) {
return -1;
}
Row trailingRow = sheet.getRow(lastRow);
if (trailingRow != null && isResultMatchSumLabelRow(trailingRow)) {
return lastRow - 1;
}
return lastRow;
}
/** K 列昵称汇总M、N 仅在连续相同 K 昵称块的最后一行展示。 */
private static void fillResultCheckColumnsMandNByKNickname(Sheet sheet, int dataEndRow, SheetStyles styles) {
Map<String, BigDecimal> sumByKNickname = new HashMap<>();
for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) {
Row row = sheet.getRow(rowIdx);
if (row == null || isResultMatchSumLabelRow(row)) {
continue;
}
String iName = normalizeResultCheckNickname(
readPaymentCellText(row.getCell(RESULT_MATCH_NEG_NAME)));
String kName = normalizeResultCheckNickname(
readPaymentCellText(row.getCell(RESULT_MATCH_POS_NAME)));
if (!includeRowForColumnKSum(iName, kName)) {
continue;
}
BigDecimal lValue = readNumericCellValue(row.getCell(RESULT_MATCH_AMT));
sumByKNickname.merge(kName, lValue, BigDecimal::add);
}
Set<Integer> displayRows = resolveConsecutiveLastRowsByNickname(
sheet, dataEndRow, LbDailyUserTradeReportExportSupport::includeRowForColumnKSum,
(iName, kName) -> kName);
writeResultCheckSummaryColumns(
sheet, dataEndRow, displayRows, sumByKNickname,
(iName, kName) -> kName, styles, false);
}
/** I 列昵称汇总:仅在 M 列为空时写入M、N 仅在连续相同 I 昵称块的最后一行展示。 */
private static void fillResultCheckColumnsMandNByINicknameIfMEmpty(
Sheet sheet, int dataEndRow, SheetStyles styles) {
Map<String, BigDecimal> sumByINickname = new HashMap<>();
for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) {
Row row = sheet.getRow(rowIdx);
if (row == null || isResultMatchSumLabelRow(row)) {
continue;
}
String iName = normalizeResultCheckNickname(
readPaymentCellText(row.getCell(RESULT_MATCH_NEG_NAME)));
if (iName.isEmpty()) {
continue;
}
BigDecimal lValue = readNumericCellValue(row.getCell(RESULT_MATCH_AMT));
sumByINickname.merge(iName, lValue, BigDecimal::add);
}
Set<Integer> displayRows = resolveConsecutiveLastRowsByNickname(
sheet, dataEndRow,
(iName, kName) -> !iName.isEmpty(),
(iName, kName) -> iName);
writeResultCheckSummaryColumns(
sheet, dataEndRow, displayRows, sumByINickname,
(iName, kName) -> iName, styles, true);
}
private static void writeResultCheckSummaryColumns(
Sheet sheet,
int dataEndRow,
Set<Integer> displayRows,
Map<String, BigDecimal> sumByNickname,
java.util.function.BiFunction<String, String, String> sumNicknameFn,
SheetStyles styles,
boolean skipWhenMHasValue) {
for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) {
Row row = sheet.getRow(rowIdx);
if (row == null || isResultMatchSumLabelRow(row)) {
continue;
}
String iName = normalizeResultCheckNickname(
readPaymentCellText(row.getCell(RESULT_MATCH_NEG_NAME)));
String kName = normalizeResultCheckNickname(
readPaymentCellText(row.getCell(RESULT_MATCH_POS_NAME)));
String sumNickname = sumNicknameFn.apply(iName, kName);
if (sumNickname == null || sumNickname.isEmpty()) {
if (!skipWhenMHasValue) {
createTextCell(row, RESULT_CHECK_COL_IK_NICKNAME_L_SUM, "", styles.textStyle);
createTextCell(row, RESULT_CHECK_COL_CURRENT_NICKNAME, "", styles.textStyle);
}
continue;
}
if (!displayRows.contains(rowIdx)) {
if (!skipWhenMHasValue) {
createTextCell(row, RESULT_CHECK_COL_IK_NICKNAME_L_SUM, "", styles.textStyle);
createTextCell(row, RESULT_CHECK_COL_CURRENT_NICKNAME, "", styles.textStyle);
}
continue;
}
if (skipWhenMHasValue && !isResultCheckColumnMEmpty(row)) {
continue;
}
BigDecimal sum = sumByNickname.getOrDefault(sumNickname, BigDecimal.ZERO);
createPaymentActualCell(
row, RESULT_CHECK_COL_IK_NICKNAME_L_SUM, sum, paymentActualStyle(rowIdx, styles));
createTextCell(row, RESULT_CHECK_COL_CURRENT_NICKNAME, sumNickname, styles.textStyle);
}
}
private static boolean isResultCheckColumnMEmpty(Row row) {
if (row == null) {
return true;
}
Cell cell = row.getCell(RESULT_CHECK_COL_IK_NICKNAME_L_SUM);
if (cell == null || cell.getCellType() == CellType.BLANK) {
return true;
}
if (cell.getCellType() == CellType.STRING) {
String text = cell.getStringCellValue();
return text == null || text.trim().isEmpty();
}
return false;
}
/** 参与 K 列汇总I、K 列昵称均非空。 */
private static boolean includeRowForColumnKSum(String iName, String kName) {
return !iName.isEmpty() && !kName.isEmpty();
}
/**
* 连续相同汇总昵称块:返回每块最后一行的行号。
*/
private static Set<Integer> resolveConsecutiveLastRowsByNickname(
Sheet sheet,
int dataEndRow,
java.util.function.BiPredicate<String, String> includeRow,
java.util.function.BiFunction<String, String, String> sumNicknameFn) {
Set<Integer> displayRows = new HashSet<>();
String groupNickname = null;
int groupLastRow = -1;
for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) {
Row row = sheet.getRow(rowIdx);
if (row == null || isResultMatchSumLabelRow(row)) {
flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow);
groupNickname = null;
groupLastRow = -1;
continue;
}
String iName = normalizeResultCheckNickname(
readPaymentCellText(row.getCell(RESULT_MATCH_NEG_NAME)));
String kName = normalizeResultCheckNickname(
readPaymentCellText(row.getCell(RESULT_MATCH_POS_NAME)));
if (!includeRow.test(iName, kName)) {
flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow);
groupNickname = null;
groupLastRow = -1;
continue;
}
String sumNickname = sumNicknameFn.apply(iName, kName);
if (sumNickname == null || sumNickname.isEmpty()) {
flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow);
groupNickname = null;
groupLastRow = -1;
continue;
}
if (!sumNickname.equals(groupNickname)) {
flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow);
groupNickname = sumNickname;
}
groupLastRow = rowIdx;
}
flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow);
return displayRows;
}
private static void flushResultCheckNicknameGroup(
Set<Integer> displayRows, String groupNickname, int groupLastRow) {
if (groupNickname != null && groupLastRow >= 0) {
displayRows.add(groupLastRow);
}
}
/** 优先使用 1..lastRow 内未占用的行,否则在表尾追加。 */ /** 优先使用 1..lastRow 内未占用的行,否则在表尾追加。 */
private static int allocateResultCheckSummaryRow( private static int allocateResultCheckSummaryRow(
Sheet sheet, Set<Integer> usedSummaryRows, int lastRow, SheetStyles styles) { Sheet sheet, Set<Integer> usedSummaryRows, int lastRow, SheetStyles styles) {
@@ -614,13 +848,15 @@ public final class LbDailyUserTradeReportExportSupport {
return nickname == null ? "" : nickname.trim(); return nickname == null ? "" : nickname.trim();
} }
/** 结果表检查表 N 列表头(第 1 行)。 */ /** 结果表检查表 MV 列表头(第 1 行)。 */
private static void writeResultCheckExtraHeaders(Sheet sheet, SheetStyles styles) { private static void writeResultCheckExtraHeaders(Sheet sheet, SheetStyles styles) {
Row header = sheet.getRow(0); Row header = sheet.getRow(0);
if (header == null) { if (header == null) {
header = sheet.createRow(0); header = sheet.createRow(0);
header.setHeightInPoints(20 * styles.rowHeightFactor); header.setHeightInPoints(20 * styles.rowHeightFactor);
} }
createHeaderCell(header, RESULT_CHECK_COL_IK_NICKNAME_L_SUM, "IK昵称L列合计", styles.headerStyle);
createHeaderCell(header, RESULT_CHECK_COL_CURRENT_NICKNAME, "当前人昵称", styles.headerStyle);
createHeaderCell(header, RESULT_CHECK_COL_USER_ID, "用户ID", styles.headerStyle); createHeaderCell(header, RESULT_CHECK_COL_USER_ID, "用户ID", styles.headerStyle);
createHeaderCell(header, RESULT_CHECK_COL_NICKNAME, "昵称", styles.headerStyle); createHeaderCell(header, RESULT_CHECK_COL_NICKNAME, "昵称", styles.headerStyle);
createHeaderCell(header, RESULT_CHECK_COL_FINAL_SETTLEMENT, "最终收付款", styles.headerStyle); createHeaderCell(header, RESULT_CHECK_COL_FINAL_SETTLEMENT, "最终收付款", styles.headerStyle);
@@ -632,17 +868,25 @@ public final class LbDailyUserTradeReportExportSupport {
} }
private static void copyResultSheetContent(Sheet source, Sheet target) { private static void copyResultSheetContent(Sheet source, Sheet target) {
copyResultSheetContent(source, target, false);
}
private static void copyResultSheetContent(Sheet source, Sheet target, boolean excludeSumLabelRows) {
target.setDefaultRowHeight(source.getDefaultRowHeight()); target.setDefaultRowHeight(source.getDefaultRowHeight());
for (int col = 0; col <= RESULT_LAST_COL; col++) { for (int col = 0; col <= RESULT_LAST_COL; col++) {
target.setColumnWidth(col, source.getColumnWidth(col)); target.setColumnWidth(col, source.getColumnWidth(col));
} }
int destRowIdx = 0;
for (int rowIdx = 0; rowIdx <= source.getLastRowNum(); rowIdx++) { for (int rowIdx = 0; rowIdx <= source.getLastRowNum(); rowIdx++) {
Row srcRow = source.getRow(rowIdx); Row srcRow = source.getRow(rowIdx);
if (srcRow == null) { if (srcRow == null) {
continue; continue;
} }
Row destRow = target.createRow(rowIdx); if (excludeSumLabelRows && rowIdx >= 1 && isResultMatchSumLabelRow(srcRow)) {
continue;
}
Row destRow = target.createRow(destRowIdx++);
destRow.setHeight(srcRow.getHeight()); destRow.setHeight(srcRow.getHeight());
for (int col = 0; col <= RESULT_LAST_COL; col++) { for (int col = 0; col <= RESULT_LAST_COL; col++) {
Cell srcCell = srcRow.getCell(col); Cell srcCell = srcRow.getCell(col);