From d7142f13d5474353f0f36854b9ff5c711d5fbe93 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Mon, 17 Aug 2026 13:48:56 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A3=9F=E5=A0=82=E6=8A=A5=E8=A1=A8=EF=BC=8C?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=AE=A1=E7=90=86=E5=A7=93=E5=90=8D=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=95=B0=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/report/CanteenReportFaceAggRow.java | 31 ++ .../dto/report/CanteenReportFaceExcel.java | 141 +++++ .../entity/CanteenReportFaceDetailEntity.java | 178 +++++++ .../entity/CanteenReportImportLogEntity.java | 89 ++++ .../entity/CanteenReportSettleParkEntity.java | 45 ++ .../entity/CanteenReportSwipeEntity.java | 9 + .../report/CanteenReportFaceMapper.java | 231 ++++++++ .../CanteenReportFaceImportController.java | 91 ++++ .../CanteenReportSettleParkController.java | 73 +++ .../CanteenReportSwipeController.java | 5 +- .../mapper/CanteenReportFaceDetailMapper.java | 34 ++ .../mapper/CanteenReportImportLogMapper.java | 9 + .../mapper/CanteenReportSettleParkMapper.java | 9 + .../mapper/CanteenReportSwipeMapper.java | 14 + .../CanteenReportFaceImportService.java | 32 ++ .../CanteenReportSettleParkService.java | 13 + .../service/CanteenReportSwipeService.java | 3 +- .../CanteenReportFaceImportServiceImpl.java | 495 ++++++++++++++++++ .../CanteenReportSettleParkServiceImpl.java | 47 ++ .../impl/CanteenReportSwipeServiceImpl.java | 3 +- .../mapper/CanteenReportFaceDetailMapper.xml | 118 +++++ .../mapper/CanteenReportSwipeMapper.xml | 33 +- .../admin/api/util/PersonNameUtils.java | 7 +- .../impl/SysUserImportExportServiceImpl.java | 4 +- 24 files changed, 1701 insertions(+), 13 deletions(-) create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportFaceAggRow.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportFaceExcel.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportFaceDetailEntity.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportImportLogEntity.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportSettleParkEntity.java create mode 100644 pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportFaceMapper.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportFaceImportController.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportSettleParkController.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportFaceDetailMapper.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportImportLogMapper.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportSettleParkMapper.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportFaceImportService.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportSettleParkService.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportFaceImportServiceImpl.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportSettleParkServiceImpl.java create mode 100644 pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenReportFaceDetailMapper.xml diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportFaceAggRow.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportFaceAggRow.java new file mode 100644 index 000000000..c1ba2ebfb --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportFaceAggRow.java @@ -0,0 +1,31 @@ +package com.bsd.pjcloud.canteen.dto.report; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 人脸明细按维度汇总后的中间行(再映射厂区/成本中心写入刷卡明细) + */ +@Data +public class CanteenReportFaceAggRow { + + private String empNo; + + private String empName; + + private String legalEntity; + + private String deptName; + + /** 三级组织 → 消费区域 */ + private String orgLevel3; + + /** 交易设备 → 结算台 */ + private String deviceName; + + /** 员工刷卡 / 支付宝 */ + private String incomeType; + + private BigDecimal amount; +} 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 new file mode 100644 index 000000000..4c8a2e06e --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/dto/report/CanteenReportFaceExcel.java @@ -0,0 +1,141 @@ +package com.bsd.pjcloud.canteen.dto.report; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +/** + * 人脸消费系统导出 Excel,列序与样表一致(共 43 列)。 + * 明细入库原样落库;汇总到刷卡明细时再做字段映射。 + */ +@Data +public class CanteenReportFaceExcel { + + @ExcelProperty(index = 0) + private String orderNo; + + @ExcelProperty(index = 1) + private String thirdOrderNo; + + @ExcelProperty(index = 2) + private String createTime; + + @ExcelProperty(index = 3) + private String payTime; + + @ExcelProperty(index = 4) + private String deductTime; + + @ExcelProperty(index = 5) + private String orgLevel1; + + @ExcelProperty(index = 6) + private String orgLevel2; + + @ExcelProperty(index = 7) + private String orgLevel3; + + @ExcelProperty(index = 8) + private String canteen; + + @ExcelProperty(index = 9) + private String canteenArea; + + @ExcelProperty(index = 10) + private String consumePoint; + + @ExcelProperty(index = 11) + private String empName; + + @ExcelProperty(index = 12) + private String phone; + + @ExcelProperty(index = 13) + private String legalEntity; + + @ExcelProperty(index = 14) + private String empNo; + + @ExcelProperty(index = 15) + private String cardNo; + + @ExcelProperty(index = 16) + private String groupName; + + @ExcelProperty(index = 17) + private String deptName; + + @ExcelProperty(index = 18) + private String payMethod; + + @ExcelProperty(index = 19) + private String payType; + + @ExcelProperty(index = 20) + private String orderAmount; + + @ExcelProperty(index = 21) + private String changeAmount; + + @ExcelProperty(index = 22) + private String storedChange; + + @ExcelProperty(index = 23) + private String subsidyChange; + + @ExcelProperty(index = 24) + private String giftChange; + + @ExcelProperty(index = 25) + private String storedBalance; + + @ExcelProperty(index = 26) + private String subsidyBalance; + + @ExcelProperty(index = 27) + private String giftBalance; + + @ExcelProperty(index = 28) + private String changeOrg; + + @ExcelProperty(index = 29) + private String discount; + + @ExcelProperty(index = 30) + private String discountAmount; + + @ExcelProperty(index = 31) + private String discountType; + + @ExcelProperty(index = 32) + private String couponType; + + @ExcelProperty(index = 33) + private String deductAmount; + + @ExcelProperty(index = 34) + private String feeAmount; + + @ExcelProperty(index = 35) + private String mealSubsidy; + + @ExcelProperty(index = 36) + private String consumeType; + + @ExcelProperty(index = 37) + private String mealSegment; + + @ExcelProperty(index = 38) + private String deviceName; + + @ExcelProperty(index = 39) + private String deviceStatus; + + @ExcelProperty(index = 40) + private String remark; + + @ExcelProperty(index = 41) + private String operatorName; + + @ExcelProperty(index = 42) + private String visitorPayMethod; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportFaceDetailEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportFaceDetailEntity.java new file mode 100644 index 000000000..5dec82367 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportFaceDetailEntity.java @@ -0,0 +1,178 @@ +package com.bsd.pjcloud.canteen.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 人脸消费逐笔明细:列与导出 Excel 一致(另含 biz_month/biz_date 便于分段与汇总)。 + */ +@Data +@TableName("canteen_report_face_detail") +@EqualsAndHashCode(callSuper = true) +@Schema(description = "食堂报表-人脸消费逐笔明细(Excel原列)") +public class CanteenReportFaceDetailEntity extends Model { + + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + private Long importLogId; + + @Schema(description = "报表归属月") + private String bizMonth; + + @Schema(description = "业务日(由扣款/支付时间解析)") + private String bizDate; + + @Schema(description = "订单号") + private String orderNo; + + @Schema(description = "第三方订单号") + private String thirdOrderNo; + + @Schema(description = "创建时间") + private String orderCreateTime; + + @Schema(description = "支付时间") + private String payTime; + + @Schema(description = "扣款时间") + private String deductTime; + + @Schema(description = "一级组织") + private String orgLevel1; + + @Schema(description = "二级组织") + private String orgLevel2; + + @Schema(description = "三级组织") + private String orgLevel3; + + @Schema(description = "食堂") + private String canteen; + + @Schema(description = "食堂区域") + private String canteenArea; + + @Schema(description = "消费点") + private String consumePoint; + + @Schema(description = "姓名") + private String empName; + + @Schema(description = "手机号") + private String phone; + + @Schema(description = "法律归属") + private String legalEntity; + + @Schema(description = "人员编号") + private String empNo; + + @Schema(description = "卡号") + private String cardNo; + + @Schema(description = "分组") + private String groupName; + + @Schema(description = "部门") + private String deptName; + + @Schema(description = "支付方式") + private String payMethod; + + @Schema(description = "支付类型") + private String payType; + + @Schema(description = "订单金额") + private BigDecimal orderAmount; + + @Schema(description = "动账金额") + private BigDecimal changeAmount; + + @Schema(description = "储值动账") + private BigDecimal storedChange; + + @Schema(description = "补贴动账") + private BigDecimal subsidyChange; + + @Schema(description = "赠送动账") + private BigDecimal giftChange; + + @Schema(description = "储值钱包余额") + private BigDecimal storedBalance; + + @Schema(description = "补贴钱包余额") + private BigDecimal subsidyBalance; + + @Schema(description = "赠送钱包余额") + private BigDecimal giftBalance; + + @Schema(description = "动账组织") + private String changeOrg; + + @Schema(description = "优惠") + private String discount; + + @Schema(description = "优惠金额") + private BigDecimal discountAmount; + + @Schema(description = "优惠类型") + private String discountType; + + @Schema(description = "券类型") + private String couponType; + + @Schema(description = "抵扣金额") + private BigDecimal deductAmount; + + @Schema(description = "手续费") + private BigDecimal feeAmount; + + @Schema(description = "餐补") + private BigDecimal mealSubsidy; + + @Schema(description = "消费类型") + private String consumeType; + + @Schema(description = "餐段") + private String mealSegment; + + @Schema(description = "交易设备") + private String deviceName; + + @Schema(description = "设备状态") + private String deviceStatus; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "操作员") + private String operatorName; + + @Schema(description = "访客餐支付方式") + private String visitorPayMethod; + + @TableField(fill = FieldFill.INSERT) + private String createBy; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + private Long tenantId; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportImportLogEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportImportLogEntity.java new file mode 100644 index 000000000..ea63a9ac8 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportImportLogEntity.java @@ -0,0 +1,89 @@ +package com.bsd.pjcloud.canteen.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +@Data +@TableName("canteen_report_import_log") +@EqualsAndHashCode(callSuper = true) +@Schema(description = "食堂报表-导入日志(演示)") +public class CanteenReportImportLogEntity extends Model { + + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键") + private Long id; + + @Schema(description = "业务月份 yyyy-MM") + private String bizMonth; + + @Schema(description = "数据起日 yyyy-MM-dd") + private String dateStart; + + @Schema(description = "数据止日 yyyy-MM-dd") + private String dateEnd; + + @Schema(description = "原始文件名") + private String fileName; + + @Schema(description = "已存档 Excel 访问路径") + private String fileUrl; + + @Schema(description = "OSS 文件名") + private String ossFileName; + + @Schema(description = "来源类型 FACE") + private String sourceType; + + @Schema(description = "Excel 有效行数") + private Integer totalRows; + + @Schema(description = "成功入库行数") + private Integer successRows; + + @Schema(description = "跳过行数") + private Integer skipRows; + + @Schema(description = "汇总写入刷卡明细行数") + private Integer aggRows; + + @Schema(description = "NONE/DONE/FAILED") + private String aggStatus; + + @Schema(description = "最近汇总时间") + private LocalDateTime aggTime; + + @Schema(description = "RUNNING/SUCCESS/FAILED") + private String status; + + @Schema(description = "失败原因摘要") + private String errorMsg; + + @Schema(description = "耗时毫秒") + private Long costMs; + + @Schema(description = "操作人") + private String operator; + + @TableField(fill = FieldFill.INSERT) + private String createBy; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + private Long tenantId; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportSettleParkEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportSettleParkEntity.java new file mode 100644 index 000000000..57250ffe6 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportSettleParkEntity.java @@ -0,0 +1,45 @@ +package com.bsd.pjcloud.canteen.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +@Data +@TableName("canteen_report_settle_park") +@EqualsAndHashCode(callSuper = true) +@Schema(description = "食堂报表-结算台厂区配置") +public class CanteenReportSettleParkEntity extends Model { + + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + @Schema(description = "结算台/交易设备名称") + private String settleCounter; + + @Schema(description = "厂区") + private String park; + + private String remark; + + @TableField(fill = FieldFill.INSERT) + private String createBy; + + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableLogic + @TableField(fill = FieldFill.INSERT) + private String delFlag; + + private Long tenantId; +} diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportSwipeEntity.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportSwipeEntity.java index 4f6bda956..bdb853316 100644 --- a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportSwipeEntity.java +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/entity/CanteenReportSwipeEntity.java @@ -22,6 +22,9 @@ public class CanteenReportSwipeEntity extends Model { @Schema(description = "业务月份 yyyy-MM") private String bizMonth; + @Schema(description = "业务日期 yyyy-MM-dd") + private String bizDate; + @Schema(description = "工号") private String empNo; @@ -58,6 +61,12 @@ public class CanteenReportSwipeEntity extends Model { @Schema(description = "备注标识") private String remarkTag; + @Schema(description = "导入日志ID") + private Long importLogId; + + @Schema(description = "来源 FACE/MANUAL/EXCEL") + private String source; + @TableField(fill = FieldFill.INSERT) @Schema(description = "创建人") private String createBy; diff --git a/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportFaceMapper.java b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportFaceMapper.java new file mode 100644 index 000000000..5ef4cddde --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-api/src/main/java/com/bsd/pjcloud/canteen/report/CanteenReportFaceMapper.java @@ -0,0 +1,231 @@ +package com.bsd.pjcloud.canteen.report; + +import com.bsd.pjcloud.canteen.dto.report.CanteenReportFaceExcel; +import com.bsd.pjcloud.canteen.entity.CanteenReportFaceDetailEntity; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 人脸 Excel → 明细原样入库;汇总时再映射收入类别 / 成本中心。 + */ +public final class CanteenReportFaceMapper { + + private static final Pattern DATE_PATTERN = Pattern.compile("(20\\d{2})[-/](\\d{1,2})[-/](\\d{1,2})"); + + private static final Map LEGAL_TO_CENTER = new LinkedHashMap<>(); + + static { + for (String c : CanteenReportConstants.COST_CENTERS) { + LEGAL_TO_CENTER.put(c, c); + LEGAL_TO_CENTER.put(c.replace('-', '_'), c); + LEGAL_TO_CENTER.put(c.replace('_', '-'), c); + } + LEGAL_TO_CENTER.put("嘉兴敏惠_铝件", "嘉兴敏惠-铝件"); + LEGAL_TO_CENTER.put("嘉兴敏惠_塑件", "嘉兴敏惠-塑件"); + LEGAL_TO_CENTER.put("嘉兴敏惠_塑件BU职能", "嘉兴敏惠-塑件BU职能"); + LEGAL_TO_CENTER.put("嘉兴敏惠_塑件技术中心", "嘉兴敏惠-塑件技术中心"); + LEGAL_TO_CENTER.put("嘉兴敏惠_塑件项目中心", "嘉兴敏惠-塑件项目中心"); + LEGAL_TO_CENTER.put("嘉兴敏惠_研究院", "嘉兴敏惠-研究院"); + LEGAL_TO_CENTER.put("嘉兴敏惠_地区共享", "嘉兴敏惠-地区共享"); + LEGAL_TO_CENTER.put("嘉兴敏华_塑件工厂", "嘉兴敏华"); + LEGAL_TO_CENTER.put("嘉兴信元_塑件开发中心", "嘉兴信元-塑件开发中心"); + LEGAL_TO_CENTER.put("嘉兴信元_塑件模具厂", "嘉兴信元-塑件模具厂"); + LEGAL_TO_CENTER.put("宁波敏实汽车零部件技术研发有限公司", "宁波敏实汽车零部件技术研发有限公司"); + LEGAL_TO_CENTER.put("浙江敏能科技有限公司", "浙江敏能"); + LEGAL_TO_CENTER.put("浙江敏盛汽车零部件有限公司", "浙江敏盛"); + LEGAL_TO_CENTER.put("嘉兴敏胜汽车零部件有限公司", "嘉兴敏胜"); + LEGAL_TO_CENTER.put("嘉兴敏实机械有限公司", "嘉兴敏实"); + LEGAL_TO_CENTER.put("敏实海拉(嘉兴)汽车零部件有限公司", "敏实海拉"); + LEGAL_TO_CENTER.put("嘉兴敏能汽车零部件有限公司", "嘉兴敏能"); + } + + private CanteenReportFaceMapper() { + } + + /** Excel 原列写入明细表(不做刷卡字段映射) */ + public static CanteenReportFaceDetailEntity toDetail(CanteenReportFaceExcel row, String bizMonth, Long importLogId) { + if (row == null) { + return null; + } + String bizDate = extractBizDate(row.getDeductTime(), row.getPayTime(), row.getCreateTime()); + BigDecimal orderAmt = firstAmount(row.getOrderAmount(), row.getChangeAmount()); + if (isBlank(row.getEmpNo()) && isBlank(row.getEmpName()) && isBlank(row.getOrderNo()) && orderAmt == null + && isBlank(bizDate)) { + return null; + } + + CanteenReportFaceDetailEntity e = new CanteenReportFaceDetailEntity(); + e.setImportLogId(importLogId); + if (!isBlank(bizMonth)) { + e.setBizMonth(bizMonth.trim()); + } + else if (bizDate != null && bizDate.length() >= 7) { + e.setBizMonth(bizDate.substring(0, 7)); + } + e.setBizDate(bizDate); + + e.setOrderNo(trim(row.getOrderNo())); + e.setThirdOrderNo(trim(row.getThirdOrderNo())); + e.setOrderCreateTime(trim(row.getCreateTime())); + e.setPayTime(trim(row.getPayTime())); + e.setDeductTime(trim(row.getDeductTime())); + e.setOrgLevel1(trim(row.getOrgLevel1())); + e.setOrgLevel2(trim(row.getOrgLevel2())); + e.setOrgLevel3(trim(row.getOrgLevel3())); + e.setCanteen(trim(row.getCanteen())); + e.setCanteenArea(trim(row.getCanteenArea())); + e.setConsumePoint(trim(row.getConsumePoint())); + e.setEmpName(trim(row.getEmpName())); + e.setPhone(trim(row.getPhone())); + e.setLegalEntity(trim(row.getLegalEntity())); + e.setEmpNo(trim(row.getEmpNo())); + e.setCardNo(trim(row.getCardNo())); + e.setGroupName(trim(row.getGroupName())); + e.setDeptName(trim(row.getDeptName())); + e.setPayMethod(trim(row.getPayMethod())); + e.setPayType(trim(row.getPayType())); + e.setOrderAmount(orderAmt == null ? BigDecimal.ZERO : orderAmt); + e.setChangeAmount(parseAmount(row.getChangeAmount())); + e.setStoredChange(parseAmount(row.getStoredChange())); + e.setSubsidyChange(parseAmount(row.getSubsidyChange())); + e.setGiftChange(parseAmount(row.getGiftChange())); + e.setStoredBalance(parseAmount(row.getStoredBalance())); + e.setSubsidyBalance(parseAmount(row.getSubsidyBalance())); + e.setGiftBalance(parseAmount(row.getGiftBalance())); + e.setChangeOrg(trim(row.getChangeOrg())); + e.setDiscount(trim(row.getDiscount())); + e.setDiscountAmount(parseAmount(row.getDiscountAmount())); + e.setDiscountType(trim(row.getDiscountType())); + e.setCouponType(trim(row.getCouponType())); + e.setDeductAmount(parseAmount(row.getDeductAmount())); + e.setFeeAmount(parseAmount(row.getFeeAmount())); + e.setMealSubsidy(parseAmount(row.getMealSubsidy())); + e.setConsumeType(trim(row.getConsumeType())); + e.setMealSegment(trim(row.getMealSegment())); + e.setDeviceName(trim(row.getDeviceName())); + e.setDeviceStatus(trim(row.getDeviceStatus())); + e.setRemark(trim(row.getRemark())); + e.setOperatorName(trim(row.getOperatorName())); + e.setVisitorPayMethod(trim(row.getVisitorPayMethod())); + return e; + } + + /** 汇总用:B扫C 等 → 支付宝;其余 → 员工刷卡 */ + public static String mapIncomeType(String payMethod, String payType) { + String m = nvl(payMethod) + " " + nvl(payType); + if (containsAny(m, "B扫C", "收钱吧", "支付宝", "微信", "扫码")) { + return CanteenReportConstants.INCOME_ALIPAY; + } + return CanteenReportConstants.INCOME_SWIPE; + } + + /** 汇总用:法律归属 → 核对成本 */ + public static String mapCostCenter(String legalEntity) { + if (isBlank(legalEntity)) { + return "未匹配成本中心"; + } + String raw = legalEntity.trim(); + if (LEGAL_TO_CENTER.containsKey(raw)) { + return LEGAL_TO_CENTER.get(raw); + } + String norm = raw.replace('_', '-'); + if (LEGAL_TO_CENTER.containsKey(norm)) { + return LEGAL_TO_CENTER.get(norm); + } + String stripped = raw.replace("有限公司", "").trim(); + for (String c : CanteenReportConstants.COST_CENTERS) { + if (raw.contains(c) || (!stripped.isEmpty() && c.contains(stripped))) { + return c; + } + } + String compactRaw = raw.replace("-", "").replace("_", ""); + for (String c : CanteenReportConstants.COST_CENTERS) { + String compact = c.replace("-", "").replace("_", ""); + if (compactRaw.contains(compact)) { + return c; + } + } + return raw; + } + + public static String extractBizDate(String... candidates) { + for (String c : candidates) { + if (isBlank(c)) { + continue; + } + String s = c.trim(); + Matcher m = DATE_PATTERN.matcher(s); + if (m.find()) { + try { + return LocalDate.of(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)), + Integer.parseInt(m.group(3))).toString(); + } + catch (Exception ignored) { + // continue + } + } + try { + String head = s.length() >= 10 ? s.substring(0, 10).replace('/', '-') : s.replace('/', '-'); + return LocalDate.parse(head, DateTimeFormatter.ISO_LOCAL_DATE).toString(); + } + catch (DateTimeParseException ignored) { + // continue + } + } + return null; + } + + public static BigDecimal firstAmount(String... vals) { + for (String v : vals) { + BigDecimal b = parseAmount(v); + if (b != null) { + return b; + } + } + return null; + } + + public static BigDecimal parseAmount(String v) { + if (isBlank(v)) { + return null; + } + String s = v.trim().replace(",", "").replace("¥", "").replace("元", ""); + try { + return new BigDecimal(s); + } + catch (Exception e) { + return null; + } + } + + private static boolean containsAny(String text, String... keys) { + if (isBlank(text)) { + return false; + } + for (String k : keys) { + if (text.contains(k)) { + return true; + } + } + return false; + } + + private static boolean isBlank(String s) { + return s == null || s.trim().isEmpty(); + } + + private static String trim(String s) { + return s == null ? null : s.trim(); + } + + private static String nvl(String s) { + return s == null ? "" : s; + } +} 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 new file mode 100644 index 000000000..828b9eeff --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportFaceImportController.java @@ -0,0 +1,91 @@ +package com.bsd.pjcloud.canteen.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.canteen.service.CanteenReportFaceImportService; +import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.log.annotation.SysLog; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +/** + * 食堂报表-人脸消费导入(明细落库 + 汇总刷卡) + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/canteenReport/faceImport") +@Tag(description = "canteenReportFaceImport", name = "食堂报表-人脸消费导入(演示)") +@SecurityRequirement(name = HttpHeaders.AUTHORIZATION) +public class CanteenReportFaceImportController { + + private final CanteenReportFaceImportService faceImportService; + + @Operation(summary = "导入日志分页") + @GetMapping("/log/page") + public R page(@ParameterObject Page page, + @RequestParam(required = false) String bizMonth, + @RequestParam(required = false) String status) { + return R.ok(faceImportService.pageLogs(page, bizMonth, status)); + } + + @Operation(summary = "导入日志详情") + @GetMapping("/log/{id}") + public R get(@PathVariable Long id) { + return R.ok(faceImportService.getById(id)); + } + + @Operation(summary = "人脸逐笔明细分页") + @GetMapping("/detail/page") + public R detailPage(@ParameterObject Page page, + @RequestParam(required = false) String bizMonth, + @RequestParam(required = false) String importLogId, + @RequestParam(required = false) String empNo, + @RequestParam(required = false) String payMethod) { + Long logId = null; + if (cn.hutool.core.util.StrUtil.isNotBlank(importLogId)) { + try { + logId = Long.parseLong(importLogId.trim()); + } + catch (NumberFormatException e) { + return R.failed("importLogId 非法"); + } + } + return R.ok(faceImportService.pageDetails(page, bizMonth, logId, empNo, payMethod)); + } + + @SysLog("人脸消费Excel导入明细") + @Operation(summary = "流式导入人脸Excel到逐笔明细表") + @PostMapping("/import") + public R importExcel(@RequestParam("file") MultipartFile file, + @RequestParam(required = false) String bizMonth, + @RequestParam(required = false) String dateStart, + @RequestParam(required = false) String dateEnd, + @RequestParam(defaultValue = "true") boolean replaceRange) { + return faceImportService.importFaceExcel(file, bizMonth, dateStart, dateEnd, replaceRange); + } + + @SysLog("人脸明细汇总到刷卡明细") + @Operation(summary = "统计汇总写入刷卡明细(B扫C→支付宝)") + @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) { + Long logId = null; + if (cn.hutool.core.util.StrUtil.isNotBlank(importLogId)) { + try { + logId = Long.parseLong(importLogId.trim()); + } + catch (NumberFormatException e) { + return R.failed("importLogId 非法: " + importLogId); + } + } + return faceImportService.aggregateToSwipe(bizMonth, dateStart, dateEnd, logId, replaceFace); + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..cbb1b6649 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportSettleParkController.java @@ -0,0 +1,73 @@ +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; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/canteenReport/settlePark") +@Tag(description = "canteenReportSettlePark", name = "食堂报表-结算台厂区配置(演示)") +@SecurityRequirement(name = HttpHeaders.AUTHORIZATION) +public class CanteenReportSettleParkController { + + private final CanteenReportSettleParkService settleParkService; + + @Operation(summary = "分页") + @GetMapping("/page") + public R page(@ParameterObject Page page, + @RequestParam(required = false) String settleCounter, + @RequestParam(required = false) String park) { + return R.ok(settleParkService.pageList(page, settleCounter, park)); + } + + @Operation(summary = "厂区枚举") + @GetMapping("/parks") + public R parks() { + return R.ok(CanteenReportConstants.PARKS); + } + + @Operation(summary = "详情") + @GetMapping("/{id}") + public R get(@PathVariable Long id) { + return R.ok(settleParkService.getById(id)); + } + + @SysLog("新增结算台厂区") + @PostMapping + public R add(@RequestBody CanteenReportSettleParkEntity entity) { + return settleParkService.saveOrUpdateRow(entity); + } + + @SysLog("修改结算台厂区") + @PutMapping + public R edit(@RequestBody CanteenReportSettleParkEntity entity) { + return settleParkService.saveOrUpdateRow(entity); + } + + @SysLog("删除结算台厂区") + @DeleteMapping + public R remove(@RequestBody Long[] ids) { + return R.ok(settleParkService.removeBatchByIds(Arrays.asList(ids))); + } + + @Operation(summary = "全部列表(汇总用)") + @GetMapping("/list") + public R listAll() { + List list = settleParkService.list(); + return R.ok(list); + } +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportSwipeController.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportSwipeController.java index 85a850cb5..c6eab4466 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportSwipeController.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/controller/CanteenReportSwipeController.java @@ -37,8 +37,9 @@ public class CanteenReportSwipeController { @RequestParam(required = false) String park, @RequestParam(required = false) String incomeType, @RequestParam(required = false) String costCenter, - @RequestParam(required = false) String empNo) { - return R.ok(swipeService.pageList(page, bizMonth, park, incomeType, costCenter, empNo)); + @RequestParam(required = false) String empNo, + @RequestParam(required = false) Long importLogId) { + return R.ok(swipeService.pageList(page, bizMonth, park, incomeType, costCenter, empNo, importLogId)); } @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 new file mode 100644 index 000000000..203dc4020 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportFaceDetailMapper.java @@ -0,0 +1,34 @@ +package com.bsd.pjcloud.canteen.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bsd.pjcloud.canteen.dto.report.CanteenReportFaceAggRow; +import com.bsd.pjcloud.canteen.entity.CanteenReportFaceDetailEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface CanteenReportFaceDetailMapper extends BaseMapper { + + int insertBatch(@Param("list") List list); + + int deletePhysicallyByImportLogId(@Param("importLogId") Long importLogId); + + int deletePhysicallyByDateRange(@Param("bizMonth") String bizMonth, + @Param("dateStart") String dateStart, @Param("dateEnd") String dateEnd); + + int deletePhysicallyDateRangeExceptLog(@Param("bizMonth") String bizMonth, + @Param("dateStart") String dateStart, @Param("dateEnd") String dateEnd, + @Param("importLogId") Long importLogId); + + /** + * 按人+维度汇总;B扫C 等归入支付宝,其余归入员工刷卡。金额用订单金额。 + * 传入 importLogId 时只按批次汇总,忽略日期段。 + */ + List selectAggByMonth(@Param("bizMonth") String bizMonth, + @Param("dateStart") String dateStart, @Param("dateEnd") String dateEnd, + @Param("importLogId") Long importLogId); + + long countDetail(@Param("bizMonth") String bizMonth, @Param("importLogId") Long importLogId); +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportImportLogMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportImportLogMapper.java new file mode 100644 index 000000000..7f32cf9ce --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportImportLogMapper.java @@ -0,0 +1,9 @@ +package com.bsd.pjcloud.canteen.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bsd.pjcloud.canteen.entity.CanteenReportImportLogEntity; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CanteenReportImportLogMapper extends BaseMapper { +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportSettleParkMapper.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportSettleParkMapper.java new file mode 100644 index 000000000..4affea9ba --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/mapper/CanteenReportSettleParkMapper.java @@ -0,0 +1,9 @@ +package com.bsd.pjcloud.canteen.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.bsd.pjcloud.canteen.entity.CanteenReportSettleParkEntity; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CanteenReportSettleParkMapper extends BaseMapper { +} 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 a1f31b349..ed1c042c8 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,5 +12,19 @@ public interface CanteenReportSwipeMapper extends BaseMapper list); } 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 new file mode 100644 index 000000000..335214711 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportFaceImportService.java @@ -0,0 +1,32 @@ +package com.bsd.pjcloud.canteen.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.bsd.pjcloud.canteen.entity.CanteenReportFaceDetailEntity; +import com.bsd.pjcloud.canteen.entity.CanteenReportImportLogEntity; +import com.bsd.pjcloud.common.core.util.R; +import org.springframework.web.multipart.MultipartFile; + +public interface CanteenReportFaceImportService extends IService { + + /** + * 流式导入人脸 Excel → 逐笔明细表(不直接写刷卡明细)。 + */ + R importFaceExcel(MultipartFile file, String bizMonth, String dateStart, String dateEnd, boolean replaceRange); + + /** + * 将明细汇总写入刷卡明细。B扫C→支付宝,其余→员工刷卡;厂区按结算台配置。 + * + * @param bizMonth 业务月 + * @param dateStart 可选日期段 + * @param dateEnd 可选日期段 + * @param importLogId 可选,仅汇总某一批导入 + * @param replaceFace 是否先清空该月已有 FACE 汇总刷卡行 + */ + R aggregateToSwipe(String bizMonth, String dateStart, String dateEnd, Long importLogId, boolean replaceFace); + + Page pageLogs(Page page, String bizMonth, String status); + + Page pageDetails(Page page, String bizMonth, Long importLogId, String empNo, + String payMethod); +} 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 new file mode 100644 index 000000000..0e37a7cab --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportSettleParkService.java @@ -0,0 +1,13 @@ +package com.bsd.pjcloud.canteen.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.bsd.pjcloud.canteen.entity.CanteenReportSettleParkEntity; +import com.bsd.pjcloud.common.core.util.R; + +public interface CanteenReportSettleParkService extends IService { + + Page pageList(Page page, String settleCounter, String park); + + R saveOrUpdateRow(CanteenReportSettleParkEntity entity); +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportSwipeService.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportSwipeService.java index 16c50a2f8..843146cb0 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportSwipeService.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/CanteenReportSwipeService.java @@ -11,7 +11,8 @@ import java.util.List; public interface CanteenReportSwipeService extends IService { - Page pageList(Page page, String bizMonth, String park, String incomeType, String costCenter, String empNo); + Page pageList(Page page, String bizMonth, String park, String incomeType, String costCenter, + String empNo, Long importLogId); R importExcel(List rows, String bizMonth, boolean replaceMonth, BindingResult bindingResult); diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportFaceImportServiceImpl.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportFaceImportServiceImpl.java new file mode 100644 index 000000000..a8181e389 --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportFaceImportServiceImpl.java @@ -0,0 +1,495 @@ +package com.bsd.pjcloud.canteen.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bsd.pjcloud.admin.api.feign.RemoteFileService; +import com.bsd.pjcloud.canteen.dto.report.CanteenReportFaceAggRow; +import com.bsd.pjcloud.canteen.dto.report.CanteenReportFaceExcel; +import com.bsd.pjcloud.canteen.entity.CanteenReportFaceDetailEntity; +import com.bsd.pjcloud.canteen.entity.CanteenReportImportLogEntity; +import com.bsd.pjcloud.canteen.entity.CanteenReportSettleParkEntity; +import com.bsd.pjcloud.canteen.entity.CanteenReportSwipeEntity; +import com.bsd.pjcloud.canteen.mapper.CanteenReportFaceDetailMapper; +import com.bsd.pjcloud.canteen.mapper.CanteenReportImportLogMapper; +import com.bsd.pjcloud.canteen.mapper.CanteenReportSettleParkMapper; +import com.bsd.pjcloud.canteen.mapper.CanteenReportSwipeMapper; +import com.bsd.pjcloud.canteen.report.CanteenReportFaceMapper; +import com.bsd.pjcloud.canteen.service.CanteenReportFaceImportService; +import com.bsd.pjcloud.common.core.constant.CommonConstants; +import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.data.tenant.TenantContextHolder; +import com.bsd.pjcloud.common.security.util.SecurityUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.math.BigDecimal; +import java.nio.file.Files; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 人脸消费:Excel → 逐笔明细表;再统计汇总 → 刷卡明细。 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class CanteenReportFaceImportServiceImpl extends ServiceImpl + implements CanteenReportFaceImportService { + + private static final int BATCH_SIZE = 1500; + + private final CanteenReportFaceDetailMapper faceDetailMapper; + private final CanteenReportSwipeMapper swipeMapper; + private final CanteenReportSettleParkMapper settleParkMapper; + private final RemoteFileService remoteFileService; + + @Override + public Page pageLogs(Page page, String bizMonth, String status) { + return page(page, Wrappers.lambdaQuery() + .eq(StrUtil.isNotBlank(bizMonth), CanteenReportImportLogEntity::getBizMonth, bizMonth) + .eq(StrUtil.isNotBlank(status), CanteenReportImportLogEntity::getStatus, status) + .orderByDesc(CanteenReportImportLogEntity::getCreateTime)); + } + + @Override + public Page pageDetails(Page page, String bizMonth, Long importLogId, String empNo, + String payMethod) { + return faceDetailMapper.selectPage(page, Wrappers.lambdaQuery() + .eq(StrUtil.isNotBlank(bizMonth), CanteenReportFaceDetailEntity::getBizMonth, bizMonth) + .eq(importLogId != null, CanteenReportFaceDetailEntity::getImportLogId, importLogId) + .like(StrUtil.isNotBlank(empNo), CanteenReportFaceDetailEntity::getEmpNo, empNo) + .like(StrUtil.isNotBlank(payMethod), CanteenReportFaceDetailEntity::getPayMethod, payMethod) + .orderByDesc(CanteenReportFaceDetailEntity::getId)); + } + + @Override + public R importFaceExcel(MultipartFile file, String bizMonth, String dateStart, String dateEnd, + boolean replaceRange) { + if (file == null || file.isEmpty()) { + return R.failed("请上传 Excel 文件"); + } + String originalName = file.getOriginalFilename(); + if (StrUtil.isBlank(originalName) || (!originalName.endsWith(".xlsx") && !originalName.endsWith(".xls"))) { + return R.failed("仅支持 .xlsx / .xls"); + } + + long t0 = System.currentTimeMillis(); + String operator = currentUser(); + Long tenantId = TenantContextHolder.getTenantId(); + LocalDateTime now = LocalDateTime.now(); + + CanteenReportImportLogEntity logEntity = new CanteenReportImportLogEntity(); + logEntity.setId(IdWorker.getId()); + logEntity.setBizMonth(StrUtil.trim(bizMonth)); + logEntity.setDateStart(StrUtil.trim(dateStart)); + logEntity.setDateEnd(StrUtil.trim(dateEnd)); + logEntity.setFileName(originalName); + logEntity.setSourceType("FACE"); + logEntity.setStatus("RUNNING"); + logEntity.setAggStatus("NONE"); + logEntity.setAggRows(0); + logEntity.setTotalRows(0); + logEntity.setSuccessRows(0); + logEntity.setSkipRows(0); + logEntity.setOperator(operator); + logEntity.setCreateBy(operator); + logEntity.setUpdateBy(operator); + logEntity.setCreateTime(now); + logEntity.setUpdateTime(now); + logEntity.setDelFlag(CommonConstants.STATUS_NORMAL); + logEntity.setTenantId(tenantId); + save(logEntity); + + File temp = null; + try { + temp = Files.createTempFile("canteen-face-", ".xlsx").toFile(); + file.transferTo(temp); + archiveExcel(temp, originalName, logEntity); + + boolean preReplace = replaceRange && StrUtil.isNotBlank(bizMonth) && StrUtil.isNotBlank(dateStart) + && StrUtil.isNotBlank(dateEnd); + if (preReplace) { + faceDetailMapper.deletePhysicallyByDateRange(bizMonth.trim(), dateStart.trim(), dateEnd.trim()); + } + + FaceDetailAccumulator acc = new FaceDetailAccumulator(logEntity.getId(), bizMonth, operator, tenantId, + now, faceDetailMapper, BATCH_SIZE); + try (InputStream in = new BufferedInputStream(new FileInputStream(temp), 1 << 20)) { + EasyExcel.read(in, CanteenReportFaceExcel.class, acc) + .sheet() + .headRowNumber(1) + .doRead(); + } + acc.flush(); + + String resolvedMonth = StrUtil.blankToDefault(StrUtil.trim(bizMonth), acc.getResolvedBizMonth()); + String resolvedStart = StrUtil.blankToDefault(StrUtil.trim(dateStart), acc.getMinDate()); + String resolvedEnd = StrUtil.blankToDefault(StrUtil.trim(dateEnd), acc.getMaxDate()); + if (StrUtil.isBlank(resolvedMonth) || StrUtil.isBlank(resolvedStart) || StrUtil.isBlank(resolvedEnd)) { + faceDetailMapper.deletePhysicallyByImportLogId(logEntity.getId()); + failLog(logEntity, "无法确定业务月或日期段,请填写业务月与数据起止日期", t0); + return R.failed(logEntity.getErrorMsg()); + } + + if (replaceRange && !preReplace) { + faceDetailMapper.deletePhysicallyDateRangeExceptLog(resolvedMonth, resolvedStart, resolvedEnd, + logEntity.getId()); + } + + logEntity.setBizMonth(resolvedMonth); + logEntity.setDateStart(resolvedStart); + logEntity.setDateEnd(resolvedEnd); + logEntity.setTotalRows(acc.getTotalRows()); + logEntity.setSuccessRows(acc.getSuccessRows()); + logEntity.setSkipRows(acc.getSkipRows()); + logEntity.setStatus("SUCCESS"); + logEntity.setAggStatus("NONE"); + logEntity.setCostMs(System.currentTimeMillis() - t0); + logEntity.setUpdateTime(LocalDateTime.now()); + updateById(logEntity); + + return R.ok(logEntity, String.format( + "明细导入成功:入库 %d 条,跳过 %d 条,日期 %s~%s,耗时 %d 秒。请点击「统计汇总到刷卡明细」生成报表数据。", + acc.getSuccessRows(), acc.getSkipRows(), resolvedStart, resolvedEnd, + (logEntity.getCostMs() + 999) / 1000)); + } + catch (Exception e) { + log.error("人脸明细导入失败 logId={}", logEntity.getId(), e); + try { + faceDetailMapper.deletePhysicallyByImportLogId(logEntity.getId()); + } + catch (Exception ignored) { + // ignore + } + failLog(logEntity, StrUtil.maxLength(StrUtil.blankToDefault(e.getMessage(), e.getClass().getSimpleName()), 900), + t0); + return R.failed("导入失败: " + e.getMessage()); + } + finally { + if (temp != null && temp.exists()) { + FileUtil.del(temp); + } + } + } + + @Override + public R aggregateToSwipe(String bizMonth, String dateStart, String dateEnd, Long importLogId, + boolean replaceFace) { + if (StrUtil.isBlank(bizMonth) && importLogId == null) { + return R.failed("请传入业务月份 bizMonth,或指定某一批导入日志"); + } + long t0 = System.currentTimeMillis(); + String operator = currentUser(); + Long tenantId = TenantContextHolder.getTenantId(); + LocalDateTime now = LocalDateTime.now(); + + String month = StrUtil.trim(bizMonth); + // 指定导入批次时:只用 importLogId,日期段不参与过滤(避免日志日期与明细 biz_date 不一致) + String useDateStart = importLogId != null ? null : StrUtil.trim(dateStart); + String useDateEnd = importLogId != null ? null : StrUtil.trim(dateEnd); + + if (importLogId != null) { + CanteenReportImportLogEntity logEntity = getById(importLogId); + if (logEntity == null) { + return R.failed("导入日志不存在,可能是页面 ID 精度丢失,请刷新后重试,或使用上方「统计汇总到刷卡明细」按月汇总"); + } + if (StrUtil.isBlank(month)) { + month = logEntity.getBizMonth(); + } + long cnt = faceDetailMapper.countDetail(null, importLogId); + if (cnt <= 0) { + return R.failed(String.format( + "该批导入日志下没有明细(importLogId=%s)。请打开「明细」确认能否查到数据;若明细也是空的,需要重新导入 Excel", + importLogId)); + } + } + else { + long cnt = faceDetailMapper.countDetail(month, null); + if (cnt <= 0) { + long any = faceDetailMapper.countDetail(null, null); + if (any <= 0) { + return R.failed("库中没有任何人脸明细,请先成功导入 Excel(导入日志里「明细入库」应大于 0)"); + } + return R.failed(String.format( + "归属月 %s 下没有明细(库中其它月份共有 %d 条)。请核对导入日志的「业务月」,或改选正确归属月再汇总", + month, any)); + } + } + + List aggRows = faceDetailMapper.selectAggByMonth(month, useDateStart, useDateEnd, + importLogId); + if (CollUtil.isEmpty(aggRows)) { + return R.failed("明细存在但汇总结果为空,请检查订单金额是否都为 0"); + } + + if (StrUtil.isBlank(month) && importLogId != null) { + CanteenReportImportLogEntity logEntity = getById(importLogId); + if (logEntity != null) { + month = logEntity.getBizMonth(); + } + } + if (StrUtil.isBlank(month)) { + return R.failed("无法确定报表归属月"); + } + + Map settleParkMap = loadSettleParkMap(); + List saveList = new ArrayList<>(aggRows.size()); + int unmatchedPark = 0; + for (CanteenReportFaceAggRow row : aggRows) { + if (row.getAmount() == null || row.getAmount().compareTo(BigDecimal.ZERO) == 0) { + continue; + } + CanteenReportSwipeEntity e = new CanteenReportSwipeEntity(); + e.setId(IdWorker.getId()); + e.setBizMonth(month); + e.setBizDate(null); + e.setEmpNo(emptyToNull(row.getEmpNo())); + e.setEmpName(emptyToNull(row.getEmpName())); + e.setLegalEntity(emptyToNull(row.getLegalEntity())); + e.setDeptName(emptyToNull(row.getDeptName())); + e.setConsumeArea(emptyToNull(row.getOrgLevel3())); + e.setSettleCounter(emptyToNull(row.getDeviceName())); + e.setConsumeAmount(row.getAmount()); + e.setIncomeType(row.getIncomeType()); + e.setCostCenter(CanteenReportFaceMapper.mapCostCenter(row.getLegalEntity())); + String park = settleParkMap.get(StrUtil.trim(row.getDeviceName())); + if (StrUtil.isBlank(park) && StrUtil.isNotBlank(row.getDeviceName())) { + unmatchedPark++; + } + e.setPark(park); + e.setImportLogId(importLogId); + e.setSource("FACE"); + e.setCreateBy(operator); + e.setUpdateBy(operator); + e.setCreateTime(now); + e.setUpdateTime(now); + e.setDelFlag(CommonConstants.STATUS_NORMAL); + e.setTenantId(tenantId); + saveList.add(e); + } + + if (CollUtil.isEmpty(saveList)) { + return R.failed("汇总后可写入金额均为 0,未写入刷卡明细"); + } + + if (replaceFace) { + if (importLogId != null) { + swipeMapper.deletePhysicallyByImportLogId(importLogId); + } + else { + swipeMapper.deletePhysicallyFaceByBizMonth(month); + } + } + + for (List chunk : CollUtil.split(saveList, BATCH_SIZE)) { + swipeMapper.insertBatch(chunk); + } + + if (importLogId != null) { + CanteenReportImportLogEntity logEntity = getById(importLogId); + if (logEntity != null) { + logEntity.setAggRows(saveList.size()); + logEntity.setAggStatus("DONE"); + logEntity.setAggTime(now); + logEntity.setUpdateTime(now); + updateById(logEntity); + } + } + else { + update(Wrappers.lambdaUpdate() + .set(CanteenReportImportLogEntity::getAggStatus, "DONE") + .set(CanteenReportImportLogEntity::getAggTime, now) + .set(CanteenReportImportLogEntity::getAggRows, saveList.size()) + .eq(CanteenReportImportLogEntity::getBizMonth, month) + .eq(CanteenReportImportLogEntity::getStatus, "SUCCESS")); + } + + long cost = System.currentTimeMillis() - t0; + String tip = unmatchedPark > 0 + ? String.format(";有 %d 条结算台未配置厂区,请到「结算台厂区」维护后再重新汇总", unmatchedPark) + : ""; + return R.ok(null, String.format("汇总成功:写入刷卡明细 %d 条,耗时 %d 秒%s", saveList.size(), (cost + 999) / 1000, tip)); + } + + private Map loadSettleParkMap() { + List list = settleParkMapper.selectList( + Wrappers.lambdaQuery()); + Map map = new HashMap<>(); + for (CanteenReportSettleParkEntity e : list) { + if (StrUtil.isNotBlank(e.getSettleCounter()) && StrUtil.isNotBlank(e.getPark())) { + map.put(e.getSettleCounter().trim(), e.getPark().trim()); + } + } + return map; + } + + private static String emptyToNull(String s) { + return StrUtil.isBlank(s) ? null : s.trim(); + } + + private void archiveExcel(File temp, String originalName, CanteenReportImportLogEntity logEntity) { + try (InputStream in = new FileInputStream(temp)) { + MultipartFile mf = new MockMultipartFile("file", originalName, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", in); + R upload = remoteFileService.innerUpload(mf, "canteen-report-face", null, "canteen-report-face"); + if (upload != null && upload.getData() instanceof Map) { + Map data = (Map) upload.getData(); + Object url = data.get("url"); + Object fn = data.get("fileName"); + if (url != null) { + logEntity.setFileUrl(String.valueOf(url)); + } + if (fn != null) { + logEntity.setOssFileName(String.valueOf(fn)); + } + updateById(logEntity); + } + } + catch (Exception e) { + log.warn("人脸 Excel 存档失败,继续导入: {}", e.getMessage()); + } + } + + private void failLog(CanteenReportImportLogEntity logEntity, String msg, long t0) { + logEntity.setStatus("FAILED"); + logEntity.setErrorMsg(msg); + logEntity.setCostMs(System.currentTimeMillis() - t0); + logEntity.setUpdateTime(LocalDateTime.now()); + updateById(logEntity); + } + + private static String currentUser() { + try { + if (SecurityUtils.getUser() != null) { + return SecurityUtils.getUser().getUsername(); + } + } + catch (Exception ignored) { + // ignore + } + return null; + } + + static final class FaceDetailAccumulator implements ReadListener { + + private final Long importLogId; + private final String bizMonthParam; + private final String operator; + private final Long tenantId; + private final LocalDateTime now; + private final CanteenReportFaceDetailMapper faceDetailMapper; + private final int batchSize; + + private final List buffer = new ArrayList<>(); + private int totalRows; + private int successRows; + private int skipRows; + private String minDate; + private String maxDate; + private String resolvedBizMonth; + + FaceDetailAccumulator(Long importLogId, String bizMonthParam, String operator, Long tenantId, + LocalDateTime now, CanteenReportFaceDetailMapper faceDetailMapper, int batchSize) { + this.importLogId = importLogId; + this.bizMonthParam = bizMonthParam; + this.operator = operator; + this.tenantId = tenantId; + this.now = now; + this.faceDetailMapper = faceDetailMapper; + this.batchSize = batchSize; + } + + @Override + public void invoke(CanteenReportFaceExcel data, AnalysisContext context) { + totalRows++; + CanteenReportFaceDetailEntity e = CanteenReportFaceMapper.toDetail(data, bizMonthParam, importLogId); + if (e == null || StrUtil.isBlank(e.getBizDate()) || StrUtil.isBlank(e.getBizMonth())) { + skipRows++; + return; + } + trackDate(e.getBizDate()); + if (resolvedBizMonth == null) { + resolvedBizMonth = e.getBizMonth(); + } + e.setId(IdWorker.getId()); + e.setCreateBy(operator); + e.setUpdateBy(operator); + e.setCreateTime(now); + e.setUpdateTime(now); + e.setDelFlag(CommonConstants.STATUS_NORMAL); + e.setTenantId(tenantId); + buffer.add(e); + if (buffer.size() >= batchSize) { + flush(); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + flush(); + } + + void flush() { + if (CollUtil.isEmpty(buffer)) { + return; + } + faceDetailMapper.insertBatch(buffer); + successRows += buffer.size(); + buffer.clear(); + } + + private void trackDate(String d) { + if (StrUtil.isBlank(d)) { + return; + } + if (minDate == null || d.compareTo(minDate) < 0) { + minDate = d; + } + if (maxDate == null || d.compareTo(maxDate) > 0) { + maxDate = d; + } + } + + int getTotalRows() { + return totalRows; + } + + int getSuccessRows() { + return successRows; + } + + int getSkipRows() { + return skipRows; + } + + String getMinDate() { + return minDate; + } + + String getMaxDate() { + return maxDate; + } + + String getResolvedBizMonth() { + return resolvedBizMonth; + } + } +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportSettleParkServiceImpl.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportSettleParkServiceImpl.java new file mode 100644 index 000000000..23369507b --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportSettleParkServiceImpl.java @@ -0,0 +1,47 @@ +package com.bsd.pjcloud.canteen.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.bsd.pjcloud.canteen.entity.CanteenReportSettleParkEntity; +import com.bsd.pjcloud.canteen.mapper.CanteenReportSettleParkMapper; +import com.bsd.pjcloud.canteen.report.CanteenReportConstants; +import com.bsd.pjcloud.canteen.service.CanteenReportSettleParkService; +import com.bsd.pjcloud.common.core.util.R; +import org.springframework.stereotype.Service; + +@Service +public class CanteenReportSettleParkServiceImpl extends ServiceImpl + implements CanteenReportSettleParkService { + + @Override + public Page pageList(Page page, String settleCounter, String park) { + return page(page, Wrappers.lambdaQuery() + .like(StrUtil.isNotBlank(settleCounter), CanteenReportSettleParkEntity::getSettleCounter, settleCounter) + .eq(StrUtil.isNotBlank(park), CanteenReportSettleParkEntity::getPark, park) + .orderByAsc(CanteenReportSettleParkEntity::getPark) + .orderByAsc(CanteenReportSettleParkEntity::getSettleCounter)); + } + + @Override + public R saveOrUpdateRow(CanteenReportSettleParkEntity entity) { + if (entity == null || StrUtil.isBlank(entity.getSettleCounter()) || StrUtil.isBlank(entity.getPark())) { + return R.failed("结算台与厂区不能为空"); + } + entity.setSettleCounter(entity.getSettleCounter().trim()); + entity.setPark(entity.getPark().trim()); + if (!CanteenReportConstants.PARKS.contains(entity.getPark()) + && !CanteenReportConstants.SHARE_PARKS.contains(entity.getPark())) { + return R.failed("厂区不在枚举内: " + entity.getPark()); + } + long cnt = count(Wrappers.lambdaQuery() + .eq(CanteenReportSettleParkEntity::getSettleCounter, entity.getSettleCounter()) + .ne(entity.getId() != null, CanteenReportSettleParkEntity::getId, entity.getId())); + if (cnt > 0) { + return R.failed("该结算台已配置厂区"); + } + saveOrUpdate(entity); + return R.ok(entity); + } +} diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportSwipeServiceImpl.java b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportSwipeServiceImpl.java index dc1373ed8..8734895c0 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportSwipeServiceImpl.java +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/java/com/bsd/pjcloud/canteen/service/impl/CanteenReportSwipeServiceImpl.java @@ -39,13 +39,14 @@ public class CanteenReportSwipeServiceImpl extends ServiceImpl pageList(Page page, String bizMonth, String park, String incomeType, - String costCenter, String empNo) { + String costCenter, String empNo, Long importLogId) { return page(page, Wrappers.lambdaQuery() .eq(StrUtil.isNotBlank(bizMonth), CanteenReportSwipeEntity::getBizMonth, bizMonth) .eq(StrUtil.isNotBlank(park), CanteenReportSwipeEntity::getPark, park) .eq(StrUtil.isNotBlank(incomeType), CanteenReportSwipeEntity::getIncomeType, incomeType) .like(StrUtil.isNotBlank(costCenter), CanteenReportSwipeEntity::getCostCenter, costCenter) .like(StrUtil.isNotBlank(empNo), CanteenReportSwipeEntity::getEmpNo, empNo) + .eq(importLogId != null, CanteenReportSwipeEntity::getImportLogId, importLogId) .orderByDesc(CanteenReportSwipeEntity::getId)); } diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenReportFaceDetailMapper.xml b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenReportFaceDetailMapper.xml new file mode 100644 index 000000000..3b9414f6d --- /dev/null +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenReportFaceDetailMapper.xml @@ -0,0 +1,118 @@ + + + + + + INSERT INTO canteen_report_face_detail ( + id, import_log_id, biz_month, biz_date, + order_no, third_order_no, order_create_time, pay_time, deduct_time, + org_level1, org_level2, org_level3, canteen, canteen_area, consume_point, + emp_name, phone, legal_entity, emp_no, card_no, group_name, dept_name, + pay_method, pay_type, order_amount, change_amount, + stored_change, subsidy_change, gift_change, + stored_balance, subsidy_balance, gift_balance, + change_org, discount, discount_amount, discount_type, coupon_type, + deduct_amount, fee_amount, meal_subsidy, + consume_type, meal_segment, device_name, device_status, remark, operator_name, visitor_pay_method, + create_by, create_time, update_by, update_time, del_flag, tenant_id + ) VALUES + + ( + #{e.id}, #{e.importLogId}, #{e.bizMonth}, #{e.bizDate}, + #{e.orderNo}, #{e.thirdOrderNo}, #{e.orderCreateTime}, #{e.payTime}, #{e.deductTime}, + #{e.orgLevel1}, #{e.orgLevel2}, #{e.orgLevel3}, #{e.canteen}, #{e.canteenArea}, #{e.consumePoint}, + #{e.empName}, #{e.phone}, #{e.legalEntity}, #{e.empNo}, #{e.cardNo}, #{e.groupName}, #{e.deptName}, + #{e.payMethod}, #{e.payType}, #{e.orderAmount}, #{e.changeAmount}, + #{e.storedChange}, #{e.subsidyChange}, #{e.giftChange}, + #{e.storedBalance}, #{e.subsidyBalance}, #{e.giftBalance}, + #{e.changeOrg}, #{e.discount}, #{e.discountAmount}, #{e.discountType}, #{e.couponType}, + #{e.deductAmount}, #{e.feeAmount}, #{e.mealSubsidy}, + #{e.consumeType}, #{e.mealSegment}, #{e.deviceName}, #{e.deviceStatus}, #{e.remark}, #{e.operatorName}, #{e.visitorPayMethod}, + #{e.createBy}, #{e.createTime}, #{e.updateBy}, #{e.updateTime}, #{e.delFlag}, #{e.tenantId} + ) + + + + + DELETE FROM canteen_report_face_detail WHERE import_log_id = #{importLogId} + + + + DELETE FROM canteen_report_face_detail + WHERE biz_month = #{bizMonth} + AND biz_date >= #{dateStart} + AND biz_date <= #{dateEnd} + + + + DELETE FROM canteen_report_face_detail + WHERE biz_month = #{bizMonth} + AND biz_date >= #{dateStart} + AND biz_date <= #{dateEnd} + AND (import_log_id IS NULL OR import_log_id <> #{importLogId}) + + + + + + diff --git a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenReportSwipeMapper.xml b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenReportSwipeMapper.xml index 081bd251f..f0176a6fc 100644 --- a/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenReportSwipeMapper.xml +++ b/pjcloud-canteen/pjcloud-canteen-biz/src/main/resources/mapper/CanteenReportSwipeMapper.xml @@ -6,18 +6,43 @@ DELETE FROM canteen_report_swipe WHERE biz_month = #{bizMonth} + + DELETE FROM canteen_report_swipe WHERE source = 'FACE' AND biz_month = #{bizMonth} + + + + DELETE FROM canteen_report_swipe + WHERE source = 'FACE' + AND biz_month = #{bizMonth} + AND biz_date >= #{dateStart} + AND biz_date <= #{dateEnd} + + + + DELETE FROM canteen_report_swipe WHERE import_log_id = #{importLogId} + + + + DELETE FROM canteen_report_swipe + WHERE source = 'FACE' + AND biz_month = #{bizMonth} + AND biz_date >= #{dateStart} + AND biz_date <= #{dateEnd} + AND (import_log_id IS NULL OR import_log_id <> #{importLogId}) + + INSERT INTO canteen_report_swipe ( - id, biz_month, emp_no, emp_name, legal_entity, dept_name, + id, biz_month, biz_date, emp_no, emp_name, legal_entity, dept_name, consume_area, settle_counter, consume_amount, income_type, - cost_center, cost_category, park, remark_tag, + cost_center, cost_category, park, remark_tag, import_log_id, source, create_by, create_time, update_by, update_time, del_flag, tenant_id ) VALUES ( - #{e.id}, #{e.bizMonth}, #{e.empNo}, #{e.empName}, #{e.legalEntity}, #{e.deptName}, + #{e.id}, #{e.bizMonth}, #{e.bizDate}, #{e.empNo}, #{e.empName}, #{e.legalEntity}, #{e.deptName}, #{e.consumeArea}, #{e.settleCounter}, #{e.consumeAmount}, #{e.incomeType}, - #{e.costCenter}, #{e.costCategory}, #{e.park}, #{e.remarkTag}, + #{e.costCenter}, #{e.costCategory}, #{e.park}, #{e.remarkTag}, #{e.importLogId}, #{e.source}, #{e.createBy}, #{e.createTime}, #{e.updateBy}, #{e.updateTime}, #{e.delFlag}, #{e.tenantId} ) diff --git a/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/util/PersonNameUtils.java b/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/util/PersonNameUtils.java index 9ae2eae63..80e71b62e 100644 --- a/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/util/PersonNameUtils.java +++ b/pjcloud-upms/pjcloud-upms-api/src/main/java/com/bsd/pjcloud/admin/api/util/PersonNameUtils.java @@ -5,7 +5,7 @@ import cn.hutool.core.util.StrUtil; import java.util.regex.Pattern; /** - * 姓名/昵称:支持中文,或英文(单词间可空格,如 John Smith) + * 姓名/昵称:支持中文、英文、数字(单词间可空格,如 John Smith、张三1) */ public final class PersonNameUtils { @@ -21,8 +21,9 @@ public final class PersonNameUtils { private static final int MAX_LEN = 32; + /** 中文 / 英文 / 数字,单词间允许空格 */ private static final Pattern PERSON_NAME_PATTERN = Pattern.compile( - "^(?:[\\u4E00-\\u9FA5]{1,32}|[A-Za-z]+(?:\\s[A-Za-z]+){0,8}|[\\u4E00-\\u9FA5A-Za-z]+(?:\\s[\\u4E00-\\u9FA5A-Za-z]+){0,8})$"); + "^[\\u4E00-\\u9FA5A-Za-z0-9]+(?:\\s[\\u4E00-\\u9FA5A-Za-z0-9]+){0,8}$"); private PersonNameUtils() { } @@ -72,7 +73,7 @@ public final class PersonNameUtils { if (CODE_NAME_EMPTY.equals(code) || CODE_NICKNAME_EMPTY.equals(code)) { return fieldLabel + "不能为空"; } - return fieldLabel + "仅支持中文,或英文(单词间可用空格,如 John Smith)"; + return fieldLabel + "仅支持中文、英文、数字(单词间可用空格)"; } } diff --git a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserImportExportServiceImpl.java b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserImportExportServiceImpl.java index a5b8b1a47..994a55901 100644 --- a/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserImportExportServiceImpl.java +++ b/pjcloud-upms/pjcloud-upms-biz/src/main/java/com/bsd/pjcloud/admin/service/impl/SysUserImportExportServiceImpl.java @@ -395,7 +395,7 @@ public class SysUserImportExportServiceImpl implements SysUserImportExportServic if (StrUtil.isBlank(excel.getName())) { excel.setName(excel.getPhone() + "学生家长"); } else if (!PersonNameUtils.isValid(excel.getName())) { - errorMessages.add("姓名仅支持中文,或英文(单词间可用空格),且不能超过32个字符"); + errorMessages.add("姓名仅支持中文、英文、数字(单词间可用空格),且不能超过32个字符"); } if (StrUtil.isBlank(excel.getPhone())) { errorMessages.add("手机号不能为空"); @@ -475,7 +475,7 @@ public class SysUserImportExportServiceImpl implements SysUserImportExportServic for (TeacherUserExcelVO excel : teacherUserExcelList) { Set errorMessages = new LinkedHashSet<>(); if (!PersonNameUtils.isValid(excel.getName())) { - errorMessages.add("姓名仅支持中文,或英文(单词间可用空格),且不能超过32个字符"); + errorMessages.add("姓名仅支持中文、英文、数字(单词间可用空格),且不能超过32个字符"); } if (StrUtil.isBlank(excel.getTeacherNo())) { errorMessages.add("学号不能为空");