✨ 完善毛利分析筛选与导出日志

This commit is contained in:
caojia
2026-10-15 14:37:18 +08:00
parent be2a359698
commit c704021f2f
15 changed files with 534 additions and 39 deletions
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -20,6 +20,8 @@ public class PrcProfitReportSummary implements Serializable
private BigDecimal salesAmount;
private BigDecimal salesGrossProfit;
private BigDecimal salesGrossMarginRate;
/* 提成汇总指标暂停使用。
private BigDecimal commissionAmount;
private Long commissionMissingOrderCount;
*/
}
@@ -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<PrcProfitReportProductRow> selectProductRows(PrcProfitReportQuery query);
List<PrcProfitReportSummary> selectSummary(PrcProfitReportQuery query);
int insertExport(PrcProfitReportExport export);
int markExportSuccess(PrcProfitReportExport export);
int markExportFailed(@Param("exportId") Long exportId, @Param("remark") String remark);
List<PrcProfitReportExport> selectExportsByUser(String exportBy);
PrcProfitReportExport selectExportByIdAndUser(@Param("exportId") Long exportId,
@Param("exportBy") String exportBy);
}
@@ -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<PrcProfitReportSummary> selectSummary(PrcProfitReportQuery query);
int createExport(PrcProfitReportExport export);
int completeExport(PrcProfitReportExport export);
int failExport(Long exportId, String remark);
List<PrcProfitReportExport> selectExports(String exportBy);
PrcProfitReportExport selectExport(Long exportId, String exportBy);
/* 提成比例维护暂停使用。
List<PrcProfitCommissionRate> selectCommissionRates();
void saveCommissionRate(PrcProfitCommissionRate rate);
int deleteCommissionRate(Long rateId);
*/
}
@@ -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<PrcProfitReportExport> 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<PrcProfitCommissionRate> selectCommissionRates()
{
@@ -136,4 +174,5 @@ public class PrcProfitReportServiceImpl implements IPrcProfitReportService
}
return commissionRateMapper.deleteById(rateId);
}
*/
}
@@ -4,6 +4,56 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bms.price.mapper.PrcProfitReportMapper">
<resultMap type="PrcProfitReportExport" id="PrcProfitReportExportResult">
<id property="exportId" column="export_id"/>
<result property="dimension" column="dimension"/>
<result property="fileName" column="file_name"/>
<result property="storageKey" column="storage_key"/>
<result property="rowCount" column="row_count"/>
<result property="exportStatus" column="export_status"/>
<result property="exportBy" column="export_by"/>
<result property="exportTime" column="export_time"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="profitReportExportColumns">
select export_id, dimension, file_name, storage_key, row_count,
export_status, export_by, export_time, remark
from prc_profit_report_export
</sql>
<insert id="insertExport" parameterType="PrcProfitReportExport" useGeneratedKeys="true" keyProperty="exportId">
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}
)
</insert>
<update id="markExportSuccess" parameterType="PrcProfitReportExport">
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>
<update id="markExportFailed">
update prc_profit_report_export
set export_status = 'FAILED', remark = #{remark}
where export_id = #{exportId} and export_status = 'PROCESSING'
</update>
<select id="selectExportsByUser" resultMap="PrcProfitReportExportResult">
<include refid="profitReportExportColumns"/>
where export_by = #{exportBy}
order by export_time desc, export_id desc
</select>
<select id="selectExportByIdAndUser" resultMap="PrcProfitReportExportResult">
<include refid="profitReportExportColumns"/>
where export_id = #{exportId} and export_by = #{exportBy}
</select>
<resultMap type="PrcProfitReportOrderRow" id="PrcProfitReportOrderResult">
<result property="orderNo" column="order_no"/>
<result property="customerCode" column="customer_code"/>
@@ -23,8 +73,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="salesAmount" column="sales_amount"/>
<result property="salesGrossProfit" column="sales_gross_profit"/>
<result property="salesGrossMarginRate" column="sales_gross_margin_rate"/>
<!-- 提成结果字段暂停使用。
<result property="commissionRate" column="commission_rate"/>
<result property="commissionAmount" column="commission_amount"/>
-->
<result property="arBillStatus" column="ar_bill_status"/>
</resultMap>
@@ -42,8 +94,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="salesAmount" column="sales_amount"/>
<result property="salesGrossProfit" column="sales_gross_profit"/>
<result property="salesGrossMarginRate" column="sales_gross_margin_rate"/>
<!-- 提成结果字段暂停使用。
<result property="commissionAmount" column="commission_amount"/>
<result property="commissionMissingOrderCount" column="commission_missing_order_count"/>
-->
<result property="incompleteOrderCount" column="incomplete_order_count"/>
</resultMap>
@@ -58,16 +112,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="salesAmount" column="sales_amount"/>
<result property="salesGrossProfit" column="sales_gross_profit"/>
<result property="salesGrossMarginRate" column="sales_gross_margin_rate"/>
<!-- 提成汇总字段暂停使用。
<result property="commissionAmount" column="commission_amount"/>
<result property="commissionMissingOrderCount" column="commission_missing_order_count"/>
-->
</resultMap>
<sql id="profitReportFromAndWhere">
from prc_income_bill i
inner join prc_order_bill p on p.bill_id = i.bill_id
<!-- 提成表关联暂停使用,毛利报表不再依赖提成配置。
left join prc_profit_commission_rate cr
on cr.belong_saler_code = i.belong_saler_code and cr.status = '0'
-->
<where>
<!-- 不将未匹配产品、国家或重量段的计费失败订单纳入毛利报表;保留历史空值(按成功处理)。 -->
and (p.billing_fail_status is null or p.billing_fail_status not in (1, 2, 3))
<if test="beginBizDate != null">
and p.biz_date &gt;= #{beginBizDate}
</if>
@@ -134,9 +194,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</otherwise>
</choose>
<choose>
<!-- 提成未计算订单下钻暂停使用。
<when test="detailType == 'COMMISSION_MISSING'">
and (cr.commission_rate is null or i.product_amount is null or i.sales_amount is null)
</when>
-->
<when test="detailType == 'INCOMPLETE'">
and (i.product_amount is null or i.redline_amount is null or i.sales_amount is null)
</when>
@@ -161,21 +223,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</where>
</sql>
<!-- 红线毛利 = 产品价 - 红线价;红线毛利率 = 红线毛利 / 红线价 * 100%。汇总先逐单计算再求和。 -->
<sql id="profitExpressions">
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
<!-- 提成比例与金额计算暂停使用;恢复时包含前置逗号。
, cr.commission_rate,
case when cr.commission_rate is not null and i.product_amount is not null and i.sales_amount is not null
then round((i.sales_amount - i.product_amount) * cr.commission_rate / 100, 4) else null end as commission_amount
-->
</sql>
<select id="selectOrderRows" resultMap="PrcProfitReportOrderResult">
@@ -186,6 +251,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="profitExpressions"/>,
p.ar_bill_status
<include refid="profitReportFromAndWhere"/>
<if test="arBillStatus != null and arBillStatus != '' and productDrilldown != true">
and p.ar_bill_status = #{arBillStatus}
</if>
order by p.biz_date desc, p.bill_id desc
</select>
@@ -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 cr.commission_rate is not null and i.product_amount is not null and i.sales_amount is not null
then (i.sales_amount - i.product_amount) * cr.commission_rate / 100 else null end) as commission_amount,
sum(case when cr.commission_rate is null or i.product_amount is null or i.sales_amount is null
then 1 else 0 end) as commission_missing_order_count,
-->
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
<include refid="profitReportFromAndWhere"/>
@@ -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
<!-- 提成汇总计算暂停使用;恢复时包含前置逗号。
, sum(case when cr.commission_rate is not null and i.product_amount is not null and i.sales_amount is not null
then (i.sales_amount - i.product_amount) * cr.commission_rate / 100 else null end) as commission_amount,
sum(case when cr.commission_rate is null or i.product_amount is null or i.sales_amount is null
then 1 else 0 end) as commission_missing_order_count
-->
<include refid="profitReportFromAndWhere"/>
<if test="arBillStatus != null and arBillStatus != ''">
and p.ar_bill_status = #{arBillStatus}
</if>
group by i.currency
order by i.currency
</select>