演示报表

This commit is contained in:
zjf committed 2026-08-13 15:34:51 +08:00
1 parent 2d68849658
commit 37923a00ac
32 files changed
+2180 -1

No files matched your search

@@ -0,0 +1,34 @@
package com.bsd.pjcloud.canteen.dto.report;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class CanteenReportBusinessExportExcel {
@ExcelProperty("类型")
private String type;
@ExcelProperty("类别")
private String category;
@ExcelProperty("敏惠园区")
private BigDecimal parkMinhui;
@ExcelProperty("敏实园区")
private BigDecimal parkMinshi;
@ExcelProperty("敏胜园区")
private BigDecimal parkMinsheng;
@ExcelProperty("敏华园区")
private BigDecimal parkMinhua;
@ExcelProperty("湖州园区")
private BigDecimal parkHuzhou;
@ExcelProperty("华智一期")
private BigDecimal parkHuazhi1;
@ExcelProperty("华智二期")
private BigDecimal parkHuazhi2;
@ExcelProperty("安吉园区")
private BigDecimal parkAnji;
@ExcelProperty("小计")
private BigDecimal subtotal;
@ExcelProperty("备注")
private String remark;
}
@@ -0,0 +1,70 @@
package com.bsd.pjcloud.canteen.dto.report;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
* 食材能耗导入:按列序对齐原表「食材能耗」sheet(含两个「差异」列)
*/
@Data
public class CanteenReportFoodEnergyExcel {
@ExcelProperty(index = 0, value = "时间")
private String bizDate;
@ExcelProperty(index = 1, value = "供应商")
private String supplier;
@ExcelProperty(index = 2, value = "收货单号")
private String receiptNo;
@ExcelProperty(index = 3, value = "类别")
private String category;
@ExcelProperty(index = 4, value = "商品编码")
private String productCode;
@ExcelProperty(index = 5, value = "品名")
private String productName;
@ExcelProperty(index = 6, value = "规格")
private String spec;
@ExcelProperty(index = 7, value = "单位")
private String unit;
@ExcelProperty(index = 8, value = "数量")
private BigDecimal qty;
@ExcelProperty(index = 9, value = "单价")
private BigDecimal unitPrice;
@ExcelProperty(index = 10, value = "定价")
private BigDecimal listPrice;
@ExcelProperty(index = 11, value = "差异")
private BigDecimal priceDiff;
@ExcelProperty(index = 12, value = "结算单价")
private BigDecimal settleUnitPrice;
@ExcelProperty(index = 13, value = "结算总金额")
private BigDecimal settleAmount;
@ExcelProperty(index = 14, value = "金额")
private BigDecimal amount;
@ExcelProperty(index = 15, value = "金额差异")
private BigDecimal amountDiff;
@ExcelProperty(index = 16, value = "食堂仓库")
private String warehouse;
@ExcelProperty(index = 17, value = "厂区")
private String park;
@ExcelProperty(index = 18, value = "分类")
private String classify;
}
@@ -0,0 +1,32 @@
package com.bsd.pjcloud.canteen.dto.report;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class CanteenReportIncomeExportExcel {
@ExcelProperty("收入类别")
private String groupName;
@ExcelProperty("成本中心")
private String costCenter;
@ExcelProperty("敏惠园区")
private BigDecimal parkMinhui;
@ExcelProperty("敏实园区")
private BigDecimal parkMinshi;
@ExcelProperty("敏胜园区")
private BigDecimal parkMinsheng;
@ExcelProperty("敏华园区")
private BigDecimal parkMinhua;
@ExcelProperty("湖州园区")
private BigDecimal parkHuzhou;
@ExcelProperty("华智一期")
private BigDecimal parkHuazhi1;
@ExcelProperty("华智二期")
private BigDecimal parkHuazhi2;
@ExcelProperty("安吉园区")
private BigDecimal parkAnji;
@ExcelProperty("小计")
private BigDecimal subtotal;
}
@@ -0,0 +1,28 @@
package com.bsd.pjcloud.canteen.dto.report;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class CanteenReportManualAdjustExcel {
@ExcelProperty("业务月份")
private String bizMonth;
@ExcelProperty("调整类型")
private String adjustType;
@ExcelProperty("成本中心")
private String costCenter;
@ExcelProperty("厂区")
private String park;
@ExcelProperty("调整金额")
private BigDecimal amount;
@ExcelProperty("备注")
private String remark;
}
@@ -0,0 +1,34 @@
package com.bsd.pjcloud.canteen.dto.report;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class CanteenReportShareExportExcel {
@ExcelProperty("成本中心")
private String costCenter;
@ExcelProperty("敏惠园区")
private BigDecimal parkMinhui;
@ExcelProperty("敏实园区")
private BigDecimal parkMinshi;
@ExcelProperty("敏胜园区")
private BigDecimal parkMinsheng;
@ExcelProperty("敏华园区")
private BigDecimal parkMinhua;
@ExcelProperty("湖州园区")
private BigDecimal parkHuzhou;
@ExcelProperty("安吉园区")
private BigDecimal parkAnji;
@ExcelProperty("华智一期")
private BigDecimal parkHuazhi1;
@ExcelProperty("小计")
private BigDecimal subtotal;
@ExcelProperty("包厢及其他收入")
private BigDecimal boxAndOther;
@ExcelProperty("其他需调整金额")
private BigDecimal otherAdjust;
@ExcelProperty("开票金额")
private BigDecimal invoiceAmount;
}
@@ -0,0 +1,49 @@
package com.bsd.pjcloud.canteen.dto.report;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
* 刷卡明细导入:按列序对齐原表「刷卡明细」sheet,兼容整本工作簿。
*/
@Data
public class CanteenReportSwipeExcel {
@ExcelProperty(index = 0, value = "工号")
private String empNo;
@ExcelProperty(index = 1, value = "姓名")
private String empName;
@ExcelProperty(index = 2, value = "法律归属")
private String legalEntity;
@ExcelProperty(index = 3, value = "部门")
private String deptName;
@ExcelProperty(index = 4, value = "消费区域")
private String consumeArea;
@ExcelProperty(index = 5, value = "结算台")
private String settleCounter;
@ExcelProperty(index = 6, value = "消费金额")
private BigDecimal consumeAmount;
@ExcelProperty(index = 7, value = "收入类别")
private String incomeType;
@ExcelProperty(index = 8, value = "核对成本(开票公司)")
private String costCenter;
@ExcelProperty(index = 9, value = "成本分类")
private String costCategory;
@ExcelProperty(index = 10, value = "厂区")
private String park;
@ExcelProperty(index = 11, value = "备注标识")
private String remarkTag;
}
@@ -0,0 +1,111 @@
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;
@Data
@TableName("canteen_report_food_energy")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "食堂报表-食材能耗明细(演示)")
public class CanteenReportFoodEnergyEntity extends Model<CanteenReportFoodEnergyEntity> {
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private Long id;
@Schema(description = "业务月份 yyyy-MM")
private String bizMonth;
@Schema(description = "业务日期(原文)")
private String bizDate;
@Schema(description = "供应商")
private String supplier;
@Schema(description = "收货单号")
private String receiptNo;
@Schema(description = "类别")
private String category;
@Schema(description = "商品编码")
private String productCode;
@Schema(description = "品名")
private String productName;
@Schema(description = "规格")
private String spec;
@Schema(description = "单位")
private String unit;
@Schema(description = "数量")
private BigDecimal qty;
@Schema(description = "单价")
private BigDecimal unitPrice;
@Schema(description = "定价")
private BigDecimal listPrice;
@Schema(description = "差异(单价)")
private BigDecimal priceDiff;
@Schema(description = "结算单价")
private BigDecimal settleUnitPrice;
@Schema(description = "结算总金额(元)")
private BigDecimal settleAmount;
@Schema(description = "金额")
private BigDecimal amount;
@Schema(description = "差异(金额)")
private BigDecimal amountDiff;
@Schema(description = "食堂仓库")
private String warehouse;
@Schema(description = "厂区")
private String park;
@Schema(description = "分类")
private String classify;
@Schema(description = "能耗金额")
private BigDecimal energyAmount;
@Schema(description = "人工")
private BigDecimal laborAmount;
@Schema(description = "耗材")
private BigDecimal consumableAmount;
@Schema(description = "外协")
private BigDecimal outsourceAmount;
@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;
}
@@ -0,0 +1,57 @@
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;
@Data
@TableName("canteen_report_manual_adjust")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "食堂报表-手工调整(演示)")
public class CanteenReportManualAdjustEntity extends Model<CanteenReportManualAdjustEntity> {
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private Long id;
@Schema(description = "业务月份 yyyy-MM")
private String bizMonth;
@Schema(description = "调整类型")
private String adjustType;
@Schema(description = "成本中心")
private String costCenter;
@Schema(description = "厂区")
private String park;
@Schema(description = "调整金额(元)")
private BigDecimal amount;
@Schema(description = "备注")
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;
}
@@ -0,0 +1,84 @@
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;
@Data
@TableName("canteen_report_swipe")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "食堂报表-刷卡消费明细(演示)")
public class CanteenReportSwipeEntity extends Model<CanteenReportSwipeEntity> {
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private Long id;
@Schema(description = "业务月份 yyyy-MM")
private String bizMonth;
@Schema(description = "工号")
private String empNo;
@Schema(description = "姓名")
private String empName;
@Schema(description = "法律归属")
private String legalEntity;
@Schema(description = "部门")
private String deptName;
@Schema(description = "消费区域")
private String consumeArea;
@Schema(description = "结算台")
private String settleCounter;
@Schema(description = "消费金额(元)")
private BigDecimal consumeAmount;
@Schema(description = "收入类别")
private String incomeType;
@Schema(description = "核对成本/开票公司")
private String costCenter;
@Schema(description = "成本分类")
private String costCategory;
@Schema(description = "厂区")
private String park;
@Schema(description = "备注标识")
private String remarkTag;
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建时间")
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "更新人")
private String updateBy;
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "更新时间")
private LocalDateTime updateTime;
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "删除标记")
private String delFlag;
@Schema(description = "租户号")
private Long tenantId;
}
@@ -0,0 +1,94 @@
package com.bsd.pjcloud.canteen.report;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 食堂报表演示常量(厂区 / 收入类别 / 成本中心)
*/
public final class CanteenReportConstants {
private CanteenReportConstants() {
}
/** 经营报表厂区列(含华智二期) */
public static final List<String> PARKS = Collections.unmodifiableList(Arrays.asList(
"敏惠园区", "敏实园区", "敏胜园区", "敏华园区", "湖州园区", "华智一期", "华智二期", "安吉园区"
));
/** 供应商分摊常用厂区列(与样表一致,无华智二期) */
public static final List<String> SHARE_PARKS = Collections.unmodifiableList(Arrays.asList(
"敏惠园区", "敏实园区", "敏胜园区", "敏华园区", "湖州园区", "安吉园区", "华智一期"
));
public static final String INCOME_SWIPE = "员工刷卡";
public static final String INCOME_ALIPAY = "支付宝";
public static final String INCOME_BAD_DEBT = "无法收回";
public static final String INCOME_PROFIT_ADJ = "利润调整";
public static final String INCOME_INVOICE = "开票结算";
public static final String INCOME_OTHER = "其他收入";
public static final String INCOME_BOX = "包厢收入";
public static final List<String> INCOME_TYPES = Collections.unmodifiableList(Arrays.asList(
INCOME_SWIPE, INCOME_ALIPAY, INCOME_BOX, INCOME_OTHER, INCOME_PROFIT_ADJ, INCOME_INVOICE, INCOME_BAD_DEBT
));
/** 非员工刷卡的其他收入类(用于收入合计/经营报表) */
public static final List<String> OTHER_INCOME_TYPES = Collections.unmodifiableList(Arrays.asList(
INCOME_ALIPAY, INCOME_BAD_DEBT, INCOME_PROFIT_ADJ, INCOME_INVOICE, INCOME_OTHER, INCOME_BOX
));
/**
* 分摊表「包厢及其他收入」仅含这两类(与样表 J 列一致);
* 可分摊金额 = 厂区食材池 − 厂区包厢 − 厂区其他收入。
*/
public static final List<String> SHARE_BOX_OTHER_TYPES = Collections.unmodifiableList(Arrays.asList(
INCOME_BOX, INCOME_OTHER
));
/** 计入厂区食材池的分类(与样表 SUMIFS 分类列一致) */
public static final List<String> SHARE_FOOD_CLASSIFY = Collections.unmodifiableList(Arrays.asList(
"食材", "能耗", "外协"
));
public static final List<String> COST_CENTERS = Collections.unmodifiableList(Arrays.asList(
"嘉兴敏华",
"嘉兴敏惠-铝件",
"嘉兴敏惠-塑件",
"嘉兴敏惠-塑件BU职能",
"嘉兴敏惠-塑件技术中心",
"嘉兴敏惠-塑件项目中心",
"嘉兴敏惠-研究院",
"嘉兴敏惠-地区共享",
"嘉兴敏胜",
"嘉兴敏实",
"嘉兴敏实-生产三部",
"嘉兴敏信",
"嘉兴敏云",
"嘉兴司诺-公共",
"嘉兴司诺-集团采购中心",
"嘉兴司诺-集团客户发展中心",
"嘉兴司诺-全球物流中心",
"展图中国-公共",
"嘉兴信鼎",
"嘉兴信元-塑件开发中心",
"嘉兴信元-塑件模具厂",
"宁波信泰-研究院",
"思行智能机器人(浙江)有限公司",
"浙江敏智",
"浙江敏能",
"浙江敏盛",
"嘉兴敏能",
"宁波敏实汽车零部件技术研发有限公司",
"嘉兴淳敏",
"嘉兴丰实",
"嘉兴领鹿",
"嘉兴滋荣",
"敏实海拉",
"浙江精诺",
"嘉兴敏强",
"嘉兴精确",
"湖州精确"
));
}
@@ -0,0 +1,28 @@
package com.bsd.pjcloud.canteen.vo.report;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
@Data
@Schema(description = "经营报表-行")
public class CanteenReportBusinessRowVO {
@Schema(description = "类型:收入/支出/经营分析/费用预估")
private String type;
@Schema(description = "类别")
private String category;
@Schema(description = "各厂区金额(万元)")
private Map<String, BigDecimal> parkAmounts = new LinkedHashMap<>();
@Schema(description = "小计(万元)")
private BigDecimal subtotal = BigDecimal.ZERO;
@Schema(description = "备注")
private String remark;
}
@@ -0,0 +1,25 @@
package com.bsd.pjcloud.canteen.vo.report;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
@Data
@Schema(description = "收入合计表-行")
public class CanteenReportIncomeRowVO {
@Schema(description = "收入大类/分组,如员工刷卡")
private String groupName;
@Schema(description = "成本中心")
private String costCenter;
@Schema(description = "各厂区金额(元)")
private Map<String, BigDecimal> parkAmounts = new LinkedHashMap<>();
@Schema(description = "小计(元)")
private BigDecimal subtotal = BigDecimal.ZERO;
}
@@ -0,0 +1,31 @@
package com.bsd.pjcloud.canteen.vo.report;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
@Data
@Schema(description = "供应商分摊表-行")
public class CanteenReportShareRowVO {
@Schema(description = "成本中心")
private String costCenter;
@Schema(description = "各厂区分摊金额(元)")
private Map<String, BigDecimal> parkAmounts = new LinkedHashMap<>();
@Schema(description = "分摊小计")
private BigDecimal subtotal = BigDecimal.ZERO;
@Schema(description = "包厢及其他收入")
private BigDecimal boxAndOther = BigDecimal.ZERO;
@Schema(description = "其他需调整金额")
private BigDecimal otherAdjust = BigDecimal.ZERO;
@Schema(description = "开票金额(分摊小计+包厢及其他+调整)")
private BigDecimal invoiceAmount = BigDecimal.ZERO;
}
@@ -0,0 +1,133 @@
package com.bsd.pjcloud.canteen.controller;
import com.bsd.pjcloud.canteen.dto.report.CanteenReportBusinessExportExcel;
import com.bsd.pjcloud.canteen.dto.report.CanteenReportIncomeExportExcel;
import com.bsd.pjcloud.canteen.dto.report.CanteenReportShareExportExcel;
import com.bsd.pjcloud.canteen.service.CanteenReportCalcService;
import com.bsd.pjcloud.canteen.vo.report.CanteenReportBusinessRowVO;
import com.bsd.pjcloud.canteen.vo.report.CanteenReportIncomeRowVO;
import com.bsd.pjcloud.canteen.vo.report.CanteenReportShareRowVO;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
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.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@RestController
@RequiredArgsConstructor
@RequestMapping("/canteenReport")
@Tag(description = "canteenReport", name = "食堂报表计算(演示)")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class CanteenReportController {
private final CanteenReportCalcService calcService;
@Operation(summary = "汇总校验")
@GetMapping("/summary")
public R summary(@RequestParam String bizMonth) {
return R.ok(calcService.summary(bizMonth));
}
@Operation(summary = "收入合计表")
@GetMapping("/income")
public R income(@RequestParam String bizMonth) {
return R.ok(calcService.incomeReport(bizMonth));
}
@Operation(summary = "供应商分摊表")
@GetMapping("/supplierShare")
public R supplierShare(@RequestParam String bizMonth) {
return R.ok(calcService.supplierShareReport(bizMonth));
}
@Operation(summary = "经营报表")
@GetMapping("/business")
public R business(@RequestParam String bizMonth) {
return R.ok(calcService.businessReport(bizMonth));
}
@ResponseExcel
@Operation(summary = "导出收入合计")
@GetMapping("/export/income")
public List<CanteenReportIncomeExportExcel> exportIncome(@RequestParam String bizMonth) {
List<CanteenReportIncomeExportExcel> list = new ArrayList<>();
for (CanteenReportIncomeRowVO row : calcService.incomeReport(bizMonth)) {
CanteenReportIncomeExportExcel x = new CanteenReportIncomeExportExcel();
x.setGroupName(row.getGroupName());
x.setCostCenter(row.getCostCenter());
Map<String, BigDecimal> p = row.getParkAmounts();
x.setParkMinhui(p.get("敏惠园区"));
x.setParkMinshi(p.get("敏实园区"));
x.setParkMinsheng(p.get("敏胜园区"));
x.setParkMinhua(p.get("敏华园区"));
x.setParkHuzhou(p.get("湖州园区"));
x.setParkHuazhi1(p.get("华智一期"));
x.setParkHuazhi2(p.get("华智二期"));
x.setParkAnji(p.get("安吉园区"));
x.setSubtotal(row.getSubtotal());
list.add(x);
}
return list;
}
@ResponseExcel
@Operation(summary = "导出供应商分摊")
@GetMapping("/export/supplierShare")
public List<CanteenReportShareExportExcel> exportShare(@RequestParam String bizMonth) {
List<CanteenReportShareExportExcel> list = new ArrayList<>();
for (CanteenReportShareRowVO row : calcService.supplierShareReport(bizMonth)) {
CanteenReportShareExportExcel x = new CanteenReportShareExportExcel();
x.setCostCenter(row.getCostCenter());
Map<String, BigDecimal> p = row.getParkAmounts();
x.setParkMinhui(p.get("敏惠园区"));
x.setParkMinshi(p.get("敏实园区"));
x.setParkMinsheng(p.get("敏胜园区"));
x.setParkMinhua(p.get("敏华园区"));
x.setParkHuzhou(p.get("湖州园区"));
x.setParkAnji(p.get("安吉园区"));
x.setParkHuazhi1(p.get("华智一期"));
x.setSubtotal(row.getSubtotal());
x.setBoxAndOther(row.getBoxAndOther());
x.setOtherAdjust(row.getOtherAdjust());
x.setInvoiceAmount(row.getInvoiceAmount());
list.add(x);
}
return list;
}
@ResponseExcel
@Operation(summary = "导出经营报表")
@GetMapping("/export/business")
public List<CanteenReportBusinessExportExcel> exportBusiness(@RequestParam String bizMonth) {
List<CanteenReportBusinessExportExcel> list = new ArrayList<>();
for (CanteenReportBusinessRowVO row : calcService.businessReport(bizMonth)) {
CanteenReportBusinessExportExcel x = new CanteenReportBusinessExportExcel();
x.setType(row.getType());
x.setCategory(row.getCategory());
Map<String, BigDecimal> p = row.getParkAmounts();
x.setParkMinhui(p.get("敏惠园区"));
x.setParkMinshi(p.get("敏实园区"));
x.setParkMinsheng(p.get("敏胜园区"));
x.setParkMinhua(p.get("敏华园区"));
x.setParkHuzhou(p.get("湖州园区"));
x.setParkHuazhi1(p.get("华智一期"));
x.setParkHuazhi2(p.get("华智二期"));
x.setParkAnji(p.get("安吉园区"));
x.setSubtotal(row.getSubtotal());
x.setRemark(row.getRemark());
list.add(x);
}
return list;
}
}
@@ -0,0 +1,74 @@
package com.bsd.pjcloud.canteen.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.canteen.dto.report.CanteenReportFoodEnergyExcel;
import com.bsd.pjcloud.canteen.entity.CanteenReportFoodEnergyEntity;
import com.bsd.pjcloud.canteen.service.CanteenReportFoodEnergyService;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
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/foodEnergy")
@Tag(description = "canteenReportFoodEnergy", name = "食堂报表-食材能耗(演示)")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class CanteenReportFoodEnergyController {
private final CanteenReportFoodEnergyService foodEnergyService;
@Operation(summary = "分页查询")
@GetMapping("/page")
public R page(@ParameterObject Page page,
@RequestParam(required = false) String bizMonth,
@RequestParam(required = false) String park,
@RequestParam(required = false) String supplier) {
return R.ok(foodEnergyService.pageList(page, bizMonth, park, supplier));
}
@Operation(summary = "详情")
@GetMapping("/{id}")
public R get(@PathVariable Long id) {
return R.ok(foodEnergyService.getById(id));
}
@SysLog("删除食材能耗")
@DeleteMapping
public R remove(@RequestBody Long[] ids) {
return R.ok(foodEnergyService.removeBatchByIds(Arrays.asList(ids)));
}
@SysLog("按月清空食材能耗")
@DeleteMapping("/month/{bizMonth}")
public R clearMonth(@PathVariable String bizMonth) {
return R.ok(foodEnergyService.remove(Wrappers.<CanteenReportFoodEnergyEntity>lambdaQuery()
.eq(CanteenReportFoodEnergyEntity::getBizMonth, bizMonth)));
}
@Operation(summary = "导入Excel(自动识别「食材能耗」sheet,可传整本报表)")
@PostMapping("/import")
public R importExcel(@RequestParam("file") org.springframework.web.multipart.MultipartFile file,
@RequestParam String bizMonth,
@RequestParam(defaultValue = "true") boolean replaceMonth) throws Exception {
List<CanteenReportFoodEnergyExcel> rows = com.bsd.pjcloud.canteen.util.CanteenReportExcelUtils
.readSheet(file, CanteenReportFoodEnergyExcel.class, "食材能耗");
return foodEnergyService.importExcel(rows, bizMonth, replaceMonth, null);
}
@ResponseExcel
@GetMapping("/export")
public List<CanteenReportFoodEnergyExcel> export(@RequestParam(required = false) String bizMonth) {
return foodEnergyService.exportExcel(bizMonth);
}
}
@@ -0,0 +1,70 @@
package com.bsd.pjcloud.canteen.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.canteen.dto.report.CanteenReportManualAdjustExcel;
import com.bsd.pjcloud.canteen.entity.CanteenReportManualAdjustEntity;
import com.bsd.pjcloud.canteen.service.CanteenReportManualAdjustService;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.excel.annotation.RequestExcel;
import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
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.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@RestController
@RequiredArgsConstructor
@RequestMapping("/canteenReport/adjust")
@Tag(description = "canteenReportAdjust", name = "食堂报表-手工调整(演示)")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class CanteenReportManualAdjustController {
private final CanteenReportManualAdjustService adjustService;
@GetMapping("/page")
public R page(@ParameterObject Page page, @RequestParam(required = false) String bizMonth) {
return R.ok(adjustService.pageList(page, bizMonth));
}
@GetMapping("/{id}")
public R get(@PathVariable Long id) {
return R.ok(adjustService.getById(id));
}
@SysLog("新增手工调整")
@PostMapping
public R save(@RequestBody CanteenReportManualAdjustEntity entity) {
return R.ok(adjustService.save(entity));
}
@SysLog("修改手工调整")
@PutMapping
public R update(@RequestBody CanteenReportManualAdjustEntity entity) {
return R.ok(adjustService.updateById(entity));
}
@SysLog("删除手工调整")
@DeleteMapping
public R remove(@RequestBody Long[] ids) {
return R.ok(adjustService.removeBatchByIds(Arrays.asList(ids)));
}
@PostMapping("/import")
public R importExcel(@RequestExcel List<CanteenReportManualAdjustExcel> rows, BindingResult bindingResult) {
return adjustService.importExcel(rows, bindingResult);
}
@ResponseExcel
@GetMapping("/export")
public List<CanteenReportManualAdjustExcel> export(@RequestParam(required = false) String bizMonth) {
return adjustService.exportExcel(bizMonth);
}
}
@@ -0,0 +1,81 @@
package com.bsd.pjcloud.canteen.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.canteen.dto.report.CanteenReportSwipeExcel;
import com.bsd.pjcloud.canteen.entity.CanteenReportSwipeEntity;
import com.bsd.pjcloud.canteen.service.CanteenReportSwipeService;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
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.List;
/**
* 食堂报表-刷卡明细(演示,无权限/无DataScope)
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/canteenReport/swipe")
@Tag(description = "canteenReportSwipe", name = "食堂报表-刷卡明细(演示)")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class CanteenReportSwipeController {
private final CanteenReportSwipeService swipeService;
@Operation(summary = "分页查询")
@GetMapping("/page")
public R page(@ParameterObject Page page,
@RequestParam(required = false) String bizMonth,
@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));
}
@Operation(summary = "详情")
@GetMapping("/{id}")
public R get(@PathVariable Long id) {
return R.ok(swipeService.getById(id));
}
@SysLog("删除刷卡明细")
@Operation(summary = "删除")
@DeleteMapping
public R remove(@RequestBody Long[] ids) {
return R.ok(swipeService.removeBatchByIds(java.util.Arrays.asList(ids)));
}
@SysLog("按月清空刷卡明细")
@Operation(summary = "按业务月清空")
@DeleteMapping("/month/{bizMonth}")
public R clearMonth(@PathVariable String bizMonth) {
return R.ok(swipeService.remove(Wrappers.<CanteenReportSwipeEntity>lambdaQuery()
.eq(CanteenReportSwipeEntity::getBizMonth, bizMonth)));
}
@Operation(summary = "导入Excel(自动识别「刷卡明细」sheet,可传整本报表)")
@PostMapping("/import")
public R importExcel(@RequestParam("file") org.springframework.web.multipart.MultipartFile file,
@RequestParam String bizMonth,
@RequestParam(defaultValue = "true") boolean replaceMonth) throws Exception {
java.util.List<CanteenReportSwipeExcel> rows = com.bsd.pjcloud.canteen.util.CanteenReportExcelUtils
.readSheet(file, CanteenReportSwipeExcel.class, "刷卡明细");
return swipeService.importExcel(rows, bizMonth, replaceMonth, null);
}
@ResponseExcel
@Operation(summary = "导出Excel")
@GetMapping("/export")
public List<CanteenReportSwipeExcel> export(@RequestParam(required = false) String bizMonth) {
return swipeService.exportExcel(bizMonth);
}
}
@@ -0,0 +1,16 @@
package com.bsd.pjcloud.canteen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bsd.pjcloud.canteen.entity.CanteenReportFoodEnergyEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface CanteenReportFoodEnergyMapper extends BaseMapper<CanteenReportFoodEnergyEntity> {
int deletePhysicallyByBizMonth(@Param("bizMonth") String bizMonth);
int insertBatch(@Param("list") List<CanteenReportFoodEnergyEntity> list);
}
@@ -0,0 +1,9 @@
package com.bsd.pjcloud.canteen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bsd.pjcloud.canteen.entity.CanteenReportManualAdjustEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CanteenReportManualAdjustMapper extends BaseMapper<CanteenReportManualAdjustEntity> {
}
@@ -0,0 +1,16 @@
package com.bsd.pjcloud.canteen.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bsd.pjcloud.canteen.entity.CanteenReportSwipeEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface CanteenReportSwipeMapper extends BaseMapper<CanteenReportSwipeEntity> {
int deletePhysicallyByBizMonth(@Param("bizMonth") String bizMonth);
int insertBatch(@Param("list") List<CanteenReportSwipeEntity> list);
}
@@ -0,0 +1,22 @@
package com.bsd.pjcloud.canteen.service;
import com.bsd.pjcloud.canteen.vo.report.CanteenReportBusinessRowVO;
import com.bsd.pjcloud.canteen.vo.report.CanteenReportIncomeRowVO;
import com.bsd.pjcloud.canteen.vo.report.CanteenReportShareRowVO;
import java.util.List;
import java.util.Map;
/**
* 食堂报表计算(演示):收入合计 / 供应商分摊 / 经营报表
*/
public interface CanteenReportCalcService {
List<CanteenReportIncomeRowVO> incomeReport(String bizMonth);
List<CanteenReportShareRowVO> supplierShareReport(String bizMonth);
List<CanteenReportBusinessRowVO> businessReport(String bizMonth);
Map<String, Object> summary(String bizMonth);
}
@@ -0,0 +1,19 @@
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.dto.report.CanteenReportFoodEnergyExcel;
import com.bsd.pjcloud.canteen.entity.CanteenReportFoodEnergyEntity;
import com.bsd.pjcloud.common.core.util.R;
import org.springframework.validation.BindingResult;
import java.util.List;
public interface CanteenReportFoodEnergyService extends IService<CanteenReportFoodEnergyEntity> {
Page<CanteenReportFoodEnergyEntity> pageList(Page page, String bizMonth, String park, String supplier);
R importExcel(List<CanteenReportFoodEnergyExcel> rows, String bizMonth, boolean replaceMonth, BindingResult bindingResult);
List<CanteenReportFoodEnergyExcel> exportExcel(String bizMonth);
}
@@ -0,0 +1,19 @@
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.dto.report.CanteenReportManualAdjustExcel;
import com.bsd.pjcloud.canteen.entity.CanteenReportManualAdjustEntity;
import com.bsd.pjcloud.common.core.util.R;
import org.springframework.validation.BindingResult;
import java.util.List;
public interface CanteenReportManualAdjustService extends IService<CanteenReportManualAdjustEntity> {
Page<CanteenReportManualAdjustEntity> pageList(Page page, String bizMonth);
R importExcel(List<CanteenReportManualAdjustExcel> rows, BindingResult bindingResult);
List<CanteenReportManualAdjustExcel> exportExcel(String bizMonth);
}
@@ -0,0 +1,19 @@
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.dto.report.CanteenReportSwipeExcel;
import com.bsd.pjcloud.canteen.entity.CanteenReportSwipeEntity;
import com.bsd.pjcloud.common.core.util.R;
import org.springframework.validation.BindingResult;
import java.util.List;
public interface CanteenReportSwipeService extends IService<CanteenReportSwipeEntity> {
Page<CanteenReportSwipeEntity> pageList(Page page, String bizMonth, String park, String incomeType, String costCenter, String empNo);
R importExcel(List<CanteenReportSwipeExcel> rows, String bizMonth, boolean replaceMonth, BindingResult bindingResult);
List<CanteenReportSwipeExcel> exportExcel(String bizMonth);
}
@@ -0,0 +1,485 @@
package com.bsd.pjcloud.canteen.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bsd.pjcloud.canteen.entity.CanteenReportFoodEnergyEntity;
import com.bsd.pjcloud.canteen.entity.CanteenReportManualAdjustEntity;
import com.bsd.pjcloud.canteen.entity.CanteenReportSwipeEntity;
import com.bsd.pjcloud.canteen.report.CanteenReportConstants;
import com.bsd.pjcloud.canteen.service.CanteenReportCalcService;
import com.bsd.pjcloud.canteen.service.CanteenReportFoodEnergyService;
import com.bsd.pjcloud.canteen.service.CanteenReportManualAdjustService;
import com.bsd.pjcloud.canteen.service.CanteenReportSwipeService;
import com.bsd.pjcloud.canteen.vo.report.CanteenReportBusinessRowVO;
import com.bsd.pjcloud.canteen.vo.report.CanteenReportIncomeRowVO;
import com.bsd.pjcloud.canteen.vo.report.CanteenReportShareRowVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 报表计算演示实现:
* 收入合计 = 刷卡明细按成本中心×厂区汇总;
* 供应商分摊 = 权重 × (厂区食材池 − 厂区包厢 − 厂区其他收入),权重=中心员工刷卡/厂区员工刷卡;
* 经营报表 = 收入(万元) + 食材支出(分摊/10000) + 毛利分析。
*/
@Service
@RequiredArgsConstructor
public class CanteenReportCalcServiceImpl implements CanteenReportCalcService {
private static final BigDecimal WAN = new BigDecimal("10000");
private static final int SCALE = 2;
private final CanteenReportSwipeService swipeService;
private final CanteenReportFoodEnergyService foodEnergyService;
private final CanteenReportManualAdjustService adjustService;
@Override
public List<CanteenReportIncomeRowVO> incomeReport(String bizMonth) {
List<CanteenReportSwipeEntity> swipes = loadSwipes(bizMonth);
// key: incomeType|costCenter|park -> amount
Map<String, BigDecimal> agg = new HashMap<>();
for (CanteenReportSwipeEntity s : swipes) {
String type = StrUtil.blankToDefault(s.getIncomeType(), "");
String center = StrUtil.blankToDefault(s.getCostCenter(), "未匹配成本中心");
String park = StrUtil.blankToDefault(s.getPark(), "");
String key = type + "|" + center + "|" + park;
agg.merge(key, nz(s.getConsumeAmount()), BigDecimal::add);
}
List<CanteenReportIncomeRowVO> rows = new ArrayList<>();
// 员工刷卡:固定成本中心顺序,再追加未配置中心
appendIncomeGroup(rows, CanteenReportConstants.INCOME_SWIPE, agg, true);
for (String type : CanteenReportConstants.OTHER_INCOME_TYPES) {
appendIncomeGroup(rows, type, agg, false);
}
// 总计
CanteenReportIncomeRowVO total = emptyIncomeRow("总计", "总计");
for (CanteenReportIncomeRowVO r : rows) {
mergePark(total.getParkAmounts(), r.getParkAmounts());
total.setSubtotal(total.getSubtotal().add(r.getSubtotal()));
}
rows.add(total);
return rows;
}
@Override
public List<CanteenReportShareRowVO> supplierShareReport(String bizMonth) {
List<CanteenReportSwipeEntity> swipes = loadSwipes(bizMonth);
List<CanteenReportFoodEnergyEntity> foods = loadFoods(bizMonth);
List<CanteenReportManualAdjustEntity> adjusts = loadAdjusts(bizMonth);
// 厂区食材池 = SUM(结算总金额) WHERE 分类 IN (食材,能耗,外协);分类空视为食材
Map<String, BigDecimal> parkFoodPool = new HashMap<>();
for (CanteenReportFoodEnergyEntity f : foods) {
String classify = StrUtil.blankToDefault(f.getClassify(), "食材").trim();
if (!CanteenReportConstants.SHARE_FOOD_CLASSIFY.contains(classify)) {
continue;
}
parkFoodPool.merge(StrUtil.blankToDefault(f.getPark(), ""), nz(f.getSettleAmount()), BigDecimal::add);
}
// 厂区员工刷卡、中心×厂区员工刷卡;厂区/中心的包厢+其他收入
Map<String, BigDecimal> parkSwipeTotal = new HashMap<>();
Map<String, BigDecimal> centerParkSwipe = new HashMap<>();
Map<String, BigDecimal> parkBoxOther = new HashMap<>();
Map<String, BigDecimal> centerBoxOther = new HashMap<>();
for (CanteenReportSwipeEntity s : swipes) {
String park = StrUtil.blankToDefault(s.getPark(), "");
String center = StrUtil.blankToDefault(s.getCostCenter(), "未匹配成本中心");
String type = StrUtil.blankToDefault(s.getIncomeType(), "");
BigDecimal amt = nz(s.getConsumeAmount());
if (CanteenReportConstants.INCOME_SWIPE.equals(type)) {
parkSwipeTotal.merge(park, amt, BigDecimal::add);
centerParkSwipe.merge(center + "|" + park, amt, BigDecimal::add);
}
if (CanteenReportConstants.SHARE_BOX_OTHER_TYPES.contains(type)) {
parkBoxOther.merge(park, amt, BigDecimal::add);
centerBoxOther.merge(center, amt, BigDecimal::add);
}
}
// 可分摊 = 厂区食材池 − 厂区包厢收入 − 厂区其他收入(与样表一致)
Map<String, BigDecimal> parkAllocable = new LinkedHashMap<>();
for (String park : CanteenReportConstants.SHARE_PARKS) {
parkAllocable.put(park, nz(parkFoodPool.get(park)).subtract(nz(parkBoxOther.get(park))));
}
Map<String, BigDecimal> centerAdjust = new HashMap<>();
BigDecimal globalAdjust = BigDecimal.ZERO;
for (CanteenReportManualAdjustEntity a : adjusts) {
if (StrUtil.isNotBlank(a.getCostCenter())) {
centerAdjust.merge(a.getCostCenter(), nz(a.getAmount()), BigDecimal::add);
}
else {
globalAdjust = globalAdjust.add(nz(a.getAmount()));
}
}
List<String> centers = new ArrayList<>(CanteenReportConstants.COST_CENTERS);
for (String k : centerParkSwipe.keySet()) {
String c = k.substring(0, k.indexOf('|'));
if (!centers.contains(c)) {
centers.add(c);
}
}
for (String c : centerBoxOther.keySet()) {
if (!centers.contains(c)) {
centers.add(c);
}
}
for (String c : centerAdjust.keySet()) {
if (!centers.contains(c)) {
centers.add(c);
}
}
List<CanteenReportShareRowVO> rows = new ArrayList<>();
Map<String, BigDecimal> shareParkTotal = new LinkedHashMap<>();
for (String park : CanteenReportConstants.SHARE_PARKS) {
shareParkTotal.put(park, BigDecimal.ZERO);
}
BigDecimal totalSub = BigDecimal.ZERO;
BigDecimal totalBox = BigDecimal.ZERO;
BigDecimal totalAdj = BigDecimal.ZERO;
BigDecimal totalInvoice = BigDecimal.ZERO;
for (String center : centers) {
CanteenReportShareRowVO row = new CanteenReportShareRowVO();
row.setCostCenter(center);
BigDecimal sub = BigDecimal.ZERO;
for (String park : CanteenReportConstants.SHARE_PARKS) {
BigDecimal allocable = nz(parkAllocable.get(park));
BigDecimal parkTotal = nz(parkSwipeTotal.get(park));
BigDecimal centerAmt = nz(centerParkSwipe.get(center + "|" + park));
BigDecimal share = BigDecimal.ZERO;
if (parkTotal.compareTo(BigDecimal.ZERO) > 0 && allocable.compareTo(BigDecimal.ZERO) != 0) {
share = centerAmt.divide(parkTotal, 8, RoundingMode.HALF_UP).multiply(allocable)
.setScale(SCALE, RoundingMode.HALF_UP);
}
row.getParkAmounts().put(park, share);
shareParkTotal.merge(park, share, BigDecimal::add);
sub = sub.add(share);
}
row.setSubtotal(sub.setScale(SCALE, RoundingMode.HALF_UP));
row.setBoxAndOther(nz(centerBoxOther.get(center)).setScale(SCALE, RoundingMode.HALF_UP));
row.setOtherAdjust(nz(centerAdjust.get(center)).setScale(SCALE, RoundingMode.HALF_UP));
row.setInvoiceAmount(row.getSubtotal().add(row.getBoxAndOther()).add(row.getOtherAdjust())
.setScale(SCALE, RoundingMode.HALF_UP));
rows.add(row);
totalSub = totalSub.add(row.getSubtotal());
totalBox = totalBox.add(row.getBoxAndOther());
totalAdj = totalAdj.add(row.getOtherAdjust());
totalInvoice = totalInvoice.add(row.getInvoiceAmount());
}
// 无成本中心的全局调整(如开票),单独一行,不参与厂区分摊
if (globalAdjust.compareTo(BigDecimal.ZERO) != 0) {
CanteenReportShareRowVO globalRow = new CanteenReportShareRowVO();
globalRow.setCostCenter("开票调整(全局)");
for (String park : CanteenReportConstants.SHARE_PARKS) {
globalRow.getParkAmounts().put(park, BigDecimal.ZERO);
}
globalRow.setSubtotal(BigDecimal.ZERO);
globalRow.setBoxAndOther(BigDecimal.ZERO);
globalRow.setOtherAdjust(globalAdjust.setScale(SCALE, RoundingMode.HALF_UP));
globalRow.setInvoiceAmount(globalRow.getOtherAdjust());
rows.add(globalRow);
totalAdj = totalAdj.add(globalRow.getOtherAdjust());
totalInvoice = totalInvoice.add(globalRow.getInvoiceAmount());
}
// 小计行
CanteenReportShareRowVO subtotalRow = new CanteenReportShareRowVO();
subtotalRow.setCostCenter("小计");
for (String park : CanteenReportConstants.SHARE_PARKS) {
subtotalRow.getParkAmounts().put(park, nz(shareParkTotal.get(park)).setScale(SCALE, RoundingMode.HALF_UP));
}
subtotalRow.setSubtotal(totalSub.setScale(SCALE, RoundingMode.HALF_UP));
subtotalRow.setBoxAndOther(totalBox.setScale(SCALE, RoundingMode.HALF_UP));
subtotalRow.setOtherAdjust(totalAdj.setScale(SCALE, RoundingMode.HALF_UP));
subtotalRow.setInvoiceAmount(totalInvoice.setScale(SCALE, RoundingMode.HALF_UP));
rows.add(subtotalRow);
return rows;
}
@Override
public List<CanteenReportBusinessRowVO> businessReport(String bizMonth) {
List<CanteenReportSwipeEntity> swipes = loadSwipes(bizMonth);
List<CanteenReportShareRowVO> shares = supplierShareReport(bizMonth);
Map<String, Map<String, BigDecimal>> incomeByTypePark = new HashMap<>();
for (String t : CanteenReportConstants.INCOME_TYPES) {
incomeByTypePark.put(t, emptyParkMap());
}
for (CanteenReportSwipeEntity s : swipes) {
String type = s.getIncomeType();
if (!incomeByTypePark.containsKey(type)) {
continue;
}
String park = StrUtil.blankToDefault(s.getPark(), "");
incomeByTypePark.get(type).merge(park, nz(s.getConsumeAmount()), BigDecimal::add);
}
List<CanteenReportBusinessRowVO> rows = new ArrayList<>();
CanteenReportBusinessRowVO incomeSub = null;
for (String type : CanteenReportConstants.INCOME_TYPES) {
CanteenReportBusinessRowVO row = toWanRow("收入(万元)", type, incomeByTypePark.get(type));
rows.add(row);
}
incomeSub = sumRows("收入(万元)", "小计", rows);
rows.add(incomeSub);
// 食材支出 = 分摊表各厂区成本中心分摊之和 / 10000
Map<String, BigDecimal> foodExpenseYuan = emptyParkMap();
for (CanteenReportShareRowVO share : shares) {
if (isShareMetaRow(share.getCostCenter())) {
continue;
}
for (Map.Entry<String, BigDecimal> e : share.getParkAmounts().entrySet()) {
foodExpenseYuan.merge(e.getKey(), nz(e.getValue()), BigDecimal::add);
}
}
CanteenReportBusinessRowVO foodRow = toWanRow("支出(万元)", "食材", foodExpenseYuan);
rows.add(foodRow);
rows.add(toWanRow("支出(万元)", "能耗", emptyParkMap()));
rows.add(toWanRow("支出(万元)", "人工", emptyParkMap()));
rows.add(toWanRow("支出(万元)", "外协", emptyParkMap()));
CanteenReportBusinessRowVO expenseSub = cloneAs("支出(万元)", "小计", foodRow);
rows.add(expenseSub);
// 毛利
CanteenReportBusinessRowVO profit = subtractRow("经营分析", "毛利", incomeSub, expenseSub);
rows.add(profit);
rows.add(ratioRow("经营分析", "毛利率", profit, incomeSub));
rows.add(ratioRow("经营分析", "成本收入比率", expenseSub, incomeSub));
// 费用预估
CanteenReportBusinessRowVO estFood = negateRow("费用预估", "预估食材", profit);
rows.add(estFood);
Map<String, BigDecimal> adjYuan = emptyParkMap();
for (String t : List.of(CanteenReportConstants.INCOME_BAD_DEBT, CanteenReportConstants.INCOME_PROFIT_ADJ,
CanteenReportConstants.INCOME_INVOICE)) {
Map<String, BigDecimal> m = incomeByTypePark.get(t);
if (m != null) {
mergePark(adjYuan, m);
}
}
CanteenReportBusinessRowVO deduct = toWanRow("费用预估", "扣除调整", adjYuan);
for (Map.Entry<String, BigDecimal> e : deduct.getParkAmounts().entrySet()) {
e.setValue(nz(e.getValue()).negate());
}
deduct.setSubtotal(nz(deduct.getSubtotal()).negate());
rows.add(deduct);
rows.add(subtractRow("费用预估", "实际费用", estFood, deduct));
return rows;
}
@Override
public Map<String, Object> summary(String bizMonth) {
List<CanteenReportSwipeEntity> swipes = loadSwipes(bizMonth);
List<CanteenReportFoodEnergyEntity> foods = loadFoods(bizMonth);
BigDecimal swipeSum = swipes.stream().map(s -> nz(s.getConsumeAmount())).reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal foodSum = foods.stream().map(f -> nz(f.getSettleAmount())).reduce(BigDecimal.ZERO, BigDecimal::add);
Map<String, Object> map = new LinkedHashMap<>();
map.put("bizMonth", bizMonth);
map.put("swipeCount", swipes.size());
map.put("swipeAmount", swipeSum);
map.put("foodCount", foods.size());
map.put("foodAmount", foodSum);
return map;
}
private static boolean isShareMetaRow(String costCenter) {
return "小计".equals(costCenter)
|| (costCenter != null && costCenter.startsWith("检查"))
|| "开票调整(全局)".equals(costCenter);
}
private void appendIncomeGroup(List<CanteenReportIncomeRowVO> rows, String incomeType,
Map<String, BigDecimal> agg, boolean useFixedCenters) {
List<String> centers = useFixedCenters ? new ArrayList<>(CanteenReportConstants.COST_CENTERS)
: new ArrayList<>();
if (!useFixedCenters) {
for (String key : agg.keySet()) {
if (key.startsWith(incomeType + "|")) {
String center = key.substring((incomeType + "|").length(), key.lastIndexOf('|'));
if (!centers.contains(center)) {
centers.add(center);
}
}
}
}
else {
for (String key : agg.keySet()) {
if (key.startsWith(incomeType + "|")) {
String center = key.substring((incomeType + "|").length(), key.lastIndexOf('|'));
if (!centers.contains(center)) {
centers.add(center);
}
}
}
}
for (String center : centers) {
CanteenReportIncomeRowVO row = emptyIncomeRow(incomeType, center);
BigDecimal sub = BigDecimal.ZERO;
boolean any = false;
for (String park : CanteenReportConstants.PARKS) {
BigDecimal amt = nz(agg.get(incomeType + "|" + center + "|" + park)).setScale(SCALE, RoundingMode.HALF_UP);
row.getParkAmounts().put(park, amt);
sub = sub.add(amt);
if (amt.compareTo(BigDecimal.ZERO) != 0) {
any = true;
}
}
row.setSubtotal(sub);
if (any || useFixedCenters) {
rows.add(row);
}
}
}
private List<CanteenReportSwipeEntity> loadSwipes(String bizMonth) {
return swipeService.list(Wrappers.<CanteenReportSwipeEntity>lambdaQuery()
.eq(CanteenReportSwipeEntity::getBizMonth, bizMonth));
}
private List<CanteenReportFoodEnergyEntity> loadFoods(String bizMonth) {
return foodEnergyService.list(Wrappers.<CanteenReportFoodEnergyEntity>lambdaQuery()
.eq(CanteenReportFoodEnergyEntity::getBizMonth, bizMonth));
}
private List<CanteenReportManualAdjustEntity> loadAdjusts(String bizMonth) {
return adjustService.list(Wrappers.<CanteenReportManualAdjustEntity>lambdaQuery()
.eq(CanteenReportManualAdjustEntity::getBizMonth, bizMonth));
}
private static BigDecimal nz(BigDecimal v) {
return v == null ? BigDecimal.ZERO : v;
}
private static CanteenReportIncomeRowVO emptyIncomeRow(String group, String center) {
CanteenReportIncomeRowVO row = new CanteenReportIncomeRowVO();
row.setGroupName(group);
row.setCostCenter(center);
for (String park : CanteenReportConstants.PARKS) {
row.getParkAmounts().put(park, BigDecimal.ZERO);
}
row.setSubtotal(BigDecimal.ZERO);
return row;
}
private static Map<String, BigDecimal> emptyParkMap() {
Map<String, BigDecimal> map = new LinkedHashMap<>();
for (String park : CanteenReportConstants.PARKS) {
map.put(park, BigDecimal.ZERO);
}
return map;
}
private static void mergePark(Map<String, BigDecimal> target, Map<String, BigDecimal> src) {
if (src == null) {
return;
}
for (Map.Entry<String, BigDecimal> e : src.entrySet()) {
target.merge(e.getKey(), nz(e.getValue()), BigDecimal::add);
}
}
private static CanteenReportBusinessRowVO toWanRow(String type, String category, Map<String, BigDecimal> yuanByPark) {
CanteenReportBusinessRowVO row = new CanteenReportBusinessRowVO();
row.setType(type);
row.setCategory(category);
BigDecimal sub = BigDecimal.ZERO;
for (String park : CanteenReportConstants.PARKS) {
BigDecimal wan = nz(yuanByPark.get(park)).divide(WAN, SCALE, RoundingMode.HALF_UP);
row.getParkAmounts().put(park, wan);
sub = sub.add(wan);
}
row.setSubtotal(sub);
return row;
}
private static CanteenReportBusinessRowVO sumRows(String type, String category, List<CanteenReportBusinessRowVO> rows) {
CanteenReportBusinessRowVO row = new CanteenReportBusinessRowVO();
row.setType(type);
row.setCategory(category);
row.setParkAmounts(emptyParkMap());
BigDecimal sub = BigDecimal.ZERO;
for (CanteenReportBusinessRowVO r : rows) {
if (!type.equals(r.getType()) || "小计".equals(r.getCategory())) {
continue;
}
mergePark(row.getParkAmounts(), r.getParkAmounts());
sub = sub.add(nz(r.getSubtotal()));
}
for (Map.Entry<String, BigDecimal> e : row.getParkAmounts().entrySet()) {
e.setValue(nz(e.getValue()).setScale(SCALE, RoundingMode.HALF_UP));
}
row.setSubtotal(sub.setScale(SCALE, RoundingMode.HALF_UP));
return row;
}
private static CanteenReportBusinessRowVO cloneAs(String type, String category, CanteenReportBusinessRowVO src) {
CanteenReportBusinessRowVO row = new CanteenReportBusinessRowVO();
row.setType(type);
row.setCategory(category);
row.setParkAmounts(new LinkedHashMap<>(src.getParkAmounts()));
row.setSubtotal(src.getSubtotal());
return row;
}
private static CanteenReportBusinessRowVO subtractRow(String type, String category,
CanteenReportBusinessRowVO a, CanteenReportBusinessRowVO b) {
CanteenReportBusinessRowVO row = new CanteenReportBusinessRowVO();
row.setType(type);
row.setCategory(category);
BigDecimal sub = BigDecimal.ZERO;
for (String park : CanteenReportConstants.PARKS) {
BigDecimal v = nz(a.getParkAmounts().get(park)).subtract(nz(b.getParkAmounts().get(park)))
.setScale(SCALE, RoundingMode.HALF_UP);
row.getParkAmounts().put(park, v);
sub = sub.add(v);
}
row.setSubtotal(sub);
return row;
}
private static CanteenReportBusinessRowVO negateRow(String type, String category, CanteenReportBusinessRowVO src) {
CanteenReportBusinessRowVO row = new CanteenReportBusinessRowVO();
row.setType(type);
row.setCategory(category);
BigDecimal sub = BigDecimal.ZERO;
for (String park : CanteenReportConstants.PARKS) {
BigDecimal v = nz(src.getParkAmounts().get(park)).negate().setScale(SCALE, RoundingMode.HALF_UP);
row.getParkAmounts().put(park, v);
sub = sub.add(v);
}
row.setSubtotal(sub);
return row;
}
private static CanteenReportBusinessRowVO ratioRow(String type, String category,
CanteenReportBusinessRowVO num, CanteenReportBusinessRowVO den) {
CanteenReportBusinessRowVO row = new CanteenReportBusinessRowVO();
row.setType(type);
row.setCategory(category);
for (String park : CanteenReportConstants.PARKS) {
BigDecimal d = nz(den.getParkAmounts().get(park));
BigDecimal n = nz(num.getParkAmounts().get(park));
BigDecimal r = d.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO
: n.divide(d, 4, RoundingMode.HALF_UP);
row.getParkAmounts().put(park, r);
}
BigDecimal ds = nz(den.getSubtotal());
row.setSubtotal(ds.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO
: nz(num.getSubtotal()).divide(ds, 4, RoundingMode.HALF_UP));
return row;
}
}
@@ -0,0 +1,180 @@
package com.bsd.pjcloud.canteen.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
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.canteen.dto.report.CanteenReportFoodEnergyExcel;
import com.bsd.pjcloud.canteen.entity.CanteenReportFoodEnergyEntity;
import com.bsd.pjcloud.canteen.mapper.CanteenReportFoodEnergyMapper;
import com.bsd.pjcloud.canteen.service.CanteenReportFoodEnergyService;
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.excel.vo.ErrorMessage;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
@RequiredArgsConstructor
public class CanteenReportFoodEnergyServiceImpl
extends ServiceImpl<CanteenReportFoodEnergyMapper, CanteenReportFoodEnergyEntity>
implements CanteenReportFoodEnergyService {
private static final int BATCH_SIZE = 800;
@Override
public Page<CanteenReportFoodEnergyEntity> pageList(Page page, String bizMonth, String park, String supplier) {
return page(page, Wrappers.<CanteenReportFoodEnergyEntity>lambdaQuery()
.eq(StrUtil.isNotBlank(bizMonth), CanteenReportFoodEnergyEntity::getBizMonth, bizMonth)
.eq(StrUtil.isNotBlank(park), CanteenReportFoodEnergyEntity::getPark, park)
.like(StrUtil.isNotBlank(supplier), CanteenReportFoodEnergyEntity::getSupplier, supplier)
.orderByDesc(CanteenReportFoodEnergyEntity::getId));
}
@Override
@Transactional(rollbackFor = Exception.class)
public R importExcel(List<CanteenReportFoodEnergyExcel> rows, String bizMonth, boolean replaceMonth,
BindingResult bindingResult) {
if (StrUtil.isBlank(bizMonth)) {
return R.failed("请传入业务月份 bizMonth,格式 yyyy-MM");
}
List<ErrorMessage> errorMessageList = bindingResult == null ? new ArrayList<>()
: (List<ErrorMessage>) bindingResult.getTarget();
if (errorMessageList == null) {
errorMessageList = new ArrayList<>();
}
if (CollUtil.isEmpty(rows)) {
return R.failed("导入数据为空");
}
List<CanteenReportFoodEnergyEntity> saveList = new ArrayList<>(Math.min(rows.size(), 32768));
int line = 1;
for (CanteenReportFoodEnergyExcel row : rows) {
line++;
Set<String> errors = new HashSet<>();
if (StrUtil.isBlank(row.getSupplier()) && row.getSettleAmount() == null) {
continue;
}
if (StrUtil.isBlank(row.getSupplier())) {
errors.add("供应商不能为空");
}
if (CollUtil.isNotEmpty(errors)) {
errorMessageList.add(new ErrorMessage((long) line, errors));
continue;
}
CanteenReportFoodEnergyEntity e = new CanteenReportFoodEnergyEntity();
BeanUtils.copyProperties(row, e);
e.setBizMonth(bizMonth);
fillCalculatedAmounts(e);
if (e.getEnergyAmount() == null) {
e.setEnergyAmount(BigDecimal.ZERO);
}
if (e.getLaborAmount() == null) {
e.setLaborAmount(BigDecimal.ZERO);
}
if (e.getConsumableAmount() == null) {
e.setConsumableAmount(BigDecimal.ZERO);
}
if (e.getOutsourceAmount() == null) {
e.setOutsourceAmount(BigDecimal.ZERO);
}
saveList.add(e);
}
if (CollUtil.isNotEmpty(errorMessageList)) {
return R.failed(errorMessageList, "导入校验失败,共 " + errorMessageList.size() + " 行有问题");
}
if (replaceMonth) {
baseMapper.deletePhysicallyByBizMonth(bizMonth);
}
fillInsertMeta(saveList);
for (List<CanteenReportFoodEnergyEntity> chunk : CollUtil.split(saveList, BATCH_SIZE)) {
baseMapper.insertBatch(chunk);
}
return R.ok(null, "导入成功,共 " + saveList.size() + " 条");
}
private void fillInsertMeta(List<CanteenReportFoodEnergyEntity> list) {
LocalDateTime now = LocalDateTime.now();
String user = null;
try {
if (SecurityUtils.getUser() != null) {
user = SecurityUtils.getUser().getUsername();
}
}
catch (Exception ignored) {
// ignore
}
Long tenantId = TenantContextHolder.getTenantId();
for (CanteenReportFoodEnergyEntity e : list) {
e.setId(IdWorker.getId());
e.setCreateBy(user);
e.setUpdateBy(user);
e.setCreateTime(now);
e.setUpdateTime(now);
e.setDelFlag(CommonConstants.STATUS_NORMAL);
e.setTenantId(tenantId);
}
}
/**
* 数量/单价/定价取 Excel;差异、结算单价、结算总金额、金额、金额差异自动补齐。
*/
private void fillCalculatedAmounts(CanteenReportFoodEnergyEntity e) {
BigDecimal qty = nz(e.getQty());
BigDecimal unitPrice = nz(e.getUnitPrice());
BigDecimal listPrice = nz(e.getListPrice());
if (e.getPriceDiff() == null) {
e.setPriceDiff(unitPrice.subtract(listPrice));
}
if (e.getSettleUnitPrice() == null) {
e.setSettleUnitPrice(listPrice);
}
if (e.getSettleAmount() == null) {
e.setSettleAmount(nz(e.getSettleUnitPrice()).multiply(qty).setScale(2, java.math.RoundingMode.HALF_UP));
}
if (e.getAmount() == null) {
e.setAmount(unitPrice.multiply(qty).setScale(2, java.math.RoundingMode.HALF_UP));
}
if (e.getAmountDiff() == null) {
e.setAmountDiff(nz(e.getAmount()).subtract(nz(e.getSettleAmount())));
}
if (e.getSettleAmount() == null) {
e.setSettleAmount(BigDecimal.ZERO);
}
}
private static BigDecimal nz(BigDecimal v) {
return v == null ? BigDecimal.ZERO : v;
}
@Override
public List<CanteenReportFoodEnergyExcel> exportExcel(String bizMonth) {
List<CanteenReportFoodEnergyEntity> list = list(Wrappers.<CanteenReportFoodEnergyEntity>lambdaQuery()
.eq(StrUtil.isNotBlank(bizMonth), CanteenReportFoodEnergyEntity::getBizMonth, bizMonth)
.orderByAsc(CanteenReportFoodEnergyEntity::getId));
List<CanteenReportFoodEnergyExcel> result = new ArrayList<>(list.size());
for (CanteenReportFoodEnergyEntity e : list) {
CanteenReportFoodEnergyExcel x = new CanteenReportFoodEnergyExcel();
BeanUtils.copyProperties(e, x);
result.add(x);
}
return result;
}
}
@@ -0,0 +1,88 @@
package com.bsd.pjcloud.canteen.service.impl;
import cn.hutool.core.collection.CollUtil;
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.dto.report.CanteenReportManualAdjustExcel;
import com.bsd.pjcloud.canteen.entity.CanteenReportManualAdjustEntity;
import com.bsd.pjcloud.canteen.mapper.CanteenReportManualAdjustMapper;
import com.bsd.pjcloud.canteen.service.CanteenReportManualAdjustService;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.excel.vo.ErrorMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
@RequiredArgsConstructor
public class CanteenReportManualAdjustServiceImpl
extends ServiceImpl<CanteenReportManualAdjustMapper, CanteenReportManualAdjustEntity>
implements CanteenReportManualAdjustService {
@Override
public Page<CanteenReportManualAdjustEntity> pageList(Page page, String bizMonth) {
return page(page, Wrappers.<CanteenReportManualAdjustEntity>lambdaQuery()
.eq(StrUtil.isNotBlank(bizMonth), CanteenReportManualAdjustEntity::getBizMonth, bizMonth)
.orderByDesc(CanteenReportManualAdjustEntity::getId));
}
@Override
@Transactional(rollbackFor = Exception.class)
public R importExcel(List<CanteenReportManualAdjustExcel> rows, BindingResult bindingResult) {
List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
if (CollUtil.isEmpty(rows)) {
return R.failed("导入数据为空");
}
List<CanteenReportManualAdjustEntity> saveList = new ArrayList<>();
int line = 1;
for (CanteenReportManualAdjustExcel row : rows) {
line++;
Set<String> errors = new HashSet<>();
if (StrUtil.isBlank(row.getBizMonth())) {
errors.add("业务月份不能为空");
}
if (row.getAmount() == null) {
errors.add("调整金额不能为空");
}
if (CollUtil.isNotEmpty(errors)) {
errorMessageList.add(new ErrorMessage((long) line, errors));
continue;
}
CanteenReportManualAdjustEntity e = new CanteenReportManualAdjustEntity();
BeanUtils.copyProperties(row, e);
if (e.getAmount() == null) {
e.setAmount(BigDecimal.ZERO);
}
saveList.add(e);
}
if (CollUtil.isNotEmpty(errorMessageList)) {
return R.failed(errorMessageList);
}
saveBatch(saveList);
return R.ok(null, "导入成功,共 " + saveList.size() + " 条");
}
@Override
public List<CanteenReportManualAdjustExcel> exportExcel(String bizMonth) {
List<CanteenReportManualAdjustEntity> list = list(Wrappers.<CanteenReportManualAdjustEntity>lambdaQuery()
.eq(StrUtil.isNotBlank(bizMonth), CanteenReportManualAdjustEntity::getBizMonth, bizMonth)
.orderByAsc(CanteenReportManualAdjustEntity::getId));
List<CanteenReportManualAdjustExcel> result = new ArrayList<>(list.size());
for (CanteenReportManualAdjustEntity e : list) {
CanteenReportManualAdjustExcel x = new CanteenReportManualAdjustExcel();
BeanUtils.copyProperties(e, x);
result.add(x);
}
return result;
}
}
@@ -0,0 +1,153 @@
package com.bsd.pjcloud.canteen.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
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.canteen.dto.report.CanteenReportSwipeExcel;
import com.bsd.pjcloud.canteen.entity.CanteenReportSwipeEntity;
import com.bsd.pjcloud.canteen.mapper.CanteenReportSwipeMapper;
import com.bsd.pjcloud.canteen.report.CanteenReportConstants;
import com.bsd.pjcloud.canteen.service.CanteenReportSwipeService;
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.excel.vo.ErrorMessage;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
@RequiredArgsConstructor
public class CanteenReportSwipeServiceImpl extends ServiceImpl<CanteenReportSwipeMapper, CanteenReportSwipeEntity>
implements CanteenReportSwipeService {
/** 每批多值 INSERT 行数(单条 SQL,避免 saveBatch 逐条写库) */
private static final int BATCH_SIZE = 800;
@Override
public Page<CanteenReportSwipeEntity> pageList(Page page, String bizMonth, String park, String incomeType,
String costCenter, String empNo) {
return page(page, Wrappers.<CanteenReportSwipeEntity>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)
.orderByDesc(CanteenReportSwipeEntity::getId));
}
@Override
@Transactional(rollbackFor = Exception.class)
public R importExcel(List<CanteenReportSwipeExcel> rows, String bizMonth, boolean replaceMonth,
BindingResult bindingResult) {
if (StrUtil.isBlank(bizMonth)) {
return R.failed("请传入业务月份 bizMonth,格式 yyyy-MM");
}
List<ErrorMessage> errorMessageList = bindingResult == null ? new ArrayList<>()
: (List<ErrorMessage>) bindingResult.getTarget();
if (errorMessageList == null) {
errorMessageList = new ArrayList<>();
}
if (CollUtil.isEmpty(rows)) {
return R.failed("导入数据为空");
}
List<CanteenReportSwipeEntity> saveList = new ArrayList<>(Math.min(rows.size(), 32768));
int line = 1;
for (CanteenReportSwipeExcel row : rows) {
line++;
if (StrUtil.isBlank(row.getEmpNo()) && row.getConsumeAmount() == null && StrUtil.isBlank(row.getIncomeType())) {
continue;
}
Set<String> errors = new HashSet<>();
if (StrUtil.isBlank(row.getIncomeType())) {
errors.add("收入类别不能为空");
}
else if (!CanteenReportConstants.INCOME_TYPES.contains(row.getIncomeType().trim())) {
errors.add("收入类别不在枚举内:" + row.getIncomeType());
}
if (StrUtil.isNotBlank(row.getPark()) && !CanteenReportConstants.PARKS.contains(row.getPark().trim())
&& !CanteenReportConstants.SHARE_PARKS.contains(row.getPark().trim())) {
errors.add("厂区不在枚举内:" + row.getPark());
}
boolean allowNeg = CanteenReportConstants.OTHER_INCOME_TYPES.contains(StrUtil.trim(row.getIncomeType()));
if (row.getConsumeAmount() != null && row.getConsumeAmount().compareTo(BigDecimal.ZERO) < 0 && !allowNeg) {
errors.add("非调整类不允许负数");
}
if (CollUtil.isNotEmpty(errors)) {
errorMessageList.add(new ErrorMessage((long) line, errors));
continue;
}
CanteenReportSwipeEntity e = new CanteenReportSwipeEntity();
BeanUtils.copyProperties(row, e);
e.setBizMonth(bizMonth);
if (e.getConsumeAmount() == null) {
e.setConsumeAmount(BigDecimal.ZERO);
}
saveList.add(e);
}
if (CollUtil.isNotEmpty(errorMessageList)) {
return R.failed(errorMessageList, "导入校验失败,共 " + errorMessageList.size() + " 行有问题");
}
if (replaceMonth) {
baseMapper.deletePhysicallyByBizMonth(bizMonth);
}
fillInsertMeta(saveList);
for (List<CanteenReportSwipeEntity> chunk : CollUtil.split(saveList, BATCH_SIZE)) {
baseMapper.insertBatch(chunk);
}
return R.ok(null, "导入成功,共 " + saveList.size() + " 条");
}
private void fillInsertMeta(List<CanteenReportSwipeEntity> list) {
LocalDateTime now = LocalDateTime.now();
String user = null;
try {
if (SecurityUtils.getUser() != null) {
user = SecurityUtils.getUser().getUsername();
}
}
catch (Exception ignored) {
// ignore
}
Long tenantId = TenantContextHolder.getTenantId();
for (CanteenReportSwipeEntity e : list) {
e.setId(IdWorker.getId());
e.setCreateBy(user);
e.setUpdateBy(user);
e.setCreateTime(now);
e.setUpdateTime(now);
e.setDelFlag(CommonConstants.STATUS_NORMAL);
e.setTenantId(tenantId);
}
}
@Override
public List<CanteenReportSwipeExcel> exportExcel(String bizMonth) {
List<CanteenReportSwipeEntity> list = list(Wrappers.<CanteenReportSwipeEntity>lambdaQuery()
.eq(StrUtil.isNotBlank(bizMonth), CanteenReportSwipeEntity::getBizMonth, bizMonth)
.orderByAsc(CanteenReportSwipeEntity::getId));
List<CanteenReportSwipeExcel> result = new ArrayList<>(list.size());
for (CanteenReportSwipeEntity e : list) {
CanteenReportSwipeExcel x = new CanteenReportSwipeExcel();
BeanUtils.copyProperties(e, x);
result.add(x);
}
return result;
}
}
@@ -0,0 +1,64 @@
package com.bsd.pjcloud.canteen.util;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.bsd.pjcloud.common.excel.converters.LocalDateStringConverter;
import com.bsd.pjcloud.common.excel.converters.LocalDateTimeStringConverter;
import com.bsd.pjcloud.common.excel.converters.LocalTimeStringConverter;
import com.bsd.pjcloud.common.excel.converters.LongStringConverter;
import com.bsd.pjcloud.common.excel.converters.StringArrayConverter;
import com.bsd.pjcloud.common.excel.handler.DefaultAnalysisEventListener;
import lombok.experimental.UtilityClass;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.util.List;
/**
* 报表演示:按 sheet 名读取 Excel(支持整本多 sheet 工作簿)
*/
@UtilityClass
public class CanteenReportExcelUtils {
@SuppressWarnings("unchecked")
public static <T> List<T> readSheet(MultipartFile file, Class<T> clazz, String preferredSheetName) throws Exception {
byte[] bytes = file.getBytes();
int sheetNo = resolveSheetNo(bytes, preferredSheetName);
DefaultAnalysisEventListener listener = new DefaultAnalysisEventListener();
EasyExcel.read(new ByteArrayInputStream(bytes), clazz, listener)
.registerConverter(LocalDateStringConverter.INSTANCE)
.registerConverter(LocalTimeStringConverter.INSTANCE)
.registerConverter(LocalDateTimeStringConverter.INSTANCE)
.registerConverter(LongStringConverter.INSTANCE)
.registerConverter(StringArrayConverter.INSTANCE)
.sheet(sheetNo)
.headRowNumber(1)
.doRead();
return (List<T>) listener.getList();
}
private static int resolveSheetNo(byte[] bytes, String preferredSheetName) {
if (preferredSheetName == null || preferredSheetName.isEmpty()) {
return 0;
}
ExcelReader reader = EasyExcel.read(new ByteArrayInputStream(bytes)).build();
try {
List<ReadSheet> sheets = reader.excelExecutor().sheetList();
for (ReadSheet s : sheets) {
if (preferredSheetName.equals(s.getSheetName())) {
return s.getSheetNo();
}
}
for (ReadSheet s : sheets) {
if (s.getSheetName() != null && s.getSheetName().contains(preferredSheetName)) {
return s.getSheetNo();
}
}
}
finally {
reader.finish();
}
return 0;
}
}
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.canteen.mapper.CanteenReportFoodEnergyMapper">
<delete id="deletePhysicallyByBizMonth">
DELETE FROM canteen_report_food_energy WHERE biz_month = #{bizMonth}
</delete>
<insert id="insertBatch">
INSERT INTO canteen_report_food_energy (
id, biz_month, biz_date, supplier, receipt_no, category,
product_code, product_name, spec, unit, qty, unit_price, list_price,
price_diff, settle_unit_price, settle_amount, amount, amount_diff,
warehouse, park, classify, energy_amount, labor_amount,
consumable_amount, outsource_amount,
create_by, create_time, update_by, update_time, del_flag, tenant_id
) VALUES
<foreach collection="list" item="e" separator=",">
(
#{e.id}, #{e.bizMonth}, #{e.bizDate}, #{e.supplier}, #{e.receiptNo}, #{e.category},
#{e.productCode}, #{e.productName}, #{e.spec}, #{e.unit}, #{e.qty}, #{e.unitPrice}, #{e.listPrice},
#{e.priceDiff}, #{e.settleUnitPrice}, #{e.settleAmount}, #{e.amount}, #{e.amountDiff},
#{e.warehouse}, #{e.park}, #{e.classify}, #{e.energyAmount}, #{e.laborAmount},
#{e.consumableAmount}, #{e.outsourceAmount},
#{e.createBy}, #{e.createTime}, #{e.updateBy}, #{e.updateTime}, #{e.delFlag}, #{e.tenantId}
)
</foreach>
</insert>
</mapper>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.canteen.mapper.CanteenReportSwipeMapper">
<delete id="deletePhysicallyByBizMonth">
DELETE FROM canteen_report_swipe WHERE biz_month = #{bizMonth}
</delete>
<insert id="insertBatch">
INSERT INTO canteen_report_swipe (
id, biz_month, emp_no, emp_name, legal_entity, dept_name,
consume_area, settle_counter, consume_amount, income_type,
cost_center, cost_category, park, remark_tag,
create_by, create_time, update_by, update_time, del_flag, tenant_id
) VALUES
<foreach collection="list" item="e" separator=",">
(
#{e.id}, #{e.bizMonth}, #{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.createBy}, #{e.createTime}, #{e.updateBy}, #{e.updateTime}, #{e.delFlag}, #{e.tenantId}
)
</foreach>
</insert>
</mapper>
@@ -6,7 +6,7 @@ server:
include-message: always
db:
num: 1
user: ${MYSQL_USER:root}
user: ${MYSQL_USER:app}
password: ${MYSQL_PWD:bsd.1799}
url:
0: jdbc:mysql://${MYSQL_HOST:pjcloud-mysql}:${MYSQL_PORT:2881}/${MYSQL_DB:pjcloudx_config}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true