From c704021f2f62d47043b870dd48abf99882a34209 Mon Sep 17 00:00:00 2001 From: caojia <1301869380@qq.com> Date: Thu, 15 Oct 2026 14:37:18 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E5=AE=8C=E5=96=84=E6=AF=9B?= =?UTF-8?q?=E5=88=A9=E5=88=86=E6=9E=90=E7=AD=9B=E9=80=89=E4=B8=8E=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/PrcProfitReportController.java | 127 +++++++++++++-- .../price/domain/PrcProfitReportExport.java | 29 ++++ .../domain/vo/PrcProfitReportOrderRow.java | 4 +- .../domain/vo/PrcProfitReportProductRow.java | 2 + .../price/domain/vo/PrcProfitReportQuery.java | 4 +- .../domain/vo/PrcProfitReportSummary.java | 2 + .../price/mapper/PrcProfitReportMapper.java | 13 ++ .../service/IPrcProfitReportService.java | 15 +- .../impl/PrcProfitReportServiceImpl.java | 49 +++++- .../mapper/price/PrcProfitReportMapper.xml | 95 +++++++++-- bms-back/sql/bms_init.sql | 16 ++ .../price/prc_profit_report_export_log.sql | 15 ++ bms-back/sql/price/profit_report.sql | 16 ++ bms-front/src/api/report/profit.js | 35 +++- bms-front/src/views/report/profit/index.vue | 151 +++++++++++++++++- 15 files changed, 534 insertions(+), 39 deletions(-) create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/domain/PrcProfitReportExport.java create mode 100644 bms-back/sql/price/prc_profit_report_export_log.sql diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/report/PrcProfitReportController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/report/PrcProfitReportController.java index aaa28ea3..73e2512e 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/report/PrcProfitReportController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/report/PrcProfitReportController.java @@ -1,23 +1,31 @@ package com.bms.web.controller.report; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.DeleteMapping; +// 提成功能暂停使用:import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.bms.common.annotation.Log; +import com.bms.common.config.BmsConfig; import com.bms.common.core.controller.BaseController; import com.bms.common.core.domain.AjaxResult; import com.bms.common.core.page.TableDataInfo; import com.bms.common.enums.BusinessType; +import com.bms.common.exception.ServiceException; +import com.bms.common.utils.file.FileUtils; import com.bms.common.utils.poi.ExcelUtil; -import com.bms.price.domain.PrcProfitCommissionRate; +// 提成功能暂停使用:import com.bms.price.domain.PrcProfitCommissionRate; +import com.bms.price.domain.PrcProfitReportExport; import com.bms.price.domain.vo.PrcProfitReportOrderRow; import com.bms.price.domain.vo.PrcProfitReportProductRow; import com.bms.price.domain.vo.PrcProfitReportQuery; @@ -39,6 +47,7 @@ public class PrcProfitReportController extends BaseController return success(profitReportService.selectSummary(query)); } + /* 提成比例查询、维护、删除接口暂停使用,保留代码便于恢复。 @PreAuthorize("@ss.hasPermi('report:profit:commission:list')") @GetMapping("/commission-rates") public AjaxResult commissionRates() @@ -62,6 +71,7 @@ public class PrcProfitReportController extends BaseController { return toAjax(profitReportService.deleteCommissionRate(rateId)); } + */ @PreAuthorize("@ss.hasPermi('report:profit:list')") @GetMapping("/orders") @@ -84,20 +94,115 @@ public class PrcProfitReportController extends BaseController @Log(title = "毛利报表-订单维度", businessType = BusinessType.EXPORT) @PreAuthorize("@ss.hasPermi('report:profit:export')") @PostMapping("/orders/export") - public void exportOrders(HttpServletResponse response, PrcProfitReportQuery query) + public AjaxResult exportOrders(PrcProfitReportQuery query) { - List rows = profitReportService.selectOrderRows(query); - new ExcelUtil(PrcProfitReportOrderRow.class) - .exportExcel(response, rows, "订单毛利报表"); + PrcProfitReportExport export = beginExport("ORDER", "订单维度毛利分析"); + try + { + List rows = profitReportService.selectOrderRows(query); + AjaxResult file = new ExcelUtil(PrcProfitReportOrderRow.class) + .exportExcel(rows, "订单毛利报表"); + return finishExport(export, rows.size(), file); + } + catch (RuntimeException e) + { + failExport(export, e); + throw e; + } } @Log(title = "毛利报表-产品维度", businessType = BusinessType.EXPORT) @PreAuthorize("@ss.hasPermi('report:profit:export')") @PostMapping("/products/export") - public void exportProducts(HttpServletResponse response, PrcProfitReportQuery query) + public AjaxResult exportProducts(PrcProfitReportQuery query) { - List rows = profitReportService.selectProductRows(query); - new ExcelUtil(PrcProfitReportProductRow.class) - .exportExcel(response, rows, "产品毛利报表"); + PrcProfitReportExport export = beginExport("PRODUCT", "产品维度毛利分析"); + try + { + List rows = profitReportService.selectProductRows(query); + AjaxResult file = new ExcelUtil(PrcProfitReportProductRow.class) + .exportExcel(rows, "产品毛利报表"); + return finishExport(export, rows.size(), file); + } + catch (RuntimeException e) + { + failExport(export, e); + throw e; + } + } + + @PreAuthorize("@ss.hasPermi('report:profit:export')") + @GetMapping("/exports") + public TableDataInfo exports() + { + startPage(); + return getDataTable(profitReportService.selectExports(getUsername())); + } + + @PreAuthorize("@ss.hasPermi('report:profit:export')") + @GetMapping("/exports/{exportId}/download") + public void downloadExport(@PathVariable Long exportId, HttpServletResponse response) throws Exception + { + PrcProfitReportExport export = profitReportService.selectExport(exportId, getUsername()); + if (export == null || !"SUCCESS".equals(export.getExportStatus()) + || export.getStorageKey() == null || !FileUtils.checkAllowDownload(export.getStorageKey())) + { + throw new ServiceException("导出文件不存在或尚未生成成功"); + } + + Path downloadRoot = Paths.get(BmsConfig.getDownloadPath()).toAbsolutePath().normalize(); + Path filePath = downloadRoot.resolve(export.getStorageKey()).normalize(); + if (!filePath.startsWith(downloadRoot) || !Files.isRegularFile(filePath)) + { + throw new ServiceException("导出文件不存在或路径非法"); + } + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + FileUtils.setAttachmentResponseHeader(response, export.getFileName()); + FileUtils.writeBytes(filePath.toString(), response.getOutputStream()); + } + + private PrcProfitReportExport beginExport(String dimension, String filePrefix) + { + PrcProfitReportExport export = new PrcProfitReportExport(); + export.setDimension(dimension); + export.setFileName(filePrefix + "_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".xlsx"); + export.setExportStatus("PROCESSING"); + export.setExportBy(getUsername()); + if (profitReportService.createExport(export) != 1) + { + throw new ServiceException("创建导出日志失败"); + } + return export; + } + + private AjaxResult finishExport(PrcProfitReportExport export, int rowCount, AjaxResult file) + { + Object storageKey = file.get("msg"); + if (storageKey == null || String.valueOf(storageKey).isBlank()) + { + throw new ServiceException("导出文件生成失败"); + } + export.setStorageKey(String.valueOf(storageKey)); + export.setRowCount(rowCount); + export.setExportStatus("SUCCESS"); + if (profitReportService.completeExport(export) != 1) + { + throw new ServiceException("保存导出日志失败"); + } + return success(export); + } + + private void failExport(PrcProfitReportExport export, RuntimeException e) + { + String message = e.getMessage(); + if (message == null || message.isBlank()) + { + message = "导出失败"; + } + if (message.length() > 500) + { + message = message.substring(0, 500); + } + profitReportService.failExport(export.getExportId(), message); } } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcProfitReportExport.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcProfitReportExport.java new file mode 100644 index 00000000..af5bf004 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/PrcProfitReportExport.java @@ -0,0 +1,29 @@ +package com.bms.price.domain; + +import java.io.Serializable; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +/** 毛利报表导出日志。 */ +@Data +public class PrcProfitReportExport implements Serializable +{ + private static final long serialVersionUID = 1L; + + private Long exportId; + /** PRODUCT 产品维度,ORDER 订单维度。 */ + private String dimension; + private String fileName; + /** 服务器下载目录下的相对文件名,不返回给前端。 */ + @JsonIgnore + private String storageKey; + private Integer rowCount; + /** PROCESSING 生成中,SUCCESS 成功,FAILED 失败。 */ + private String exportStatus; + private String exportBy; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date exportTime; + private String remark; +} diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportOrderRow.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportOrderRow.java index 9a80b6c9..87f15f9b 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportOrderRow.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportOrderRow.java @@ -50,10 +50,12 @@ public class PrcProfitReportOrderRow implements Serializable private BigDecimal salesGrossProfit; @Excel(name = "销售毛利率(%)", scale = 4, width = 18) private BigDecimal salesGrossMarginRate; + /* 提成字段及 Excel 导出列暂停使用。 @Excel(name = "提成比例(%)", scale = 4, width = 16) private BigDecimal commissionRate; @Excel(name = "提成金额", scale = 4, width = 16) private BigDecimal commissionAmount; - @Excel(name = "应收状态", width = 14) + */ + @Excel(name = "应收状态", readConverterExp = "UNBILLED=未出账,PARTIAL=部分出账,BILLED=已出账", width = 14) private String arBillStatus; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportProductRow.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportProductRow.java index 03b3e454..2791b7b2 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportProductRow.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportProductRow.java @@ -37,10 +37,12 @@ public class PrcProfitReportProductRow implements Serializable private BigDecimal salesGrossProfit; @Excel(name = "销售毛利率(%)", scale = 4, width = 18) private BigDecimal salesGrossMarginRate; + /* 提成字段及 Excel 导出列暂停使用。 @Excel(name = "提成金额", scale = 4, width = 16) private BigDecimal commissionAmount; @Excel(name = "未计算提成订单数", width = 20) private Long commissionMissingOrderCount; + */ @Excel(name = "产品价或价格缺失订单数", width = 22) private Long incompleteOrderCount; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportQuery.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportQuery.java index bba53666..a7d00e2d 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportQuery.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportQuery.java @@ -31,8 +31,10 @@ public class PrcProfitReportQuery extends BaseEntity private String productCode; private String countryCode; private String currency; + /** 订单维度应收状态筛选:UNBILLED、PARTIAL、BILLED。 */ + private String arBillStatus; /** 是否按产品维度分组值下钻订单明细,允许分组字段为空。 */ private Boolean productDrilldown; - /** 订单明细弹窗的筛选类型:ALL、COMMISSION_MISSING、INCOMPLETE。 */ + /** 订单明细弹窗的筛选类型:ALL、INCOMPLETE;COMMISSION_MISSING 暂停使用。 */ private String detailType; } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportSummary.java b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportSummary.java index 337ec0e4..b4428398 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportSummary.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/domain/vo/PrcProfitReportSummary.java @@ -20,6 +20,8 @@ public class PrcProfitReportSummary implements Serializable private BigDecimal salesAmount; private BigDecimal salesGrossProfit; private BigDecimal salesGrossMarginRate; + /* 提成汇总指标暂停使用。 private BigDecimal commissionAmount; private Long commissionMissingOrderCount; + */ } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcProfitReportMapper.java b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcProfitReportMapper.java index c9137889..28ed98a3 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcProfitReportMapper.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/mapper/PrcProfitReportMapper.java @@ -1,6 +1,8 @@ package com.bms.price.mapper; import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.bms.price.domain.PrcProfitReportExport; import com.bms.price.domain.vo.PrcProfitReportOrderRow; import com.bms.price.domain.vo.PrcProfitReportProductRow; import com.bms.price.domain.vo.PrcProfitReportQuery; @@ -14,4 +16,15 @@ public interface PrcProfitReportMapper List selectProductRows(PrcProfitReportQuery query); List selectSummary(PrcProfitReportQuery query); + + int insertExport(PrcProfitReportExport export); + + int markExportSuccess(PrcProfitReportExport export); + + int markExportFailed(@Param("exportId") Long exportId, @Param("remark") String remark); + + List selectExportsByUser(String exportBy); + + PrcProfitReportExport selectExportByIdAndUser(@Param("exportId") Long exportId, + @Param("exportBy") String exportBy); } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcProfitReportService.java b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcProfitReportService.java index 3877c9ac..7da8d988 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcProfitReportService.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/IPrcProfitReportService.java @@ -1,7 +1,8 @@ package com.bms.price.service; import java.util.List; -import com.bms.price.domain.PrcProfitCommissionRate; +// 提成功能暂停使用:import com.bms.price.domain.PrcProfitCommissionRate; +import com.bms.price.domain.PrcProfitReportExport; import com.bms.price.domain.vo.PrcProfitReportOrderRow; import com.bms.price.domain.vo.PrcProfitReportProductRow; import com.bms.price.domain.vo.PrcProfitReportQuery; @@ -16,9 +17,21 @@ public interface IPrcProfitReportService List selectSummary(PrcProfitReportQuery query); + int createExport(PrcProfitReportExport export); + + int completeExport(PrcProfitReportExport export); + + int failExport(Long exportId, String remark); + + List selectExports(String exportBy); + + PrcProfitReportExport selectExport(Long exportId, String exportBy); + + /* 提成比例维护暂停使用。 List selectCommissionRates(); void saveCommissionRate(PrcProfitCommissionRate rate); int deleteCommissionRate(Long rateId); + */ } diff --git a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcProfitReportServiceImpl.java b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcProfitReportServiceImpl.java index dbb317a1..b9256126 100644 --- a/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcProfitReportServiceImpl.java +++ b/bms-back/bms-system/src/main/java/com/bms/price/service/impl/PrcProfitReportServiceImpl.java @@ -3,15 +3,16 @@ package com.bms.price.service.impl; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; -import java.math.BigDecimal; +// 提成功能暂停使用:import java.math.BigDecimal; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +// 提成功能暂停使用:import org.springframework.transaction.annotation.Transactional; import com.bms.common.annotation.DataScope; import com.bms.common.exception.ServiceException; -import com.bms.common.utils.SecurityUtils; -import com.bms.price.domain.PrcProfitCommissionRate; -import com.bms.price.mapper.PrcProfitCommissionRateMapper; +// 提成功能暂停使用:import com.bms.common.utils.SecurityUtils; +// 提成功能暂停使用:import com.bms.price.domain.PrcProfitCommissionRate; +// 提成功能暂停使用:import com.bms.price.mapper.PrcProfitCommissionRateMapper; +import com.bms.price.domain.PrcProfitReportExport; import com.bms.price.domain.vo.PrcProfitReportOrderRow; import com.bms.price.domain.vo.PrcProfitReportProductRow; import com.bms.price.domain.vo.PrcProfitReportQuery; @@ -28,8 +29,10 @@ public class PrcProfitReportServiceImpl implements IPrcProfitReportService @Autowired private PrcProfitReportMapper profitReportMapper; + /* 提成配置依赖暂停使用。 @Autowired private PrcProfitCommissionRateMapper commissionRateMapper; + */ @Override @DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = "report:profit:list") @@ -55,6 +58,36 @@ public class PrcProfitReportServiceImpl implements IPrcProfitReportService return profitReportMapper.selectSummary(query); } + @Override + public int createExport(PrcProfitReportExport export) + { + return profitReportMapper.insertExport(export); + } + + @Override + public int completeExport(PrcProfitReportExport export) + { + return profitReportMapper.markExportSuccess(export); + } + + @Override + public int failExport(Long exportId, String remark) + { + return profitReportMapper.markExportFailed(exportId, remark); + } + + @Override + public List selectExports(String exportBy) + { + return profitReportMapper.selectExportsByUser(exportBy); + } + + @Override + public PrcProfitReportExport selectExport(Long exportId, String exportBy) + { + return profitReportMapper.selectExportByIdAndUser(exportId, exportBy); + } + /** 将报表的多行输入拆分为去重后的查询条件,支持换行、空格、逗号和分号分隔。 */ private void normalizeMultiValueFilters(PrcProfitReportQuery query) { @@ -62,6 +95,10 @@ public class PrcProfitReportServiceImpl implements IPrcProfitReportService { return; } + if ("COMMISSION_MISSING".equals(query.getDetailType())) + { + throw new ServiceException("提成功能已暂停使用"); + } query.setOrderNos(splitFilterValues(query.getOrderNo(), "订单号")); query.setCustomerCodes(splitFilterValues(query.getCustomerCode(), "客户代码")); query.setCustomerNames(splitFilterValues(query.getCustomerName(), "客户名称")); @@ -88,6 +125,7 @@ public class PrcProfitReportServiceImpl implements IPrcProfitReportService return new ArrayList<>(values); } + /* 提成比例查询、保存、删除暂停使用,保留原逻辑便于恢复。 @Override public List selectCommissionRates() { @@ -136,4 +174,5 @@ public class PrcProfitReportServiceImpl implements IPrcProfitReportService } return commissionRateMapper.deleteById(rateId); } + */ } diff --git a/bms-back/bms-system/src/main/resources/mapper/price/PrcProfitReportMapper.xml b/bms-back/bms-system/src/main/resources/mapper/price/PrcProfitReportMapper.xml index 5db9c8ca..15d769c9 100644 --- a/bms-back/bms-system/src/main/resources/mapper/price/PrcProfitReportMapper.xml +++ b/bms-back/bms-system/src/main/resources/mapper/price/PrcProfitReportMapper.xml @@ -4,6 +4,56 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> + + + + + + + + + + + + + + select export_id, dimension, file_name, storage_key, row_count, + export_status, export_by, export_time, remark + from prc_profit_report_export + + + + insert into prc_profit_report_export ( + dimension, file_name, storage_key, row_count, export_status, export_by, export_time, remark + ) values ( + #{dimension}, #{fileName}, #{storageKey}, #{rowCount}, #{exportStatus}, #{exportBy}, sysdate(), #{remark} + ) + + + + update prc_profit_report_export + set file_name = #{fileName}, storage_key = #{storageKey}, row_count = #{rowCount}, + export_status = 'SUCCESS', remark = null + where export_id = #{exportId} and export_status = 'PROCESSING' + + + + update prc_profit_report_export + set export_status = 'FAILED', remark = #{remark} + where export_id = #{exportId} and export_status = 'PROCESSING' + + + + + + @@ -23,8 +73,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -42,8 +94,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -58,16 +112,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + from prc_income_bill i inner join prc_order_bill p on p.bill_id = i.bill_id + + + and (p.billing_fail_status is null or p.billing_fail_status not in (1, 2, 3)) and p.biz_date >= #{beginBizDate} @@ -134,9 +194,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + and (i.product_amount is null or i.redline_amount is null or i.sales_amount is null) @@ -161,21 +223,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + i.product_amount as cost_amount, i.redline_amount, case when i.product_amount is not null and i.redline_amount is not null - then i.redline_amount - i.product_amount else null end as redline_gross_profit, + then i.product_amount - i.redline_amount else null end as redline_gross_profit, case when i.product_amount is not null and i.redline_amount is not null and i.redline_amount != 0 - then round((i.redline_amount - i.product_amount) * 100 / i.redline_amount, 4) else null end as redline_gross_margin_rate, + then round((i.product_amount - i.redline_amount) * 100 / i.redline_amount, 4) else null end as redline_gross_margin_rate, i.sales_amount, case when i.product_amount is not null and i.sales_amount is not null then i.sales_amount - i.product_amount else null end as sales_gross_profit, case when i.product_amount is not null and i.sales_amount is not null and i.sales_amount != 0 - then round((i.sales_amount - i.product_amount) * 100 / i.sales_amount, 4) else null end as sales_gross_margin_rate, - cr.commission_rate, + then round((i.sales_amount - i.product_amount) * 100 / i.sales_amount, 4) else null end as sales_gross_margin_rate + @@ -195,7 +263,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" sum(i.product_amount) as cost_amount, sum(i.redline_amount) as redline_amount, sum(case when i.product_amount is not null and i.redline_amount is not null - then i.redline_amount - i.product_amount else null end) as redline_gross_profit, + then i.product_amount - i.redline_amount else null end) as redline_gross_profit, case when sum(case when i.product_amount is not null and i.redline_amount is not null then i.redline_amount else null end) is null or sum(case when i.product_amount is not null and i.redline_amount is not null @@ -203,7 +271,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" then null else round( sum(case when i.product_amount is not null and i.redline_amount is not null - then i.redline_amount - i.product_amount else null end) * 100 / + then i.product_amount - i.redline_amount else null end) * 100 / sum(case when i.product_amount is not null and i.redline_amount is not null then i.redline_amount else null end), 4) end as redline_gross_margin_rate, @@ -221,10 +289,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" sum(case when i.product_amount is not null and i.sales_amount is not null then i.sales_amount else null end), 4) end as sales_gross_margin_rate, + sum(case when i.product_amount is null or i.redline_amount is null or i.sales_amount is null then 1 else 0 end) as incomplete_order_count @@ -239,7 +309,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" sum(i.product_amount) as cost_amount, sum(i.redline_amount) as redline_amount, sum(case when i.product_amount is not null and i.redline_amount is not null - then i.redline_amount - i.product_amount else null end) as redline_gross_profit, + then i.product_amount - i.redline_amount else null end) as redline_gross_profit, case when sum(case when i.product_amount is not null and i.redline_amount is not null then i.redline_amount else null end) is null or sum(case when i.product_amount is not null and i.redline_amount is not null @@ -247,7 +317,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" then null else round( sum(case when i.product_amount is not null and i.redline_amount is not null - then i.redline_amount - i.product_amount else null end) * 100 / + then i.product_amount - i.redline_amount else null end) * 100 / sum(case when i.product_amount is not null and i.redline_amount is not null then i.redline_amount else null end), 4) end as redline_gross_margin_rate, @@ -264,12 +334,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" then i.sales_amount - i.product_amount else null end) * 100 / sum(case when i.product_amount is not null and i.sales_amount is not null then i.sales_amount else null end), 4) - end as sales_gross_margin_rate, - sum(case when cr.commission_rate is not null and i.product_amount is not null and i.sales_amount is not null + end as sales_gross_margin_rate + + + and p.ar_bill_status = #{arBillStatus} + group by i.currency order by i.currency diff --git a/bms-back/sql/bms_init.sql b/bms-back/sql/bms_init.sql index 46b109d1..8d88e607 100644 --- a/bms-back/sql/bms_init.sql +++ b/bms-back/sql/bms_init.sql @@ -2326,3 +2326,19 @@ CREATE TABLE `test` ( SET FOREIGN_KEY_CHECKS = 1; +-- 毛利分析导出日志;已有环境请执行 sql/price/prc_profit_report_export_log.sql。 +CREATE TABLE IF NOT EXISTS `prc_profit_report_export` ( + `export_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '毛利报表导出ID', + `dimension` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PRODUCT产品维度,ORDER订单维度', + `file_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '下载文件名', + `storage_key` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '下载目录下的文件Key', + `row_count` int(11) DEFAULT NULL COMMENT '导出记录数', + `export_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PROCESSING生成中,SUCCESS成功,FAILED失败', + `export_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '导出人', + `export_time` datetime NOT NULL COMMENT '导出时间', + `remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '失败原因', + PRIMARY KEY (`export_id`), + KEY `idx_profit_report_export_user_time` (`export_by`, `export_time`), + KEY `idx_profit_report_export_status` (`export_status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='毛利报表导出日志'; + diff --git a/bms-back/sql/price/prc_profit_report_export_log.sql b/bms-back/sql/price/prc_profit_report_export_log.sql new file mode 100644 index 00000000..7dad1026 --- /dev/null +++ b/bms-back/sql/price/prc_profit_report_export_log.sql @@ -0,0 +1,15 @@ +-- 毛利分析产品、订单维度导出日志及历史文件索引。 +CREATE TABLE IF NOT EXISTS `prc_profit_report_export` ( + `export_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '毛利报表导出ID', + `dimension` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PRODUCT产品维度,ORDER订单维度', + `file_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '下载文件名', + `storage_key` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '下载目录下的文件Key', + `row_count` int(11) DEFAULT NULL COMMENT '导出记录数', + `export_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PROCESSING生成中,SUCCESS成功,FAILED失败', + `export_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '导出人', + `export_time` datetime NOT NULL COMMENT '导出时间', + `remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '失败原因', + PRIMARY KEY (`export_id`), + KEY `idx_profit_report_export_user_time` (`export_by`, `export_time`), + KEY `idx_profit_report_export_status` (`export_status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='毛利报表导出日志'; diff --git a/bms-back/sql/price/profit_report.sql b/bms-back/sql/price/profit_report.sql index 214568a2..f252a01f 100644 --- a/bms-back/sql/price/profit_report.sql +++ b/bms-back/sql/price/profit_report.sql @@ -16,6 +16,22 @@ CREATE TABLE IF NOT EXISTS `prc_profit_commission_rate` ( KEY `idx_profit_commission_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='毛利报表业务员固定提成比例'; +-- 毛利分析产品、订单维度导出日志及历史文件索引。 +CREATE TABLE IF NOT EXISTS `prc_profit_report_export` ( + `export_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '毛利报表导出ID', + `dimension` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PRODUCT产品维度,ORDER订单维度', + `file_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '下载文件名', + `storage_key` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '下载目录下的文件Key', + `row_count` int(11) DEFAULT NULL COMMENT '导出记录数', + `export_status` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'PROCESSING生成中,SUCCESS成功,FAILED失败', + `export_by` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '导出人', + `export_time` datetime NOT NULL COMMENT '导出时间', + `remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '失败原因', + PRIMARY KEY (`export_id`), + KEY `idx_profit_report_export_user_time` (`export_by`, `export_time`), + KEY `idx_profit_report_export_status` (`export_status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='毛利报表导出日志'; + SET @index_exists := ( SELECT COUNT(1) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prc_order_bill' diff --git a/bms-front/src/api/report/profit.js b/bms-front/src/api/report/profit.js index efcee09d..76dcecbe 100644 --- a/bms-front/src/api/report/profit.js +++ b/bms-front/src/api/report/profit.js @@ -1,4 +1,15 @@ -import request, { download } from '@/utils/request' +import request from '@/utils/request' +import { tansParams } from '@/utils/bms' + +function createProfitExport(url, params) { + return request({ + url, + method: 'post', + data: params, + transformRequest: [tansParams], + headers: { 'Content-Type': 'application/x-www-form-urlencoded' } + }) +} export function listProfitSummary(params) { return request({ @@ -25,13 +36,30 @@ export function listProfitProducts(params) { } export function exportProfitOrders(params) { - return download('/report/profit/orders/export', params, '订单毛利报表.xlsx') + return createProfitExport('/report/profit/orders/export', params) } export function exportProfitProducts(params) { - return download('/report/profit/products/export', params, '产品毛利报表.xlsx') + return createProfitExport('/report/profit/products/export', params) } +export function listProfitExports(params) { + return request({ + url: '/report/profit/exports', + method: 'get', + params + }) +} + +export function downloadProfitExport(exportId) { + return request({ + url: '/report/profit/exports/' + exportId + '/download', + method: 'get', + responseType: 'blob' + }) +} + +/* 提成功能暂停使用,保留接口定义便于恢复。 export function listProfitCommissionRates() { return request({ url: '/report/profit/commission-rates', @@ -53,3 +81,4 @@ export function deleteProfitCommissionRate(rateId) { method: 'delete' }) } +*/ diff --git a/bms-front/src/views/report/profit/index.vue b/bms-front/src/views/report/profit/index.vue index f18f4b78..f7eea0bc 100644 --- a/bms-front/src/views/report/profit/index.vue +++ b/bms-front/src/views/report/profit/index.vue @@ -65,6 +65,15 @@ /> + + + + + + + + +
搜索 @@ -97,15 +106,19 @@
销售毛利
{{ money(summary.salesGrossProfit) }} {{ rate(summary.salesGrossMarginRate) }}
+
价格缺失订单
{{ summary.incompleteOrderCount || 0 }}
+
@@ -117,11 +130,18 @@
+ + 导出日志 + @@ -177,8 +199,10 @@ + @@ -223,6 +247,37 @@ + + + + + + + + + + + + + + + + + + + + + + +
@@ -661,6 +796,7 @@ handleSearch() padding-bottom: 8px; } +/* 提成设置弹窗样式暂停使用。 .commission-toolbar { display: flex; align-items: center; @@ -673,6 +809,7 @@ handleSearch() color: var(--el-text-color-secondary); font-size: 13px; } +*/ @media (max-width: 768px) { .summary-list {