From b0494ff5b6befcec4905414b91cf7f8cd5201d8c Mon Sep 17 00:00:00 2001 From: cst61 Date: Thu, 28 May 2026 08:44:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=A5=E8=A1=A8=E6=A3=80=E6=9F=A5=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rj/service/impl/LbGoodsServiceImpl.java | 2 +- .../LbDailyUserTradeReportExportSupport.java | 582 +++++++++++++++++- src/main/resources/application.yml | 2 +- 3 files changed, 582 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/rj/service/impl/LbGoodsServiceImpl.java b/src/main/java/com/rj/service/impl/LbGoodsServiceImpl.java index c06af61..549c953 100644 --- a/src/main/java/com/rj/service/impl/LbGoodsServiceImpl.java +++ b/src/main/java/com/rj/service/impl/LbGoodsServiceImpl.java @@ -220,7 +220,7 @@ public class LbGoodsServiceImpl extends ServiceImpl impl w.le(LbGoods::getUpdatedAt, updatedEnd); } - w.orderByDesc(LbGoods::getUpdatedAt).orderByDesc(LbGoods::getId); + w.orderByDesc(LbGoods::getPrice).orderByDesc(LbGoods::getId); Page page = this.page(new Page<>(current, size), w); result.put("success", true); diff --git a/src/main/java/com/rj/service/support/LbDailyUserTradeReportExportSupport.java b/src/main/java/com/rj/service/support/LbDailyUserTradeReportExportSupport.java index 6215317..b0136e3 100644 --- a/src/main/java/com/rj/service/support/LbDailyUserTradeReportExportSupport.java +++ b/src/main/java/com/rj/service/support/LbDailyUserTradeReportExportSupport.java @@ -66,7 +66,21 @@ public final class LbDailyUserTradeReportExportSupport { private static final int RESULT_CHECK_COL_BUY_TOTAL_FEE = 20; private static final int RESULT_CHECK_COL_SELL_DETAIL = 21; private static final int RESULT_CHECK_COL_SELL_TOTAL = 22; - private static final int RESULT_CHECK_LAST_COL = RESULT_CHECK_COL_SELL_TOTAL; + /** 结果表检查表 Y 列:Z 列订单金额对应昵称;Z 列:S 列多笔订单金额逐行展开。 */ + private static final int RESULT_CHECK_COL_BUY_ORDER_NICKNAME = 24; + private static final int RESULT_CHECK_COL_BUY_ORDER_AMOUNT = 25; + /** 结果表检查表 AA 列:按 Y 列昵称汇总 Z 列订单金额。 */ + private static final int RESULT_CHECK_COL_BUY_ORDER_YZ_SUM = 26; + /** 结果表检查表 AC 列:AD 列卖出订单金额对应昵称;AD 列:V 列多笔卖出金额逐行展开。 */ + private static final int RESULT_CHECK_COL_SELL_ORDER_NICKNAME = 28; + private static final int RESULT_CHECK_COL_SELL_ORDER_AMOUNT = 29; + /** 结果表检查表 AE 列:按 AC 列昵称汇总 AD 列卖出订单金额。 */ + private static final int RESULT_CHECK_COL_SELL_ORDER_AD_SUM = 30; + /** 结果表检查表 AF~AH:总表与检查表买入差异核对。 */ + private static final int RESULT_CHECK_COL_BUY_DIFF_SUMMARY = 31; + private static final int RESULT_CHECK_COL_BUY_DIFF_CHECK = 32; + private static final int RESULT_CHECK_COL_BUY_DIFF = 33; + private static final int RESULT_CHECK_LAST_COL = RESULT_CHECK_COL_BUY_DIFF; private static final BigDecimal RESULT_CHECK_BUY_TOTAL_FEE_RATE = new BigDecimal("0.01"); private static final int PAYMENT_COL_NAME = 0; @@ -140,6 +154,7 @@ public final class LbDailyUserTradeReportExportSupport { fillResultCheckSheet( workbook.getSheetAt(RESULT_CHECK_SHEET_INDEX), resultSheet, + workbook.getSheetAt(SUMMARY_SHEET_INDEX), userIdByNickname, buyTotalByBuyerId, buyDetailByBuyerId, @@ -454,6 +469,7 @@ public final class LbDailyUserTradeReportExportSupport { private static void fillResultCheckSheet( Sheet target, Sheet resultSource, + Sheet summarySheet, Map userIdByNickname, Map buyTotalByBuyerId, Map buyDetailByBuyerId, @@ -540,11 +556,353 @@ public final class LbDailyUserTradeReportExportSupport { } fillResultCheckColumnsMandNByIandKNickname(target, styles); + fillResultCheckColumnZFromColumnS(target, styles); + fillResultCheckColumnAdFromColumnV(target, styles); + fillResultCheckColumnAaSumByYNickname(target, styles); + fillResultCheckColumnAeSumByAcNickname(target, styles); writeResultCheckColumnLAndMSum(target, styles); + writeResultCheckBuyAmountDiffRows(target, summarySheet, styles); autoSizeColumns(target, RESULT_CHECK_LAST_COL); + shrinkResultCheckDetailColumnWidths(target); } - /** 结果表检查表:末行追加 L、M 列数据合计。 */ + /** 结果表检查表 S、V 列(订单/卖出金额明细)宽度缩小为当前 20%。 */ + private static void shrinkResultCheckDetailColumnWidths(Sheet sheet) { + shrinkColumnWidthToRatio(sheet, RESULT_CHECK_COL_BUY_DETAIL, 0.2d); + shrinkColumnWidthToRatio(sheet, RESULT_CHECK_COL_SELL_DETAIL, 0.2d); + } + + private static void shrinkColumnWidthToRatio(Sheet sheet, int col, double ratio) { + int currentWidth = sheet.getColumnWidth(col); + int shrunk = Math.max(256, (int) Math.round(currentWidth * ratio)); + sheet.setColumnWidth(col, shrunk); + } + + /** + * S 列(订单金额明细)含多笔订单时,按「+」拆分后逐行写入 Z 列,Y 列写入对应昵称;仅 1 笔订单不写 Y/Z。 + */ + private static void fillResultCheckColumnZFromColumnS(Sheet sheet, SheetStyles styles) { + int dataEndRow = resolveResultCheckDataEndRow(sheet); + if (dataEndRow < 1) { + return; + } + + for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) { + Row row = sheet.getRow(rowIdx); + if (row == null || isResultMatchSumLabelRow(row)) { + continue; + } + String buyDetail = readPaymentCellText(row.getCell(RESULT_CHECK_COL_BUY_DETAIL)); + if (buyDetail.isEmpty()) { + continue; + } + List amounts = parseOrderAmountDetailText(buyDetail); + if (amounts.size() <= 1) { + continue; + } + String nickname = readPaymentCellText(row.getCell(RESULT_CHECK_COL_NICKNAME)); + if (nickname.isEmpty()) { + nickname = readPaymentCellText(row.getCell(RESULT_CHECK_COL_CURRENT_NICKNAME)); + } + + int writeRow = rowIdx; + for (BigDecimal amount : amounts) { + writeRow = findNextEmptyResultCheckColumnZ(sheet, writeRow); + Row targetRow = ensureResultCheckRow(sheet, writeRow, styles); + createPaymentActualCell( + targetRow, RESULT_CHECK_COL_BUY_ORDER_AMOUNT, amount, paymentActualStyle(writeRow, styles)); + if (!nickname.isEmpty() && isResultCheckColumnYEmpty(targetRow)) { + createTextCell(targetRow, RESULT_CHECK_COL_BUY_ORDER_NICKNAME, nickname, styles.textStyle); + } + writeRow++; + } + } + } + + /** + * V 列(卖出金额明细)含多笔订单时,按「+」拆分后逐行写入 AD 列,AC 列写入 Q 列昵称;仅 1 笔订单不写 AC/AD。 + */ + private static void fillResultCheckColumnAdFromColumnV(Sheet sheet, SheetStyles styles) { + int dataEndRow = resolveResultCheckDataEndRow(sheet); + if (dataEndRow < 1) { + return; + } + + for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) { + Row row = sheet.getRow(rowIdx); + if (row == null || isResultMatchSumLabelRow(row)) { + continue; + } + String sellDetail = readPaymentCellText(row.getCell(RESULT_CHECK_COL_SELL_DETAIL)); + if (sellDetail.isEmpty()) { + continue; + } + List amounts = parseOrderAmountDetailText(sellDetail); + if (amounts.size() <= 1) { + continue; + } + String nickname = readPaymentCellText(row.getCell(RESULT_CHECK_COL_NICKNAME)); + + int writeRow = rowIdx; + for (BigDecimal amount : amounts) { + writeRow = findNextEmptyResultCheckColumnAd(sheet, writeRow); + Row targetRow = ensureResultCheckRow(sheet, writeRow, styles); + createPaymentActualCell( + targetRow, RESULT_CHECK_COL_SELL_ORDER_AMOUNT, amount, paymentActualStyle(writeRow, styles)); + if (!nickname.isEmpty() && isResultCheckColumnAcEmpty(targetRow)) { + createTextCell(targetRow, RESULT_CHECK_COL_SELL_ORDER_NICKNAME, nickname, styles.textStyle); + } + writeRow++; + } + } + } + + /** AE 列:按 AC 列昵称汇总 AD 列卖出订单金额,连续相同昵称仅末行展示。 */ + private static void fillResultCheckColumnAeSumByAcNickname(Sheet sheet, SheetStyles styles) { + int dataEndRow = resolveResultCheckDataEndRow(sheet); + if (dataEndRow < 1) { + return; + } + + Map sumByAcNickname = new HashMap<>(); + for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) { + Row row = sheet.getRow(rowIdx); + if (row == null || isResultMatchSumLabelRow(row)) { + continue; + } + String acName = normalizeResultCheckNickname( + readPaymentCellText(row.getCell(RESULT_CHECK_COL_SELL_ORDER_NICKNAME))); + if (acName.isEmpty() || isResultCheckColumnAdEmpty(row)) { + continue; + } + BigDecimal adValue = readNumericCellValue(row.getCell(RESULT_CHECK_COL_SELL_ORDER_AMOUNT)); + sumByAcNickname.merge(acName, adValue, BigDecimal::add); + } + + Set displayRows = resolveConsecutiveLastRowsByColumnAc(sheet, dataEndRow); + + for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) { + Row row = sheet.getRow(rowIdx); + if (row == null || isResultMatchSumLabelRow(row)) { + continue; + } + String acName = normalizeResultCheckNickname( + readPaymentCellText(row.getCell(RESULT_CHECK_COL_SELL_ORDER_NICKNAME))); + if (displayRows.contains(rowIdx)) { + BigDecimal sum = sumByAcNickname.getOrDefault(acName, BigDecimal.ZERO); + createPaymentActualCell( + row, RESULT_CHECK_COL_SELL_ORDER_AD_SUM, sum, paymentActualStyle(rowIdx, styles)); + } else { + createTextCell(row, RESULT_CHECK_COL_SELL_ORDER_AD_SUM, "", styles.textStyle); + } + } + } + + /** AA 列:按 Y 列昵称汇总 Z 列订单金额,连续相同昵称仅末行展示。 */ + private static void fillResultCheckColumnAaSumByYNickname(Sheet sheet, SheetStyles styles) { + int dataEndRow = resolveResultCheckDataEndRow(sheet); + if (dataEndRow < 1) { + return; + } + + Map sumByYNickname = new HashMap<>(); + for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) { + Row row = sheet.getRow(rowIdx); + if (row == null || isResultMatchSumLabelRow(row)) { + continue; + } + String yName = normalizeResultCheckNickname( + readPaymentCellText(row.getCell(RESULT_CHECK_COL_BUY_ORDER_NICKNAME))); + if (yName.isEmpty() || isResultCheckColumnZEmpty(row)) { + continue; + } + BigDecimal zValue = readNumericCellValue(row.getCell(RESULT_CHECK_COL_BUY_ORDER_AMOUNT)); + sumByYNickname.merge(yName, zValue, BigDecimal::add); + } + + Set displayRows = resolveConsecutiveLastRowsByColumnY(sheet, dataEndRow); + + for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) { + Row row = sheet.getRow(rowIdx); + if (row == null || isResultMatchSumLabelRow(row)) { + continue; + } + String yName = normalizeResultCheckNickname( + readPaymentCellText(row.getCell(RESULT_CHECK_COL_BUY_ORDER_NICKNAME))); + if (displayRows.contains(rowIdx)) { + BigDecimal sum = sumByYNickname.getOrDefault(yName, BigDecimal.ZERO); + createPaymentActualCell( + row, RESULT_CHECK_COL_BUY_ORDER_YZ_SUM, sum, paymentActualStyle(rowIdx, styles)); + } else { + createTextCell(row, RESULT_CHECK_COL_BUY_ORDER_YZ_SUM, "", styles.textStyle); + } + } + } + + private static Set resolveConsecutiveLastRowsByColumnY(Sheet sheet, int dataEndRow) { + Set 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 yName = normalizeResultCheckNickname( + readPaymentCellText(row.getCell(RESULT_CHECK_COL_BUY_ORDER_NICKNAME))); + if (yName.isEmpty()) { + flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow); + groupNickname = null; + groupLastRow = -1; + continue; + } + if (!yName.equals(groupNickname)) { + flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow); + groupNickname = yName; + } + groupLastRow = rowIdx; + } + flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow); + return displayRows; + } + + private static Set resolveConsecutiveLastRowsByColumnAc(Sheet sheet, int dataEndRow) { + Set 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 acName = normalizeResultCheckNickname( + readPaymentCellText(row.getCell(RESULT_CHECK_COL_SELL_ORDER_NICKNAME))); + if (acName.isEmpty()) { + flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow); + groupNickname = null; + groupLastRow = -1; + continue; + } + if (!acName.equals(groupNickname)) { + flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow); + groupNickname = acName; + } + groupLastRow = rowIdx; + } + flushResultCheckNicknameGroup(displayRows, groupNickname, groupLastRow); + return displayRows; + } + + private static List parseOrderAmountDetailText(String detail) { + if (detail == null || detail.isBlank()) { + return List.of(); + } + List amounts = new ArrayList<>(); + for (String part : detail.split("\\+")) { + String trimmed = part.trim(); + if (trimmed.isEmpty()) { + continue; + } + try { + amounts.add(new BigDecimal(trimmed)); + } catch (NumberFormatException ignored) { + // skip invalid segment + } + } + return amounts; + } + + private static int findNextEmptyResultCheckColumnZ(Sheet sheet, int startRow) { + int rowIdx = Math.max(1, startRow); + while (true) { + Row row = sheet.getRow(rowIdx); + if (row == null || isResultCheckColumnZEmpty(row)) { + return rowIdx; + } + rowIdx++; + } + } + + private static int findNextEmptyResultCheckColumnAd(Sheet sheet, int startRow) { + int rowIdx = Math.max(1, startRow); + while (true) { + Row row = sheet.getRow(rowIdx); + if (row == null || isResultCheckColumnAdEmpty(row)) { + return rowIdx; + } + rowIdx++; + } + } + + private static boolean isResultCheckColumnYEmpty(Row row) { + if (row == null) { + return true; + } + Cell cell = row.getCell(RESULT_CHECK_COL_BUY_ORDER_NICKNAME); + 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; + } + + private static boolean isResultCheckColumnZEmpty(Row row) { + if (row == null) { + return true; + } + Cell cell = row.getCell(RESULT_CHECK_COL_BUY_ORDER_AMOUNT); + 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; + } + + private static boolean isResultCheckColumnAcEmpty(Row row) { + if (row == null) { + return true; + } + Cell cell = row.getCell(RESULT_CHECK_COL_SELL_ORDER_NICKNAME); + 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; + } + + private static boolean isResultCheckColumnAdEmpty(Row row) { + if (row == null) { + return true; + } + Cell cell = row.getCell(RESULT_CHECK_COL_SELL_ORDER_AMOUNT); + 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; + } + + /** 结果表检查表:末行追加 L、M、T、W、Z、AA 列数据合计。 */ private static void writeResultCheckColumnLAndMSum(Sheet sheet, SheetStyles styles) { int lastRow = sheet.getLastRowNum(); if (lastRow < 1) { @@ -562,6 +920,10 @@ public final class LbDailyUserTradeReportExportSupport { BigDecimal sumL = BigDecimal.ZERO; BigDecimal sumM = BigDecimal.ZERO; + BigDecimal sumT = BigDecimal.ZERO; + BigDecimal sumW = BigDecimal.ZERO; + BigDecimal sumZ = BigDecimal.ZERO; + BigDecimal sumAa = BigDecimal.ZERO; for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) { Row row = sheet.getRow(rowIdx); if (row == null || isResultMatchSumLabelRow(row)) { @@ -571,6 +933,18 @@ public final class LbDailyUserTradeReportExportSupport { if (!isResultCheckColumnMEmpty(row)) { sumM = sumM.add(readNumericCellValue(row.getCell(RESULT_CHECK_COL_IK_NICKNAME_L_SUM))); } + if (!isResultCheckColumnTEmpty(row)) { + sumT = sumT.add(readNumericCellValue(row.getCell(RESULT_CHECK_COL_BUY_TOTAL))); + } + if (!isResultCheckColumnWEmpty(row)) { + sumW = sumW.add(readNumericCellValue(row.getCell(RESULT_CHECK_COL_SELL_TOTAL))); + } + if (!isResultCheckColumnZEmpty(row)) { + sumZ = sumZ.add(readNumericCellValue(row.getCell(RESULT_CHECK_COL_BUY_ORDER_AMOUNT))); + } + if (!isResultCheckColumnAaEmpty(row)) { + sumAa = sumAa.add(readNumericCellValue(row.getCell(RESULT_CHECK_COL_BUY_ORDER_YZ_SUM))); + } } int sumRowIdx = dataEndRow + 1; @@ -582,6 +956,163 @@ public final class LbDailyUserTradeReportExportSupport { 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); + createDecimalCell(sumRow, RESULT_CHECK_COL_BUY_TOTAL, sumT, styles.summaryAmountStyle); + createDecimalCell(sumRow, RESULT_CHECK_COL_SELL_TOTAL, sumW, styles.summaryAmountStyle); + createPaymentActualCell(sumRow, RESULT_CHECK_COL_BUY_ORDER_AMOUNT, sumZ, styles.summaryRoundedAmountStyle); + createPaymentActualCell(sumRow, RESULT_CHECK_COL_BUY_ORDER_YZ_SUM, sumAa, styles.summaryRoundedAmountStyle); + } + + /** + * 总表 D 列(当日买入)与检查表 T 列(订单买入)按昵称核对;差异写在求和行之后。 + * 总表 A 列昵称 ↔ 检查表 Q 列昵称。 + */ + private static void writeResultCheckBuyAmountDiffRows( + Sheet checkSheet, Sheet summarySheet, SheetStyles styles) { + if (summarySheet == null) { + return; + } + int sumRowIdx = findResultCheckSumRowIndex(checkSheet); + if (sumRowIdx < 1) { + return; + } + + Map summaryBuyByNickname = readSummaryDailyBuyByNickname(summarySheet); + Map checkBuyByNickname = readCheckBuyByNickname(checkSheet, sumRowIdx - 1); + List diffItems = buildBuyAmountDiffItems(summaryBuyByNickname, checkBuyByNickname); + + BigDecimal summaryTotal = readSummarySheetDailyBuyTotal(summarySheet); + Row sumRow = checkSheet.getRow(sumRowIdx); + BigDecimal checkTotal = sumRow == null + ? BigDecimal.ZERO + : readNumericCellValue(sumRow.getCell(RESULT_CHECK_COL_BUY_TOTAL)); + + int writeRow = sumRowIdx + 1; + Row titleRow = ensureResultCheckRow(checkSheet, writeRow++, styles); + createTextCell(titleRow, RESULT_MATCH_NEG_NAME, "【买入核对差异】", styles.summaryTextStyle); + + if (summaryTotal.compareTo(checkTotal) != 0) { + Row totalDiffRow = ensureResultCheckRow(checkSheet, writeRow++, styles); + createTextCell(totalDiffRow, RESULT_MATCH_NEG_NAME, "合计差异", styles.summaryTextStyle); + createTextCell(totalDiffRow, RESULT_CHECK_COL_NICKNAME, "合计", styles.summaryTextStyle); + createDecimalCell(totalDiffRow, RESULT_CHECK_COL_BUY_DIFF_SUMMARY, summaryTotal, styles.summaryAmountStyle); + createDecimalCell(totalDiffRow, RESULT_CHECK_COL_BUY_DIFF_CHECK, checkTotal, styles.summaryAmountStyle); + createDecimalCell( + totalDiffRow, + RESULT_CHECK_COL_BUY_DIFF, + summaryTotal.subtract(checkTotal), + styles.summaryAmountStyle); + } + + if (diffItems.isEmpty()) { + Row okRow = ensureResultCheckRow(checkSheet, writeRow, styles); + createTextCell(okRow, RESULT_MATCH_NEG_NAME, "无昵称级差异", styles.textStyle); + return; + } + + for (BuyAmountDiffItem item : diffItems) { + Row row = ensureResultCheckRow(checkSheet, writeRow++, styles); + createTextCell(row, RESULT_MATCH_NEG_NAME, item.reason(), styles.textStyle); + createTextCell(row, RESULT_CHECK_COL_NICKNAME, item.nickname(), styles.textStyle); + if (item.summaryBuyAmt() != null) { + createDecimalCell(row, RESULT_CHECK_COL_BUY_DIFF_SUMMARY, item.summaryBuyAmt(), styles.amountStyle); + } + if (item.checkBuyAmt() != null) { + createDecimalCell(row, RESULT_CHECK_COL_BUY_DIFF_CHECK, item.checkBuyAmt(), styles.amountStyle); + } + createDecimalCell(row, RESULT_CHECK_COL_BUY_DIFF, item.diffAmt(), styles.amountStyle); + } + } + + private static int findResultCheckSumRowIndex(Sheet sheet) { + for (int rowIdx = sheet.getLastRowNum(); rowIdx >= 1; rowIdx--) { + Row row = sheet.getRow(rowIdx); + if (row != null && isResultMatchSumLabelRow(row)) { + return rowIdx; + } + } + return -1; + } + + private static Map readSummaryDailyBuyByNickname(Sheet summarySheet) { + Map buyByNickname = new LinkedHashMap<>(); + int lastRow = summarySheet.getLastRowNum(); + for (int rowIdx = 1; rowIdx <= lastRow; rowIdx++) { + Row row = summarySheet.getRow(rowIdx); + if (row == null) { + continue; + } + String nickname = normalizeResultCheckNickname(readPaymentCellText(row.getCell(COL_NICKNAME))); + if (nickname.isEmpty()) { + continue; + } + buyByNickname.put(nickname, readNumericCellValue(row.getCell(COL_DAILY_BUY))); + } + return buyByNickname; + } + + private static BigDecimal readSummarySheetDailyBuyTotal(Sheet summarySheet) { + int lastRow = summarySheet.getLastRowNum(); + if (lastRow < 1) { + return BigDecimal.ZERO; + } + Row lastDataRow = summarySheet.getRow(lastRow); + if (lastDataRow == null) { + return BigDecimal.ZERO; + } + return readNumericCellValue(lastDataRow.getCell(COL_DAILY_BUY)); + } + + private static Map readCheckBuyByNickname(Sheet checkSheet, int dataEndRow) { + Map buyByNickname = new LinkedHashMap<>(); + for (int rowIdx = 1; rowIdx <= dataEndRow; rowIdx++) { + Row row = checkSheet.getRow(rowIdx); + if (row == null || isResultMatchSumLabelRow(row)) { + continue; + } + String nickname = normalizeResultCheckNickname( + readPaymentCellText(row.getCell(RESULT_CHECK_COL_NICKNAME))); + if (nickname.isEmpty() || isResultCheckColumnTEmpty(row)) { + continue; + } + buyByNickname.put(nickname, readNumericCellValue(row.getCell(RESULT_CHECK_COL_BUY_TOTAL))); + } + return buyByNickname; + } + + private static List buildBuyAmountDiffItems( + Map summaryBuyByNickname, + Map checkBuyByNickname) { + LinkedHashSet allNicknames = new LinkedHashSet<>(); + allNicknames.addAll(summaryBuyByNickname.keySet()); + allNicknames.addAll(checkBuyByNickname.keySet()); + + List diffItems = new ArrayList<>(); + for (String nickname : allNicknames) { + boolean inSummary = summaryBuyByNickname.containsKey(nickname); + boolean inCheck = checkBuyByNickname.containsKey(nickname); + BigDecimal summaryAmt = summaryBuyByNickname.get(nickname); + BigDecimal checkAmt = checkBuyByNickname.get(nickname); + + if (inSummary && !inCheck) { + diffItems.add(new BuyAmountDiffItem( + nickname, summaryAmt, null, "仅总表有", summaryAmt)); + } else if (!inSummary && inCheck) { + diffItems.add(new BuyAmountDiffItem( + nickname, null, checkAmt, "仅检查表有", defaultZero(checkAmt).negate())); + } else if (inSummary && summaryAmt.compareTo(checkAmt) != 0) { + diffItems.add(new BuyAmountDiffItem( + nickname, summaryAmt, checkAmt, "金额不一致", summaryAmt.subtract(checkAmt))); + } + } + return diffItems; + } + + private record BuyAmountDiffItem( + String nickname, + BigDecimal summaryBuyAmt, + BigDecimal checkBuyAmt, + String reason, + BigDecimal diffAmt) { } /** @@ -721,6 +1252,44 @@ public final class LbDailyUserTradeReportExportSupport { return false; } + private static boolean isResultCheckColumnTEmpty(Row row) { + return isResultCheckNumericColumnEmpty(row, RESULT_CHECK_COL_BUY_TOTAL); + } + + private static boolean isResultCheckColumnWEmpty(Row row) { + return isResultCheckNumericColumnEmpty(row, RESULT_CHECK_COL_SELL_TOTAL); + } + + private static boolean isResultCheckNumericColumnEmpty(Row row, int col) { + if (row == null) { + return true; + } + Cell cell = row.getCell(col); + 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; + } + + private static boolean isResultCheckColumnAaEmpty(Row row) { + if (row == null) { + return true; + } + Cell cell = row.getCell(RESULT_CHECK_COL_BUY_ORDER_YZ_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(); @@ -865,6 +1434,15 @@ public final class LbDailyUserTradeReportExportSupport { createHeaderCell(header, RESULT_CHECK_COL_BUY_TOTAL_FEE, "1%金额", styles.headerStyle); createHeaderCell(header, RESULT_CHECK_COL_SELL_DETAIL, "卖出金额明细", styles.headerStyle); createHeaderCell(header, RESULT_CHECK_COL_SELL_TOTAL, "当日卖出金额", styles.headerStyle); + createHeaderCell(header, RESULT_CHECK_COL_BUY_ORDER_NICKNAME, "订单昵称", styles.headerStyle); + createHeaderCell(header, RESULT_CHECK_COL_BUY_ORDER_AMOUNT, "订单金额", styles.headerStyle); + createHeaderCell(header, RESULT_CHECK_COL_BUY_ORDER_YZ_SUM, "Y昵称Z列合计", styles.headerStyle); + createHeaderCell(header, RESULT_CHECK_COL_SELL_ORDER_NICKNAME, "卖出订单昵称", styles.headerStyle); + createHeaderCell(header, RESULT_CHECK_COL_SELL_ORDER_AMOUNT, "卖出订单金额", styles.headerStyle); + createHeaderCell(header, RESULT_CHECK_COL_SELL_ORDER_AD_SUM, "AC昵称AD列合计", styles.headerStyle); + createHeaderCell(header, RESULT_CHECK_COL_BUY_DIFF_SUMMARY, "总表买入", styles.headerStyle); + createHeaderCell(header, RESULT_CHECK_COL_BUY_DIFF_CHECK, "检查表买入", styles.headerStyle); + createHeaderCell(header, RESULT_CHECK_COL_BUY_DIFF, "买入差异", styles.headerStyle); } private static void copyResultSheetContent(Sheet source, Sheet target) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 630017f..d228806 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -23,7 +23,7 @@ hxr: cookie: PHPSID=f3710baf6381da418aa832660ccb9d08 phpsid: "f3710baf6381da418aa832660ccb9d08" # 货品同步 /api/order/goods(LbGoodsController#sync-from-hxr) - goods-api-token: "04e2a8aa-d178-4da4-b97f-057849607824" + goods-api-token: "e176f641-4425-4930-bab3-3cfdc2a8fd2a" goods-api-app-str: "ssniQQ3UP2Vr8mXwaugssgaOLzQo0cX5" goods-api-origin: "https://hxrdweb.hxrdsm.cn" goods-api-referer: "https://hxrdweb.hxrdsm.cn/"