From 25801fe42ff6821a70eca5475f137ac3726a7283 Mon Sep 17 00:00:00 2001 From: caojia <1301869380@qq.com> Date: Thu, 5 Nov 2026 15:41:20 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=8C=89=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E5=8C=BA=E5=9F=9F=E8=AE=A1=E7=AE=97=E6=AF=9B=E5=88=A9=E5=B9=B6?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B1=87=E6=80=BB=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/PrcProfitReportController.java | 23 +++- .../com/bms/report/ProfitAreaRuleTest.java | 53 +++++++++ .../price/domain/vo/PrcProfitReportQuery.java | 2 + .../com/bms/price/enums/ProfitAreaRule.java | 43 ++++++++ .../service/IPrcProfitReportService.java | 3 + .../impl/PrcProfitReportServiceImpl.java | 21 +++- .../mapper/price/PrcProfitReportMapper.xml | 101 +++++++++--------- bms-front/src/api/report/profit.js | 4 + bms-front/src/views/report/profit/index.vue | 75 +++++++++---- 9 files changed, 248 insertions(+), 77 deletions(-) create mode 100644 bms-back/bms-admin/src/test/java/com/bms/report/ProfitAreaRuleTest.java create mode 100644 bms-back/bms-system/src/main/java/com/bms/price/enums/ProfitAreaRule.java 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 3183d9db..23d94dde 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 @@ -32,6 +32,7 @@ import com.bms.common.utils.uuid.IdUtils; import com.bms.common.utils.poi.ExcelUtil; // 提成功能暂停使用:import com.bms.price.domain.PrcProfitCommissionRate; import com.bms.price.domain.PrcProfitReportExport; +import com.bms.price.enums.ProfitAreaRule; import com.bms.price.domain.vo.PrcProfitReportOrderRow; import com.bms.price.domain.vo.PrcProfitReportProductRow; import com.bms.price.domain.vo.PrcProfitReportQuery; @@ -58,6 +59,14 @@ public class PrcProfitReportController extends BaseController return getDataTable(profitReportService.selectCustomerOptions(keywords)); } + /** 客户区域字典值与毛利规则的映射。 */ + @PreAuthorize("@ss.hasPermi('report:profit:list')") + @GetMapping("/area-rules") + public AjaxResult areaRules() + { + return success(profitReportService.selectAreaRules()); + } + @PreAuthorize("@ss.hasPermi('report:profit:list')") @GetMapping("/summary") public AjaxResult summary(PrcProfitReportQuery query) @@ -118,7 +127,7 @@ public class PrcProfitReportController extends BaseController try { List rows = profitReportService.selectOrderRows(query); - return finishExport(export, rows, PrcProfitReportOrderRow.class, "订单毛利报表"); + return finishExport(export, rows, PrcProfitReportOrderRow.class, "订单毛利报表", query.getProfitRule()); } catch (RuntimeException e) { @@ -136,7 +145,7 @@ public class PrcProfitReportController extends BaseController try { List rows = profitReportService.selectProductRows(query); - return finishExport(export, rows, PrcProfitReportProductRow.class, "产品毛利报表"); + return finishExport(export, rows, PrcProfitReportProductRow.class, "产品毛利报表", query.getProfitRule()); } catch (RuntimeException e) { @@ -200,7 +209,8 @@ public class PrcProfitReportController extends BaseController return export; } - private AjaxResult finishExport(PrcProfitReportExport export, List rows, Class rowType, String sheetName) + private AjaxResult finishExport(PrcProfitReportExport export, List rows, Class rowType, String sheetName, + String profitRule) { Path temp = null; try @@ -208,7 +218,12 @@ public class PrcProfitReportController extends BaseController temp = Files.createTempFile("profit-report-", ".xlsx"); try (OutputStream output = Files.newOutputStream(temp)) { - new ExcelUtil(rowType).exportExcelToStream(rows, sheetName, output); + ExcelUtil excelUtil = new ExcelUtil<>(rowType); + if (ProfitAreaRule.GUANGZHOU.name().equals(profitRule)) + { + excelUtil.hideColumn("costAmount", "redlineGrossProfit", "redlineGrossMarginRate"); + } + excelUtil.exportExcelToStream(rows, sheetName, output); } String objectKey = "report/profit/" + DateUtils.datePath() + "/" + IdUtils.fastSimpleUUID() + ".xlsx"; export.setStorageKey(ossStorage.upload(temp, objectKey, diff --git a/bms-back/bms-admin/src/test/java/com/bms/report/ProfitAreaRuleTest.java b/bms-back/bms-admin/src/test/java/com/bms/report/ProfitAreaRuleTest.java new file mode 100644 index 00000000..297deb98 --- /dev/null +++ b/bms-back/bms-admin/src/test/java/com/bms/report/ProfitAreaRuleTest.java @@ -0,0 +1,53 @@ +package com.bms.report; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; +import com.bms.common.exception.ServiceException; +import com.bms.price.domain.vo.PrcProfitReportQuery; +import com.bms.price.mapper.PrcProfitReportMapper; +import com.bms.price.service.impl.PrcProfitReportServiceImpl; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +class ProfitAreaRuleTest +{ + @Test + void reportQueriesResolveRuleOnServerAndRejectUnknownAreas() + { + AtomicInteger calls = new AtomicInteger(); + PrcProfitReportMapper mapper = (PrcProfitReportMapper) Proxy.newProxyInstance( + getClass().getClassLoader(), new Class[] { PrcProfitReportMapper.class }, + (proxy, method, args) -> { + calls.incrementAndGet(); + return Collections.emptyList(); + }); + PrcProfitReportServiceImpl service = new PrcProfitReportServiceImpl(); + ReflectionTestUtils.setField(service, "profitReportMapper", mapper); + + PrcProfitReportQuery query = new PrcProfitReportQuery(); + query.setCustomerArea(" GZ "); + query.setProfitRule("SHENZHEN"); + service.selectSummary(query); + assertEquals("GZ", query.getCustomerArea()); + assertEquals("GUANGZHOU", query.getProfitRule()); + + query.setCustomerArea("C"); + query.setProfitRule("GUANGZHOU"); + service.selectOrderRows(query); + assertEquals("SHENZHEN", query.getProfitRule()); + service.selectProductRows(query); + assertEquals(3, calls.get()); + + query.setCustomerArea("UNKNOWN"); + assertThrows(ServiceException.class, () -> service.selectProductRows(query)); + query.setCustomerArea(""); + assertThrows(ServiceException.class, () -> service.selectSummary(query)); + assertEquals(3, calls.get()); + assertEquals("SHENZHEN", service.selectAreaRules().get("C")); + assertEquals("GUANGZHOU", service.selectAreaRules().get("GZ")); + } +} 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 170b3f3a..6e62a0bc 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 @@ -26,6 +26,8 @@ public class PrcProfitReportQuery extends BaseEntity private List customerCodes; /** 按客户当前区域筛选。 */ private String customerArea; + /** 服务端根据客户区域确定的计算规则。 */ + private String profitRule; private String customerName; /** 客户名称拆分后的多值查询条件。 */ private List customerNames; diff --git a/bms-back/bms-system/src/main/java/com/bms/price/enums/ProfitAreaRule.java b/bms-back/bms-system/src/main/java/com/bms/price/enums/ProfitAreaRule.java new file mode 100644 index 00000000..5e8629b3 --- /dev/null +++ b/bms-back/bms-system/src/main/java/com/bms/price/enums/ProfitAreaRule.java @@ -0,0 +1,43 @@ +package com.bms.price.enums; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** 客户区域字典值对应的毛利计算规则。 */ +public enum ProfitAreaRule +{ + SHENZHEN("C"), + GUANGZHOU("GZ"); + + private final String customerArea; + + ProfitAreaRule(String customerArea) + { + this.customerArea = customerArea; + } + + public static ProfitAreaRule fromCustomerArea(String customerArea) + { + if (customerArea != null) + { + for (ProfitAreaRule rule : values()) + { + if (rule.customerArea.equals(customerArea.trim())) + { + return rule; + } + } + } + return null; + } + + public static Map areaRules() + { + Map result = new LinkedHashMap<>(); + for (ProfitAreaRule rule : values()) + { + result.put(rule.customerArea, rule.name()); + } + return result; + } +} 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 ad214559..ec23604b 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,6 +1,7 @@ package com.bms.price.service; import java.util.List; +import java.util.Map; import com.bms.customer.customerinfo.domain.BmsCustomerOption; // 提成功能暂停使用:import com.bms.price.domain.PrcProfitCommissionRate; import com.bms.price.domain.PrcProfitReportExport; @@ -14,6 +15,8 @@ public interface IPrcProfitReportService { List selectCustomerOptions(String keywords); + Map selectAreaRules(); + List selectOrderRows(PrcProfitReportQuery query); List selectProductRows(PrcProfitReportQuery query); 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 8ab3023c..594f6d6d 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,6 +3,7 @@ package com.bms.price.service.impl; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; // 提成功能暂停使用:import java.math.BigDecimal; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -19,6 +20,7 @@ import com.bms.price.domain.vo.PrcProfitReportOrderRow; import com.bms.price.domain.vo.PrcProfitReportProductRow; import com.bms.price.domain.vo.PrcProfitReportQuery; import com.bms.price.domain.vo.PrcProfitReportSummary; +import com.bms.price.enums.ProfitAreaRule; import com.bms.price.mapper.PrcProfitReportMapper; import com.bms.price.service.IPrcProfitReportService; @@ -48,6 +50,12 @@ public class PrcProfitReportServiceImpl implements IPrcProfitReportService return customerMapper.selectCustomerOptions(query); } + @Override + public Map selectAreaRules() + { + return ProfitAreaRule.areaRules(); + } + @Override public List selectOrderRows(PrcProfitReportQuery query) { @@ -105,12 +113,23 @@ public class PrcProfitReportServiceImpl implements IPrcProfitReportService { if (query == null) { - return; + throw new ServiceException("请选择客户区域"); } if ("COMMISSION_MISSING".equals(query.getDetailType())) { throw new ServiceException("提成功能已暂停使用"); } + if (query.getCustomerArea() == null || query.getCustomerArea().trim().isEmpty()) + { + throw new ServiceException("请选择客户区域"); + } + ProfitAreaRule rule = ProfitAreaRule.fromCustomerArea(query.getCustomerArea()); + if (rule == null) + { + throw new ServiceException("该客户区域未配置毛利规则"); + } + query.setCustomerArea(query.getCustomerArea().trim()); + query.setProfitRule(rule.name()); query.setOrderNos(splitFilterValues(query.getOrderNo(), "订单号")); query.setCustomerCodes(splitFilterValues(query.getCustomerCode(), "客户代码")); query.setCustomerNames(splitFilterValues(query.getCustomerName(), "客户名称")); 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 75861a98..ac667444 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 @@ -200,8 +200,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - + + + i.sales_amount is not null and i.redline_amount is not null + and not (coalesce(p.billing_fail_status, 0) in (23, 24, 25) and i.sales_amount = 0 and i.redline_amount = 0) + + + + + null as cost_amount, i.redline_amount, + null as redline_gross_profit, null as redline_gross_margin_rate, + i.sales_amount, + case when + then i.sales_amount - i.redline_amount else null end as sales_gross_profit, + case when and i.sales_amount != 0 + then round((i.sales_amount - i.redline_amount) * 100 / i.sales_amount, 4) + else null end as sales_gross_margin_rate + + i.product_amount as cost_amount, i.redline_amount, case when i.product_amount is not null and i.redline_amount is not null @@ -213,11 +230,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 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 - + + - + select p.product_code, p.product_name, p.country_code, p.country_name, + i.currency, count(*) as order_count, + group by p.product_code, p.product_name, p.country_code, p.country_name, i.currency order by p.product_code, p.country_code, i.currency @@ -279,41 +314,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"