From 68d78d4b82379e4945de0dae667b996677f338bb Mon Sep 17 00:00:00 2001
From: zjf <1799650030@qq.com>
Date: Thu, 10 Sep 2026 11:10:42 +0800
Subject: [PATCH] =?UTF-8?q?=E6=95=8F=E5=AE=9E=E9=80=82=E9=85=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../dto/report/CanteenReportBoxExcel.java | 94 +++----
.../dto/report/CanteenReportFaceExcel.java | 90 +++----
.../entity/CanteenReportPersonEntity.java | 3 +
.../report/CanteenReportBoxExcelBinder.java | 136 ++++++++++
.../report/CanteenReportConstants.java | 6 +
.../report/CanteenReportFaceExcelBinder.java | 238 ++++++++++++++++++
.../CanteenReportBoxImportController.java | 8 +
.../controller/CanteenReportController.java | 10 +
.../CanteenReportFaceImportController.java | 12 +-
.../CanteenReportFoodEnergyController.java | 6 +-
.../CanteenReportPersonController.java | 21 +-
.../CanteenReportSettleParkController.java | 5 +-
.../mapper/CanteenReportFaceDetailMapper.java | 8 +-
.../mapper/CanteenReportPersonMapper.java | 2 +
.../mapper/CanteenReportSwipeMapper.java | 21 +-
.../CanteenReportBoxImportService.java | 3 +
.../service/CanteenReportCalcService.java | 3 +
.../CanteenReportFaceImportService.java | 6 +-
.../service/CanteenReportPersonService.java | 18 +-
.../CanteenReportSettleParkService.java | 3 +
.../CanteenReportBoxImportServiceImpl.java | 225 ++++++++++++++++-
.../impl/CanteenReportCalcServiceImpl.java | 178 ++++++++++---
.../CanteenReportFaceImportServiceImpl.java | 206 +++++++++++++--
.../CanteenReportFoodEnergyServiceImpl.java | 1 +
.../CanteenReportManualAdjustServiceImpl.java | 1 +
.../impl/CanteenReportPersonServiceImpl.java | 67 +++--
.../impl/CanteenReportRosterServiceImpl.java | 1 +
.../CanteenReportSettleParkServiceImpl.java | 25 +-
.../impl/CanteenReportSwipeServiceImpl.java | 1 +
.../mapper/CanteenReportFaceDetailMapper.xml | 11 +-
.../mapper/CanteenReportPersonMapper.xml | 8 +-
.../mapper/CanteenReportSwipeMapper.xml | 45 +++-
32 files changed, 1226 insertions(+), 236 deletions(-)
create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportBoxExcelBinder.java
create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportFaceExcelBinder.java
diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportBoxExcel.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportBoxExcel.java
index a14a58914..070e319dc 100644
--- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportBoxExcel.java
+++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportBoxExcel.java
@@ -1,53 +1,41 @@
-package com.bsd.pjcloud.canteen.dto.report;
-
-import com.alibaba.excel.annotation.ExcelProperty;
-import com.bsd.pjcloud.canteen.excel.ExcelYuanConverter;
-import lombok.Data;
-
-import java.math.BigDecimal;
-
-/**
- * 包厢台账(汇总)Excel,列序对齐样表。
- */
-@Data
-public class CanteenReportBoxExcel {
-
- @ExcelProperty(index = 0)
- private String monthText;
-
- @ExcelProperty(index = 1)
- private String bizDate;
-
- @ExcelProperty(index = 5)
- private String costCenter;
-
- @ExcelProperty(index = 6)
- private String costCenterCode;
-
- @ExcelProperty(index = 8)
- private String empNo;
-
- @ExcelProperty(index = 9)
- private String empName;
-
- @ExcelProperty(index = 10, converter = ExcelYuanConverter.class)
- private BigDecimal foodAmount;
-
- @ExcelProperty(index = 11, converter = ExcelYuanConverter.class)
- private BigDecimal drinkAmount;
-
- @ExcelProperty(index = 12, converter = ExcelYuanConverter.class)
- private BigDecimal totalAmount;
-
- @ExcelProperty(index = 14)
- private String legalEntity;
-
- @ExcelProperty(index = 15)
- private String remark1;
-
- @ExcelProperty(index = 16)
- private String remark2;
-
- @ExcelProperty(index = 17)
- private String park;
-}
+package com.bsd.pjcloud.canteen.dto.report;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 包厢台账(汇总)行数据。
+ *
+ * 导入已改为 {@code CanteenReportBoxExcelBinder} 按规范化表头名称映射,
+ * 不再依赖 EasyExcel {@code @ExcelProperty}(多值 value 会被当成多级表头而非别名)。
+ */
+@Data
+public class CanteenReportBoxExcel {
+
+ private String monthText;
+
+ private String bizDate;
+
+ private String costCenter;
+
+ private String costCenterCode;
+
+ private String empNo;
+
+ private String empName;
+
+ private BigDecimal foodAmount;
+
+ private BigDecimal drinkAmount;
+
+ private BigDecimal totalAmount;
+
+ private String legalEntity;
+
+ private String remark1;
+
+ private String remark2;
+
+ private String park;
+}
diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportFaceExcel.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportFaceExcel.java
index 4c8a2e06e..dd3b5ed4b 100644
--- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportFaceExcel.java
+++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportFaceExcel.java
@@ -4,138 +4,140 @@ import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
- * 人脸消费系统导出 Excel,列序与样表一致(共 43 列)。
+ * 人脸消费系统导出 Excel。
+ *
+ * 按表头名称匹配(不按固定列序号),避免不同月份导出增删/调列后字段错位。
* 明细入库原样落库;汇总到刷卡明细时再做字段映射。
*/
@Data
public class CanteenReportFaceExcel {
- @ExcelProperty(index = 0)
+ @ExcelProperty({ "订单号" })
private String orderNo;
- @ExcelProperty(index = 1)
+ @ExcelProperty({ "第三方订单号" })
private String thirdOrderNo;
- @ExcelProperty(index = 2)
+ @ExcelProperty({ "创建时间" })
private String createTime;
- @ExcelProperty(index = 3)
+ @ExcelProperty({ "支付时间" })
private String payTime;
- @ExcelProperty(index = 4)
+ @ExcelProperty({ "扣款时间" })
private String deductTime;
- @ExcelProperty(index = 5)
+ @ExcelProperty({ "一级组织" })
private String orgLevel1;
- @ExcelProperty(index = 6)
+ @ExcelProperty({ "二级组织" })
private String orgLevel2;
- @ExcelProperty(index = 7)
+ @ExcelProperty({ "三级组织" })
private String orgLevel3;
- @ExcelProperty(index = 8)
+ @ExcelProperty({ "食堂" })
private String canteen;
- @ExcelProperty(index = 9)
+ @ExcelProperty({ "食堂区域" })
private String canteenArea;
- @ExcelProperty(index = 10)
+ @ExcelProperty({ "消费点" })
private String consumePoint;
- @ExcelProperty(index = 11)
+ @ExcelProperty({ "姓名" })
private String empName;
- @ExcelProperty(index = 12)
+ @ExcelProperty({ "手机号", "手机", "电话" })
private String phone;
- @ExcelProperty(index = 13)
+ @ExcelProperty({ "法律归属" })
private String legalEntity;
- @ExcelProperty(index = 14)
+ @ExcelProperty({ "人员编号", "工号", "员工编号" })
private String empNo;
- @ExcelProperty(index = 15)
+ @ExcelProperty({ "卡号" })
private String cardNo;
- @ExcelProperty(index = 16)
+ @ExcelProperty({ "分组" })
private String groupName;
- @ExcelProperty(index = 17)
+ @ExcelProperty({ "部门" })
private String deptName;
- @ExcelProperty(index = 18)
+ @ExcelProperty({ "支付方式" })
private String payMethod;
- @ExcelProperty(index = 19)
+ @ExcelProperty({ "支付类型" })
private String payType;
- @ExcelProperty(index = 20)
+ @ExcelProperty({ "订单金额" })
private String orderAmount;
- @ExcelProperty(index = 21)
+ @ExcelProperty({ "动账金额" })
private String changeAmount;
- @ExcelProperty(index = 22)
+ @ExcelProperty({ "储值动账" })
private String storedChange;
- @ExcelProperty(index = 23)
+ @ExcelProperty({ "补贴动账" })
private String subsidyChange;
- @ExcelProperty(index = 24)
+ @ExcelProperty({ "赠送动账" })
private String giftChange;
- @ExcelProperty(index = 25)
+ @ExcelProperty({ "储值钱包余额" })
private String storedBalance;
- @ExcelProperty(index = 26)
+ @ExcelProperty({ "补贴钱包余额" })
private String subsidyBalance;
- @ExcelProperty(index = 27)
+ @ExcelProperty({ "赠送钱包余额" })
private String giftBalance;
- @ExcelProperty(index = 28)
+ @ExcelProperty({ "动账组织" })
private String changeOrg;
- @ExcelProperty(index = 29)
+ @ExcelProperty({ "优惠" })
private String discount;
- @ExcelProperty(index = 30)
+ @ExcelProperty({ "优惠金额" })
private String discountAmount;
- @ExcelProperty(index = 31)
+ @ExcelProperty({ "优惠类型" })
private String discountType;
- @ExcelProperty(index = 32)
+ @ExcelProperty({ "券类型" })
private String couponType;
- @ExcelProperty(index = 33)
+ @ExcelProperty({ "抵扣金额" })
private String deductAmount;
- @ExcelProperty(index = 34)
+ @ExcelProperty({ "手续费" })
private String feeAmount;
- @ExcelProperty(index = 35)
+ @ExcelProperty({ "餐补" })
private String mealSubsidy;
- @ExcelProperty(index = 36)
+ @ExcelProperty({ "消费类型" })
private String consumeType;
- @ExcelProperty(index = 37)
+ @ExcelProperty({ "餐段" })
private String mealSegment;
- @ExcelProperty(index = 38)
+ @ExcelProperty({ "交易设备", "结算台", "设备名称" })
private String deviceName;
- @ExcelProperty(index = 39)
+ @ExcelProperty({ "设备状态" })
private String deviceStatus;
- @ExcelProperty(index = 40)
+ @ExcelProperty({ "备注" })
private String remark;
- @ExcelProperty(index = 41)
+ @ExcelProperty({ "操作员" })
private String operatorName;
- @ExcelProperty(index = 42)
+ @ExcelProperty({ "访客餐支付方式" })
private String visitorPayMethod;
}
diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportPersonEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportPersonEntity.java
index 3f39de2cd..36c2c330f 100644
--- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportPersonEntity.java
+++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportPersonEntity.java
@@ -17,6 +17,9 @@ public class CanteenReportPersonEntity extends Model
@TableId(type = IdType.ASSIGN_ID)
private Long id;
+ @Schema(description = "适用月份 yyyy-MM")
+ private String bizMonth;
+
@Schema(description = "工号")
private String empNo;
diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportBoxExcelBinder.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportBoxExcelBinder.java
new file mode 100644
index 000000000..9228a7b79
--- /dev/null
+++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportBoxExcelBinder.java
@@ -0,0 +1,136 @@
+package com.bsd.pjcloud.canteen.report;
+
+import cn.hutool.core.util.StrUtil;
+import com.bsd.pjcloud.canteen.dto.report.CanteenReportBoxExcel;
+import com.bsd.pjcloud.canteen.excel.ExcelYuanConverter;
+
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+
+/**
+ * 包厢台账 Excel:按规范化表头名称映射(不依赖列序号 / EasyExcel 多值注解)。
+ *
+ * 注意:EasyExcel 的 {@code @ExcelProperty({"A","B"})} 表示多级表头,不是别名;
+ * 样表常见表头含括号/空格,必须自行规范化匹配。
+ */
+public final class CanteenReportBoxExcelBinder {
+
+ private static final Map ALIAS_TO_FIELD;
+
+ private static final Map> FIELD_SETTERS;
+
+ static {
+ Map alias = new HashMap<>();
+ putAliases(alias, "monthText", "月份", "业务月");
+ putAliases(alias, "bizDate", "日期", "业务日", "消费日期");
+ putAliases(alias, "costCenter", "成本中心名称(核对)", "成本中心名称", "成本中心", "核对成本", "核对成本(开票公司)");
+ putAliases(alias, "costCenterCode", "成本中心代码", "成本中心编码");
+ putAliases(alias, "empNo", "签单人工号", "工号", "人员编号", "员工编号");
+ putAliases(alias, "empName", "签单人", "姓名", "员工姓名");
+ putAliases(alias, "foodAmount", "食品", "食品金额");
+ putAliases(alias, "drinkAmount", "酒水", "酒水金额");
+ putAliases(alias, "totalAmount", "总计", "总金额", "金额", "消费金额");
+ putAliases(alias, "legalEntity", "费用承担公司(核对)", "费用承担公司", "法律归属", "法人公司");
+ putAliases(alias, "remark1", "备注1", "备注");
+ putAliases(alias, "remark2", "备注2");
+ putAliases(alias, "park", "园区", "厂区");
+ ALIAS_TO_FIELD = Collections.unmodifiableMap(alias);
+
+ Map> setters = new HashMap<>();
+ setters.put("monthText", CanteenReportBoxExcel::setMonthText);
+ setters.put("bizDate", CanteenReportBoxExcel::setBizDate);
+ setters.put("costCenter", CanteenReportBoxExcel::setCostCenter);
+ setters.put("costCenterCode", CanteenReportBoxExcel::setCostCenterCode);
+ setters.put("empNo", CanteenReportBoxExcel::setEmpNo);
+ setters.put("empName", CanteenReportBoxExcel::setEmpName);
+ setters.put("foodAmount", (row, v) -> row.setFoodAmount(parseAmount(v)));
+ setters.put("drinkAmount", (row, v) -> row.setDrinkAmount(parseAmount(v)));
+ setters.put("totalAmount", (row, v) -> row.setTotalAmount(parseAmount(v)));
+ setters.put("legalEntity", CanteenReportBoxExcel::setLegalEntity);
+ setters.put("remark1", CanteenReportBoxExcel::setRemark1);
+ setters.put("remark2", CanteenReportBoxExcel::setRemark2);
+ setters.put("park", CanteenReportBoxExcel::setPark);
+ FIELD_SETTERS = Collections.unmodifiableMap(setters);
+ }
+
+ private CanteenReportBoxExcelBinder() {
+ }
+
+ private static void putAliases(Map alias, String field, String... names) {
+ for (String n : names) {
+ alias.put(normalizeHead(n), field);
+ }
+ }
+
+ /** 去空白/全角空格等,便于表头匹配 */
+ public static String normalizeHead(String head) {
+ if (head == null) {
+ return "";
+ }
+ String s = head.replace('\u00A0', ' ').replace(' ', ' ').trim();
+ s = s.replaceAll("\\s+", "");
+ return s;
+ }
+
+ public static Map buildColumnFieldMap(Map rawHeadByIndex,
+ Set outMatchedFields) {
+ Map colField = new LinkedHashMap<>();
+ if (rawHeadByIndex == null) {
+ return colField;
+ }
+ for (Map.Entry e : rawHeadByIndex.entrySet()) {
+ String field = ALIAS_TO_FIELD.get(normalizeHead(e.getValue()));
+ if (field == null) {
+ continue;
+ }
+ colField.put(e.getKey(), field);
+ if (outMatchedFields != null) {
+ outMatchedFields.add(field);
+ }
+ }
+ return colField;
+ }
+
+ public static boolean hasField(Set matchedFields, String... fields) {
+ if (matchedFields == null || matchedFields.isEmpty()) {
+ return false;
+ }
+ for (String f : fields) {
+ if (matchedFields.contains(f)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static CanteenReportBoxExcel bind(Map colFieldMap, Map row) {
+ CanteenReportBoxExcel excel = new CanteenReportBoxExcel();
+ if (colFieldMap == null || row == null) {
+ return excel;
+ }
+ for (Map.Entry e : colFieldMap.entrySet()) {
+ BiConsumer setter = FIELD_SETTERS.get(e.getValue());
+ if (setter == null) {
+ continue;
+ }
+ boolean preferInteger = "empNo".equals(e.getValue()) || "costCenterCode".equals(e.getValue());
+ String val = CanteenReportFaceExcelBinder.cellToPlainString(row.get(e.getKey()), preferInteger);
+ if (val != null) {
+ setter.accept(excel, val);
+ }
+ }
+ return excel;
+ }
+
+ private static BigDecimal parseAmount(String raw) {
+ if (StrUtil.isBlank(raw)) {
+ return null;
+ }
+ return ExcelYuanConverter.parseYuan(raw);
+ }
+}
diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportConstants.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportConstants.java
index 2251105bc..6ceca1b10 100644
--- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportConstants.java
+++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportConstants.java
@@ -36,6 +36,12 @@ public final class CanteenReportConstants {
/** 刷卡备注:人员信息未命中,核对成本已回退法律归属 */
public static final String REMARK_PERSON_MISS = "未命中人员信息";
+ /** 人脸汇总匹配方式:按当月人员信息(工号→核对成本) */
+ public static final String MATCH_MODE_PERSON = "PERSON";
+
+ /** 人脸汇总匹配方式:按当月花名册(工号→人事范围/核对成本) */
+ public static final String MATCH_MODE_ROSTER = "ROSTER";
+
public static final List INCOME_TYPES = Collections.unmodifiableList(Arrays.asList(
INCOME_SWIPE, INCOME_ALIPAY, INCOME_BOX, INCOME_OTHER, INCOME_PROFIT_ADJ, INCOME_INVOICE, INCOME_BAD_DEBT
));
diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportFaceExcelBinder.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportFaceExcelBinder.java
new file mode 100644
index 000000000..535fc2bbb
--- /dev/null
+++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportFaceExcelBinder.java
@@ -0,0 +1,238 @@
+package com.bsd.pjcloud.canteen.report;
+
+import cn.hutool.core.util.StrUtil;
+import com.bsd.pjcloud.canteen.dto.report.CanteenReportFaceExcel;
+
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+/**
+ * 人脸 Excel:按规范化表头名称映射到字段(不依赖列序号 / EasyExcel 注解精确匹配)。
+ *
+ * 处理空格、全角空格、常见别名;数字工号避免科学计数法。
+ */
+public final class CanteenReportFaceExcelBinder {
+
+ /** 规范化表头 → 字段名 */
+ private static final Map ALIAS_TO_FIELD;
+
+ private static final Map> FIELD_SETTERS;
+
+ static {
+ Map alias = new HashMap<>();
+ putAliases(alias, "orderNo", "订单号");
+ putAliases(alias, "thirdOrderNo", "第三方订单号");
+ putAliases(alias, "createTime", "创建时间");
+ putAliases(alias, "payTime", "支付时间");
+ putAliases(alias, "deductTime", "扣款时间");
+ putAliases(alias, "orgLevel1", "一级组织");
+ putAliases(alias, "orgLevel2", "二级组织");
+ putAliases(alias, "orgLevel3", "三级组织");
+ putAliases(alias, "canteen", "食堂");
+ putAliases(alias, "canteenArea", "食堂区域");
+ putAliases(alias, "consumePoint", "消费点");
+ putAliases(alias, "empName", "姓名");
+ putAliases(alias, "phone", "手机号", "手机", "电话", "联系电话");
+ putAliases(alias, "legalEntity", "法律归属");
+ putAliases(alias, "empNo", "人员编号", "工号", "员工编号", "人员工号", "员工号");
+ putAliases(alias, "cardNo", "卡号");
+ putAliases(alias, "groupName", "分组", "员工分组");
+ putAliases(alias, "deptName", "部门");
+ putAliases(alias, "payMethod", "支付方式");
+ putAliases(alias, "payType", "支付类型");
+ putAliases(alias, "orderAmount", "订单金额");
+ putAliases(alias, "changeAmount", "动账金额");
+ putAliases(alias, "storedChange", "储值动账");
+ putAliases(alias, "subsidyChange", "补贴动账");
+ putAliases(alias, "giftChange", "赠送动账");
+ putAliases(alias, "storedBalance", "储值钱包余额");
+ putAliases(alias, "subsidyBalance", "补贴钱包余额");
+ putAliases(alias, "giftBalance", "赠送钱包余额");
+ putAliases(alias, "changeOrg", "动账组织");
+ putAliases(alias, "discount", "优惠");
+ putAliases(alias, "discountAmount", "优惠金额");
+ putAliases(alias, "discountType", "优惠类型");
+ putAliases(alias, "couponType", "券类型");
+ putAliases(alias, "deductAmount", "抵扣金额");
+ putAliases(alias, "feeAmount", "手续费");
+ putAliases(alias, "mealSubsidy", "餐补");
+ putAliases(alias, "consumeType", "消费类型");
+ putAliases(alias, "mealSegment", "餐段");
+ putAliases(alias, "deviceName", "交易设备", "结算台", "设备名称", "设备");
+ putAliases(alias, "deviceStatus", "设备状态");
+ putAliases(alias, "remark", "备注");
+ putAliases(alias, "operatorName", "操作员");
+ putAliases(alias, "visitorPayMethod", "访客餐支付方式");
+ ALIAS_TO_FIELD = Collections.unmodifiableMap(alias);
+
+ Map> setters = new HashMap<>();
+ setters.put("orderNo", CanteenReportFaceExcel::setOrderNo);
+ setters.put("thirdOrderNo", CanteenReportFaceExcel::setThirdOrderNo);
+ setters.put("createTime", CanteenReportFaceExcel::setCreateTime);
+ setters.put("payTime", CanteenReportFaceExcel::setPayTime);
+ setters.put("deductTime", CanteenReportFaceExcel::setDeductTime);
+ setters.put("orgLevel1", CanteenReportFaceExcel::setOrgLevel1);
+ setters.put("orgLevel2", CanteenReportFaceExcel::setOrgLevel2);
+ setters.put("orgLevel3", CanteenReportFaceExcel::setOrgLevel3);
+ setters.put("canteen", CanteenReportFaceExcel::setCanteen);
+ setters.put("canteenArea", CanteenReportFaceExcel::setCanteenArea);
+ setters.put("consumePoint", CanteenReportFaceExcel::setConsumePoint);
+ setters.put("empName", CanteenReportFaceExcel::setEmpName);
+ setters.put("phone", CanteenReportFaceExcel::setPhone);
+ setters.put("legalEntity", CanteenReportFaceExcel::setLegalEntity);
+ setters.put("empNo", CanteenReportFaceExcel::setEmpNo);
+ setters.put("cardNo", CanteenReportFaceExcel::setCardNo);
+ setters.put("groupName", CanteenReportFaceExcel::setGroupName);
+ setters.put("deptName", CanteenReportFaceExcel::setDeptName);
+ setters.put("payMethod", CanteenReportFaceExcel::setPayMethod);
+ setters.put("payType", CanteenReportFaceExcel::setPayType);
+ setters.put("orderAmount", CanteenReportFaceExcel::setOrderAmount);
+ setters.put("changeAmount", CanteenReportFaceExcel::setChangeAmount);
+ setters.put("storedChange", CanteenReportFaceExcel::setStoredChange);
+ setters.put("subsidyChange", CanteenReportFaceExcel::setSubsidyChange);
+ setters.put("giftChange", CanteenReportFaceExcel::setGiftChange);
+ setters.put("storedBalance", CanteenReportFaceExcel::setStoredBalance);
+ setters.put("subsidyBalance", CanteenReportFaceExcel::setSubsidyBalance);
+ setters.put("giftBalance", CanteenReportFaceExcel::setGiftBalance);
+ setters.put("changeOrg", CanteenReportFaceExcel::setChangeOrg);
+ setters.put("discount", CanteenReportFaceExcel::setDiscount);
+ setters.put("discountAmount", CanteenReportFaceExcel::setDiscountAmount);
+ setters.put("discountType", CanteenReportFaceExcel::setDiscountType);
+ setters.put("couponType", CanteenReportFaceExcel::setCouponType);
+ setters.put("deductAmount", CanteenReportFaceExcel::setDeductAmount);
+ setters.put("feeAmount", CanteenReportFaceExcel::setFeeAmount);
+ setters.put("mealSubsidy", CanteenReportFaceExcel::setMealSubsidy);
+ setters.put("consumeType", CanteenReportFaceExcel::setConsumeType);
+ setters.put("mealSegment", CanteenReportFaceExcel::setMealSegment);
+ setters.put("deviceName", CanteenReportFaceExcel::setDeviceName);
+ setters.put("deviceStatus", CanteenReportFaceExcel::setDeviceStatus);
+ setters.put("remark", CanteenReportFaceExcel::setRemark);
+ setters.put("operatorName", CanteenReportFaceExcel::setOperatorName);
+ setters.put("visitorPayMethod", CanteenReportFaceExcel::setVisitorPayMethod);
+ FIELD_SETTERS = Collections.unmodifiableMap(setters);
+ }
+
+ private CanteenReportFaceExcelBinder() {
+ }
+
+ private static void putAliases(Map alias, String field, String... names) {
+ for (String n : names) {
+ alias.put(normalizeHead(n), field);
+ }
+ }
+
+ /** 去空白/全角空格等,便于表头匹配 */
+ public static String normalizeHead(String head) {
+ if (head == null) {
+ return "";
+ }
+ String s = head.replace('\u00A0', ' ').replace(' ', ' ').trim();
+ // 去掉所有空白,兼容「人员 编号」「人员编号\n」
+ s = s.replaceAll("\\s+", "");
+ return s;
+ }
+
+ /**
+ * 根据原始表头行建立:列下标 → 字段名
+ *
+ * @return 列下标到字段;同时 outMatchedFields 填入已命中字段名
+ */
+ public static Map buildColumnFieldMap(Map rawHeadByIndex,
+ java.util.Set outMatchedFields) {
+ Map colField = new LinkedHashMap<>();
+ if (rawHeadByIndex == null) {
+ return colField;
+ }
+ for (Map.Entry e : rawHeadByIndex.entrySet()) {
+ String field = ALIAS_TO_FIELD.get(normalizeHead(e.getValue()));
+ if (field == null) {
+ continue;
+ }
+ colField.put(e.getKey(), field);
+ if (outMatchedFields != null) {
+ outMatchedFields.add(field);
+ }
+ }
+ return colField;
+ }
+
+ public static boolean hasField(java.util.Set matchedFields, String... fields) {
+ if (matchedFields == null || matchedFields.isEmpty()) {
+ return false;
+ }
+ for (String f : fields) {
+ if (matchedFields.contains(f)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static CanteenReportFaceExcel bind(Map colFieldMap, Map row) {
+ CanteenReportFaceExcel excel = new CanteenReportFaceExcel();
+ if (colFieldMap == null || row == null) {
+ return excel;
+ }
+ for (Map.Entry e : colFieldMap.entrySet()) {
+ BiConsumer setter = FIELD_SETTERS.get(e.getValue());
+ if (setter == null) {
+ continue;
+ }
+ String val = cellToPlainString(row.get(e.getKey()), "empNo".equals(e.getValue()) || "phone".equals(e.getValue())
+ || "cardNo".equals(e.getValue()) || "orderNo".equals(e.getValue()));
+ if (val != null) {
+ setter.accept(excel, val);
+ }
+ }
+ return excel;
+ }
+
+ /**
+ * 单元格转字符串;对工号/手机号等尽量保留整数形式,避免 1.0093664E7。
+ */
+ public static String cellToPlainString(Object raw, boolean preferInteger) {
+ if (raw == null) {
+ return null;
+ }
+ if (raw instanceof Number) {
+ return numberToPlain((Number) raw, preferInteger);
+ }
+ String s = String.valueOf(raw).trim();
+ if (s.isEmpty() || "null".equalsIgnoreCase(s)) {
+ return null;
+ }
+ if (preferInteger && s.matches("[-+]?\\d+(\\.\\d+)?([Ee][-+]?\\d+)?")) {
+ try {
+ return numberToPlain(new BigDecimal(s), true);
+ }
+ catch (Exception ignored) {
+ return s;
+ }
+ }
+ return s;
+ }
+
+ private static String numberToPlain(Number n, boolean preferInteger) {
+ BigDecimal bd = n instanceof BigDecimal ? (BigDecimal) n : new BigDecimal(n.toString());
+ if (preferInteger) {
+ try {
+ bd = bd.stripTrailingZeros();
+ if (bd.scale() <= 0) {
+ return bd.toBigInteger().toString();
+ }
+ }
+ catch (Exception ignored) {
+ // fall through
+ }
+ }
+ return bd.stripTrailingZeros().toPlainString();
+ }
+
+ public static String blankToNull(String s) {
+ return StrUtil.isBlank(s) ? null : s.trim();
+ }
+}
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportBoxImportController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportBoxImportController.java
index 730aa2122..dce50594f 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportBoxImportController.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportBoxImportController.java
@@ -28,4 +28,12 @@ public class CanteenReportBoxImportController {
@RequestParam(defaultValue = "true") boolean replaceMonth) {
return boxImportService.importExcel(file, bizMonth, replaceMonth);
}
+
+ @SysLog("清空包厢刷卡")
+ @Operation(summary = "按月清空包厢刷卡(source=BOX);可传 park 只清某厂区")
+ @DeleteMapping("/clear")
+ public R clear(@RequestParam String bizMonth,
+ @RequestParam(required = false) String park) {
+ return boxImportService.clearByMonthPark(bizMonth, park);
+ }
}
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportController.java
index e8a96d2cf..d69df8142 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportController.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportController.java
@@ -80,6 +80,16 @@ public class CanteenReportController {
return R.ok(calcService.businessReport(bizMonth));
}
+ @Operation(summary = "当月动态厂区列(来自刷卡/食材实际数据)")
+ @GetMapping("/parks")
+ public R parks(@RequestParam String bizMonth) {
+ List parks = calcService.resolveParkColumns(bizMonth);
+ Map map = new java.util.LinkedHashMap<>();
+ map.put("parks", parks);
+ map.put("shareParks", parks);
+ return R.ok(map);
+ }
+
@ResponseExcel
@Operation(summary = "导出收入合计")
@GetMapping("/export/income")
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportFaceImportController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportFaceImportController.java
index eec4756fd..2d0a878a1 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportFaceImportController.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportFaceImportController.java
@@ -113,14 +113,16 @@ public class CanteenReportFaceImportController {
}
@SysLog("人脸明细汇总到刷卡明细")
- @Operation(summary = "统计汇总写入刷卡明细(支付方式原样作收入类别;B扫C等不自动归支付宝)")
+ @Operation(summary = "统计汇总写入刷卡明细;matchMode=PERSON|ROSTER;excludeParks 排除厂区(逗号分隔)")
@PostMapping("/aggregate")
public R aggregate(@RequestParam(required = false) String bizMonth,
@RequestParam(required = false) String dateStart,
@RequestParam(required = false) String dateEnd,
@RequestParam(required = false) String importLogId,
@RequestParam(defaultValue = "true") boolean replaceFace,
- @RequestParam(required = false) String orgLevel2s) {
+ @RequestParam(required = false) String orgLevel2s,
+ @RequestParam(defaultValue = "PERSON") String matchMode,
+ @RequestParam(required = false) String excludeParks) {
Long logId = null;
if (cn.hutool.core.util.StrUtil.isNotBlank(importLogId)) {
try {
@@ -134,7 +136,11 @@ public class CanteenReportFaceImportController {
java.util.List orgs = cn.hutool.core.util.StrUtil.isBlank(orgLevel2s)
? java.util.List.of()
: cn.hutool.core.util.StrUtil.splitTrim(orgLevel2s, ',');
- return faceImportService.aggregateToSwipe(bizMonth, dateStart, dateEnd, logId, replaceFace, orgs);
+ java.util.List exclude = cn.hutool.core.util.StrUtil.isBlank(excludeParks)
+ ? java.util.List.of()
+ : cn.hutool.core.util.StrUtil.splitTrim(excludeParks, ',');
+ return faceImportService.aggregateToSwipe(bizMonth, dateStart, dateEnd, logId, replaceFace, orgs, matchMode,
+ exclude);
}
@Operation(summary = "明细中的二级组织列表(汇总筛选用)")
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportFoodEnergyController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportFoodEnergyController.java
index 4132604c9..fb4e5d4ff 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportFoodEnergyController.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportFoodEnergyController.java
@@ -63,9 +63,11 @@ public class CanteenReportFoodEnergyController {
@SysLog("按月清空食材能耗")
@DeleteMapping("/month/{bizMonth}")
- public R clearMonth(@PathVariable String bizMonth) {
+ public R clearMonth(@PathVariable String bizMonth,
+ @RequestParam(required = false) String park) {
return R.ok(foodEnergyService.remove(Wrappers.lambdaQuery()
- .eq(CanteenReportFoodEnergyEntity::getBizMonth, bizMonth)));
+ .eq(CanteenReportFoodEnergyEntity::getBizMonth, bizMonth)
+ .eq(cn.hutool.core.util.StrUtil.isNotBlank(park), CanteenReportFoodEnergyEntity::getPark, park)));
}
@Operation(summary = "导入Excel(自动识别收发存汇总表 / 食堂报表「食材能耗」sheet)")
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportPersonController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportPersonController.java
index 45a88c83e..e7c79ecec 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportPersonController.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportPersonController.java
@@ -32,10 +32,11 @@ public class CanteenReportPersonController {
@Operation(summary = "分页")
@GetMapping("/page")
public R page(@ParameterObject Page page,
+ @RequestParam(required = false) String bizMonth,
@RequestParam(required = false) String empNo,
@RequestParam(required = false) String empName,
@RequestParam(required = false) String costCenter) {
- return R.ok(personService.pageList(page, empNo, empName, costCenter));
+ return R.ok(personService.pageList(page, bizMonth, empNo, empName, costCenter));
}
@Operation(summary = "详情")
@@ -72,20 +73,28 @@ public class CanteenReportPersonController {
return personService.clearAll();
}
+ @SysLog("按月清空人员信息")
+ @Operation(summary = "按适用月清空")
+ @DeleteMapping("/month")
+ public R clearByMonth(@RequestParam String bizMonth) {
+ return personService.clearByMonth(bizMonth);
+ }
+
@SysLog("导入人员信息")
- @Operation(summary = "导入Excel:replaceAll=true 全量覆盖;false 新增导入(同工号更新)")
+ @Operation(summary = "导入Excel:需传 bizMonth;replaceMonth=true 覆盖当月;false 同月同工号更新")
@PostMapping("/import")
public R importExcel(@RequestParam("file") MultipartFile file,
- @RequestParam(defaultValue = "true") boolean replaceAll) throws Exception {
+ @RequestParam String bizMonth,
+ @RequestParam(defaultValue = "true") boolean replaceMonth) throws Exception {
List rows = CanteenReportExcelUtils.readSheet(file,
CanteenReportPersonExcel.class, "刷卡明细");
- return personService.importExcel(rows, replaceAll);
+ return personService.importExcel(rows, bizMonth, replaceMonth);
}
@ResponseExcel
@Operation(summary = "导出")
@GetMapping("/export")
- public List export() {
- return personService.exportExcel();
+ public List export(@RequestParam(required = false) String bizMonth) {
+ return personService.exportExcel(bizMonth);
}
}
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportSettleParkController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportSettleParkController.java
index cbb1b6649..c680c4138 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportSettleParkController.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportSettleParkController.java
@@ -2,7 +2,6 @@ package com.bsd.pjcloud.canteen.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.canteen.entity.CanteenReportSettleParkEntity;
-import com.bsd.pjcloud.canteen.report.CanteenReportConstants;
import com.bsd.pjcloud.canteen.service.CanteenReportSettleParkService;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.log.annotation.SysLog;
@@ -34,10 +33,10 @@ public class CanteenReportSettleParkController {
return R.ok(settleParkService.pageList(page, settleCounter, park));
}
- @Operation(summary = "厂区枚举")
+ @Operation(summary = "厂区枚举(配置表+推荐名,可自由扩展)")
@GetMapping("/parks")
public R parks() {
- return R.ok(CanteenReportConstants.PARKS);
+ return R.ok(settleParkService.listConfiguredParks());
}
@Operation(summary = "详情")
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportFaceDetailMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportFaceDetailMapper.java
index 23ef847a2..5e98bb63a 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportFaceDetailMapper.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportFaceDetailMapper.java
@@ -14,14 +14,16 @@ public interface CanteenReportFaceDetailMapper extends BaseMapper list);
- int deletePhysicallyByImportLogId(@Param("importLogId") Long importLogId);
+ /** 分批物理删除,配合 limit 避免 OceanBase query_timeout */
+ int deletePhysicallyByImportLogId(@Param("importLogId") Long importLogId, @Param("limit") int limit);
int deletePhysicallyByDateRange(@Param("bizMonth") String bizMonth,
- @Param("dateStart") String dateStart, @Param("dateEnd") String dateEnd);
+ @Param("dateStart") String dateStart, @Param("dateEnd") String dateEnd,
+ @Param("limit") int limit);
int deletePhysicallyDateRangeExceptLog(@Param("bizMonth") String bizMonth,
@Param("dateStart") String dateStart, @Param("dateEnd") String dateEnd,
- @Param("importLogId") Long importLogId);
+ @Param("importLogId") Long importLogId, @Param("limit") int limit);
/**
* 按人+维度汇总;刷脸等→员工刷卡,B扫C等扫码保持支付方式原样。金额用订单金额。
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportPersonMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportPersonMapper.java
index 73c56e9c9..015d222c4 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportPersonMapper.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportPersonMapper.java
@@ -12,5 +12,7 @@ public interface CanteenReportPersonMapper extends BaseMapper list);
}
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportSwipeMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportSwipeMapper.java
index 60368a857..f23f785d8 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportSwipeMapper.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportSwipeMapper.java
@@ -12,22 +12,33 @@ public interface CanteenReportSwipeMapper extends BaseMapper excludeParks, @Param("limit") int limit);
int insertBatch(@Param("list") List list);
}
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportBoxImportService.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportBoxImportService.java
index 013d59548..a44de504f 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportBoxImportService.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportBoxImportService.java
@@ -9,4 +9,7 @@ import org.springframework.web.multipart.MultipartFile;
public interface CanteenReportBoxImportService {
R importExcel(MultipartFile file, String bizMonth, boolean replaceMonth);
+
+ /** 清空某月包厢刷卡;park 非空则只清该厂区 */
+ R clearByMonthPark(String bizMonth, String park);
}
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportCalcService.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportCalcService.java
index 549bb9155..8c5a923d9 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportCalcService.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportCalcService.java
@@ -23,4 +23,7 @@ public interface CanteenReportCalcService {
List businessReport(String bizMonth);
Map summary(String bizMonth);
+
+ /** 当月刷卡/食材实际出现的厂区列(动态) */
+ List resolveParkColumns(String bizMonth);
}
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportFaceImportService.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportFaceImportService.java
index eff5b15e1..d2d90f4f1 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportFaceImportService.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportFaceImportService.java
@@ -23,10 +23,12 @@ public interface CanteenReportFaceImportService extends IService orgLevel2s);
+ List orgLevel2s, String matchMode, List excludeParks);
Page pageLogs(Page page, String bizMonth, String status);
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportPersonService.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportPersonService.java
index 27d83e1d4..e5b3ee7cc 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportPersonService.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportPersonService.java
@@ -11,21 +11,27 @@ import java.util.Map;
public interface CanteenReportPersonService extends IService {
- Page pageList(Page page, String empNo, String empName, String costCenter);
+ Page pageList(Page page, String bizMonth, String empNo, String empName,
+ String costCenter);
R savePerson(CanteenReportPersonEntity entity);
R updatePerson(CanteenReportPersonEntity entity);
/**
- * @param replaceAll true=全量覆盖;false=新增导入(同工号则更新)
+ * 按适用月导入。
+ *
+ * @param bizMonth 适用月 yyyy-MM
+ * @param replaceMonth true=先清空该月再入库;false=同月同工号更新
*/
- R importExcel(List rows, boolean replaceAll);
+ R importExcel(List rows, String bizMonth, boolean replaceMonth);
- List exportExcel();
+ List exportExcel(String bizMonth);
R clearAll();
- /** empNo → costCenter(空核对成本也占位) */
- Map loadCostCenterMap();
+ R clearByMonth(String bizMonth);
+
+ /** 指定适用月:empNo → costCenter(空核对成本也占位) */
+ Map loadCostCenterMap(String bizMonth);
}
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportSettleParkService.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportSettleParkService.java
index 0e37a7cab..5882284f6 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportSettleParkService.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportSettleParkService.java
@@ -10,4 +10,7 @@ public interface CanteenReportSettleParkService extends IService pageList(Page page, String settleCounter, String park);
R saveOrUpdateRow(CanteenReportSettleParkEntity entity);
+
+ /** 配置过的厂区 + 推荐常量(可自由扩展) */
+ java.util.List listConfiguredParks();
}
diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportBoxImportServiceImpl.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportBoxImportServiceImpl.java
index 27bc57b7d..589847d89 100644
--- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportBoxImportServiceImpl.java
+++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportBoxImportServiceImpl.java
@@ -3,12 +3,13 @@ package com.bsd.pjcloud.canteen.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
-import com.bsd.pjcloud.canteen.excel.ExcelYuanConverter;
-import com.bsd.pjcloud.canteen.excel.ExcelYuanStringConverter;
import com.bsd.pjcloud.canteen.dto.report.CanteenReportBoxExcel;
import com.bsd.pjcloud.canteen.entity.CanteenReportSwipeEntity;
import com.bsd.pjcloud.canteen.mapper.CanteenReportSwipeMapper;
+import com.bsd.pjcloud.canteen.report.CanteenReportBoxExcelBinder;
import com.bsd.pjcloud.canteen.report.CanteenReportConstants;
import com.bsd.pjcloud.canteen.service.CanteenReportBoxImportService;
import com.bsd.pjcloud.common.core.constant.CommonConstants;
@@ -23,7 +24,11 @@ import org.springframework.web.multipart.MultipartFile;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
@Service
@RequiredArgsConstructor
@@ -31,9 +36,33 @@ public class CanteenReportBoxImportServiceImpl implements CanteenReportBoxImport
private static final String SOURCE_BOX = "BOX";
private static final int BATCH = 800;
+ private static final int DELETE_BATCH = 3000;
+ private static final int SKIP_SAMPLE_LIMIT = 50;
private final CanteenReportSwipeMapper swipeMapper;
+ private int deleteBoxByMonthPark(String bizMonth, String park) {
+ int total = 0;
+ int n;
+ do {
+ n = swipeMapper.deletePhysicallyBySourceBizMonthPark(SOURCE_BOX, bizMonth, park, DELETE_BATCH);
+ total += n;
+ }
+ while (n >= DELETE_BATCH);
+ return total;
+ }
+
+ private int deleteBoxByMonth(String bizMonth) {
+ int total = 0;
+ int n;
+ do {
+ n = swipeMapper.deletePhysicallyBySourceAndBizMonth(SOURCE_BOX, bizMonth, DELETE_BATCH);
+ total += n;
+ }
+ while (n >= DELETE_BATCH);
+ return total;
+ }
+
@Override
@Transactional(rollbackFor = Exception.class)
public R importExcel(MultipartFile file, String bizMonth, boolean replaceMonth) {
@@ -44,13 +73,15 @@ public class CanteenReportBoxImportServiceImpl implements CanteenReportBoxImport
return R.failed("请传入业务月份 bizMonth");
}
try {
- List rows = EasyExcel.read(file.getInputStream())
- .head(CanteenReportBoxExcel.class)
- .registerConverter(new ExcelYuanConverter())
- .registerConverter(new ExcelYuanStringConverter())
+ BoxRowAccumulator acc = new BoxRowAccumulator();
+ EasyExcel.read(file.getInputStream(), acc)
.sheet()
.headRowNumber(1)
- .doReadSync();
+ .doRead();
+ if (StrUtil.isNotBlank(acc.headError)) {
+ return R.failed(acc.headError);
+ }
+ List rows = acc.rows;
if (CollUtil.isEmpty(rows)) {
return R.failed("导入数据为空");
}
@@ -58,16 +89,27 @@ public class CanteenReportBoxImportServiceImpl implements CanteenReportBoxImport
Long tenantId = TenantContextHolder.getTenantId();
LocalDateTime now = LocalDateTime.now();
List saveList = new ArrayList<>();
+ Map skipReasonCount = new LinkedHashMap<>();
+ List skipSamples = new ArrayList<>();
int skip = 0;
- for (CanteenReportBoxExcel row : rows) {
+ for (int i = 0; i < rows.size(); i++) {
+ CanteenReportBoxExcel row = rows.get(i);
+ int excelRow = i + 2; // 表头第1行,数据从第2行起
+ if (isBlankRow(row)) {
+ skip++;
+ recordSkip(skipReasonCount, skipSamples, excelRow, "空行", row);
+ continue;
+ }
BigDecimal amount = resolveAmount(row);
if (amount == null || amount.compareTo(BigDecimal.ZERO) == 0) {
skip++;
+ recordSkip(skipReasonCount, skipSamples, excelRow, "无金额或金额为0(总计/食品+酒水)", row);
continue;
}
if (StrUtil.isBlank(row.getCostCenter()) && StrUtil.isBlank(row.getEmpNo())
&& StrUtil.isBlank(row.getPark())) {
skip++;
+ recordSkip(skipReasonCount, skipSamples, excelRow, "成本中心、签单人工号、园区均为空", row);
continue;
}
CanteenReportSwipeEntity e = new CanteenReportSwipeEntity();
@@ -95,22 +137,181 @@ public class CanteenReportBoxImportServiceImpl implements CanteenReportBoxImport
saveList.add(e);
}
if (CollUtil.isEmpty(saveList)) {
- return R.failed("无有效包厢金额可入库");
+ Map emptyData = buildSkipPayload(0, skip, skipReasonCount, skipSamples);
+ return R.failed(emptyData, "无有效包厢金额可入库。" + buildSkipSummary(skip, skipReasonCount, skipSamples));
}
if (replaceMonth) {
- swipeMapper.deletePhysicallyBySourceAndBizMonth(SOURCE_BOX, bizMonth.trim());
+ deleteBoxByMonth(bizMonth.trim());
}
for (List chunk : CollUtil.split(saveList, BATCH)) {
swipeMapper.insertBatch(chunk);
}
- return R.ok(null, String.format("包厢导入成功:写入刷卡 %d 条(收入类别=包厢收入),跳过 %d 条",
- saveList.size(), skip));
+ Map data = buildSkipPayload(saveList.size(), skip, skipReasonCount, skipSamples);
+ String msg = String.format("包厢导入成功:写入刷卡 %d 条(收入类别=包厢收入),跳过 %d 条",
+ saveList.size(), skip);
+ if (skip > 0) {
+ msg = msg + "。可查看跳过明细。";
+ }
+ return R.ok(data, msg);
}
catch (Exception e) {
return R.failed("导入失败: " + e.getMessage());
}
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public R clearByMonthPark(String bizMonth, String park) {
+ if (StrUtil.isBlank(bizMonth)) {
+ return R.failed("请传入业务月份 bizMonth");
+ }
+ String month = bizMonth.trim();
+ String parkTrim = StrUtil.trim(park);
+ int n = deleteBoxByMonthPark(month, StrUtil.isBlank(parkTrim) ? null : parkTrim);
+ if (StrUtil.isNotBlank(parkTrim)) {
+ return R.ok(null, String.format("已清空 %s 厂区「%s」包厢刷卡 %d 条", month, parkTrim, n));
+ }
+ return R.ok(null, String.format("已清空 %s 全部包厢刷卡 %d 条", month, n));
+ }
+
+ /**
+ * 按表头名称读包厢明细(Map 行),关键列缺失时直接报错。
+ */
+ static final class BoxRowAccumulator extends AnalysisEventListener