This commit is contained in:
van
2026-04-05 21:34:40 +08:00
parent 8a77598c88
commit 9e0c6d88b1
9 changed files with 257 additions and 6 deletions

View File

@@ -24,6 +24,7 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.jarvis.domain.JDOrder;
import com.ruoyi.jarvis.domain.dto.JDOrderSimpleDTO;
import com.ruoyi.jarvis.service.IJDOrderProfitService;
import com.ruoyi.jarvis.service.IJDOrderService;
import com.ruoyi.jarvis.service.IInstructionService;
import com.ruoyi.common.utils.poi.ExcelUtil;
@@ -40,16 +41,19 @@ public class JDOrderListController extends BaseController
{
private final IJDOrderService jdOrderService;
private final IJDOrderProfitService jdOrderProfitService;
private final IOrderRowsService orderRowsService;
private final IInstructionService instructionService;
private final GroupRebateExcelImportService groupRebateExcelImportService;
private final IGroupRebateExcelUploadService groupRebateExcelUploadService;
public JDOrderListController(IJDOrderService jdOrderService, IOrderRowsService orderRowsService,
public JDOrderListController(IJDOrderService jdOrderService, IJDOrderProfitService jdOrderProfitService,
IOrderRowsService orderRowsService,
IInstructionService instructionService,
GroupRebateExcelImportService groupRebateExcelImportService,
IGroupRebateExcelUploadService groupRebateExcelUploadService) {
this.jdOrderService = jdOrderService;
this.jdOrderProfitService = jdOrderProfitService;
this.orderRowsService = orderRowsService;
this.instructionService = instructionService;
this.groupRebateExcelImportService = groupRebateExcelImportService;
@@ -279,6 +283,8 @@ public class JDOrderListController extends BaseController
@PutMapping
public AjaxResult edit(@RequestBody JDOrder jdOrder)
{
jdOrderProfitService.recalculate(jdOrder);
jdOrder.getParams().put("applyProfitFields", Boolean.TRUE);
return toAjax(jdOrderService.updateJDOrder(jdOrder));
}
@@ -373,6 +379,43 @@ public class JDOrderListController extends BaseController
* 一次性批量更新历史订单将赔付金额大于0的订单标记为后返到账
* 此方法只应执行一次,用于处理历史数据
*/
/**
* 按 ID 批量重算售价(自动从型号配置回填)与利润(清除手动锁定后按规则计算)
*/
@Log(title = "JD订单批量重算利润", businessType = BusinessType.UPDATE)
@PostMapping("/tools/recalc-profit")
@SuppressWarnings("unchecked")
public AjaxResult recalcProfitBatch(@RequestBody(required = false) Map<String, Object> body) {
if (body == null || !body.containsKey("ids")) {
return AjaxResult.error("请传入 ids 数组");
}
Object raw = body.get("ids");
if (!(raw instanceof List)) {
return AjaxResult.error("ids 须为数组");
}
List<?> idList = (List<?>) raw;
if (idList.isEmpty()) {
return AjaxResult.error("ids 不能为空");
}
int affected = 0;
for (Object o : idList) {
if (o == null) {
continue;
}
long id = ((Number) o).longValue();
JDOrder order = jdOrderService.selectJDOrderById(id);
if (order == null) {
continue;
}
order.setProfitManual(0);
order.setSellingPriceManual(0);
jdOrderProfitService.recalculate(order);
order.getParams().put("applyProfitFields", Boolean.TRUE);
affected += jdOrderService.updateJDOrder(order);
}
return AjaxResult.success("已更新 " + affected + " 条订单的售价/利润字段");
}
@Log(title = "批量标记后返到账", businessType = BusinessType.UPDATE)
@RequestMapping(value = "/tools/batch-mark-rebate-received", method = {RequestMethod.POST, RequestMethod.GET})
public AjaxResult batchMarkRebateReceivedForCompensation() {

View File

@@ -154,6 +154,21 @@ public class JDOrder extends BaseEntity {
/** 后返备注中是否存在异常项1是 0否便于列表筛选 */
private Integer rebateRemarkHasAbnormal;
/** 售价渠道direct 直款xianyu 闲鱼(仅 F 单使用) */
private String sellingPriceType;
/** 售价(对客成交价,可手动改) */
private Double sellingPrice;
/** 利润(可手动改;非 H-TF/F 一般为空) */
private Double profit;
/** 售价是否手动锁定1 是:不再按型号配置自动回填) */
private Integer sellingPriceManual;
/** 利润是否手动锁定1 是:保存时不再自动重算) */
private Integer profitManual;
}

View File

@@ -27,6 +27,12 @@ public class ProductJdConfig extends BaseEntity
/** 佣金(支付) - 支付给下单人的佣金 */
private BigDecimal commissionPay;
/** 参考售价(直款渠道) */
private BigDecimal sellingPriceDirect;
/** 参考售价(闲鱼渠道,订单侧仍会 ×0.984 计算实收) */
private BigDecimal sellingPriceXianyu;
public ProductJdConfig() {
}
@@ -76,6 +82,22 @@ public class ProductJdConfig extends BaseEntity
this.commissionPay = commissionPay;
}
public BigDecimal getSellingPriceDirect() {
return sellingPriceDirect;
}
public void setSellingPriceDirect(BigDecimal sellingPriceDirect) {
this.sellingPriceDirect = sellingPriceDirect;
}
public BigDecimal getSellingPriceXianyu() {
return sellingPriceXianyu;
}
public void setSellingPriceXianyu(BigDecimal sellingPriceXianyu) {
this.sellingPriceXianyu = sellingPriceXianyu;
}
@Override
public String toString() {
return "ProductJdConfig{" +
@@ -84,6 +106,8 @@ public class ProductJdConfig extends BaseEntity
", commission=" + commission +
", commissionReceive=" + commissionReceive +
", commissionPay=" + commissionPay +
", sellingPriceDirect=" + sellingPriceDirect +
", sellingPriceXianyu=" + sellingPriceXianyu +
'}';
}
}

View File

@@ -0,0 +1,15 @@
package com.ruoyi.jarvis.service;
import com.ruoyi.jarvis.domain.JDOrder;
/**
* 订单利润/售价:按分销标识规则计算并写回订单对象(由列表保存前调用)。
*/
public interface IJDOrderProfitService {
/**
* 根据分销标识、型号配置、手动标记等,填充售价(自动时)并计算利润。
* 会修改传入的 {@code order}。
*/
void recalculate(JDOrder order);
}

View File

@@ -0,0 +1,104 @@
package com.ruoyi.jarvis.service.impl;
import com.ruoyi.jarvis.domain.JDOrder;
import com.ruoyi.jarvis.domain.ProductJdConfig;
import com.ruoyi.jarvis.service.IJDOrderProfitService;
import com.ruoyi.jarvis.service.IProductJdConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
@Service
public class JDOrderProfitServiceImpl implements IJDOrderProfitService {
private static final double XIANYU_NET_FACTOR = 0.984;
@Autowired
private IProductJdConfigService productJdConfigService;
@Override
public void recalculate(JDOrder order) {
if (order == null) {
return;
}
String mark = order.getDistributionMark() == null ? "" : order.getDistributionMark().trim();
boolean profitLocked = order.getProfitManual() != null && order.getProfitManual() == 1;
boolean sellingLocked = order.getSellingPriceManual() != null && order.getSellingPriceManual() == 1;
if ("H-TF".equals(mark)) {
order.setSellingPriceType(null);
order.setSellingPrice(null);
if (!profitLocked) {
String buyer = order.getBuyer();
boolean fan = buyer != null && buyer.trim().startsWith("凡-");
order.setProfit(fan ? 65.0 : 15.0);
}
return;
}
if ("F".equals(mark)) {
if (!sellingLocked) {
fillSellingPriceFromConfig(order);
}
if (!profitLocked) {
computeProfitForF(order);
}
return;
}
if (!profitLocked) {
order.setProfit(null);
}
}
private void fillSellingPriceFromConfig(JDOrder order) {
String type = order.getSellingPriceType();
if (type == null || type.isEmpty()) {
return;
}
String model = order.getModelNumber();
if (model == null || model.trim().isEmpty()) {
return;
}
ProductJdConfig cfg = productJdConfigService.selectProductJdConfigByModel(model.trim());
if (cfg == null) {
return;
}
if ("direct".equals(type)) {
BigDecimal p = cfg.getSellingPriceDirect();
if (p != null) {
order.setSellingPrice(p.doubleValue());
}
} else if ("xianyu".equals(type)) {
BigDecimal p = cfg.getSellingPriceXianyu();
if (p != null) {
order.setSellingPrice(p.doubleValue());
}
}
}
private void computeProfitForF(JDOrder order) {
String type = order.getSellingPriceType();
Double sp = order.getSellingPrice();
if (type == null || type.isEmpty() || sp == null) {
order.setProfit(null);
return;
}
double pay = order.getPaymentAmount() != null ? order.getPaymentAmount() : 0.0;
double rebate = order.getRebateAmount() != null ? order.getRebateAmount() : 0.0;
double netReceipt;
if ("xianyu".equals(type)) {
netReceipt = BigDecimal.valueOf(sp).multiply(BigDecimal.valueOf(XIANYU_NET_FACTOR))
.setScale(2, RoundingMode.HALF_UP).doubleValue();
} else if ("direct".equals(type)) {
netReceipt = sp;
} else {
order.setProfit(null);
return;
}
order.setProfit(BigDecimal.valueOf(netReceipt - pay - rebate)
.setScale(2, RoundingMode.HALF_UP).doubleValue());
}
}

View File

@@ -2,6 +2,7 @@ package com.ruoyi.jarvis.service.impl;
import com.ruoyi.jarvis.domain.JDOrder;
import com.ruoyi.jarvis.mapper.JDOrderMapper;
import com.ruoyi.jarvis.service.IJDOrderProfitService;
import com.ruoyi.jarvis.service.IJDOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -14,6 +15,9 @@ public class JDOrderServiceImpl implements IJDOrderService {
@Autowired
private JDOrderMapper jdOrderMapper;
@Autowired
private IJDOrderProfitService jdOrderProfitService;
@Override
public List<JDOrder> selectJDOrderList(JDOrder jdOrder) {
return jdOrderMapper.selectJDOrderList(jdOrder);
@@ -31,6 +35,7 @@ public class JDOrderServiceImpl implements IJDOrderService {
@Override
public int insertJDOrder(JDOrder jdOrder) {
jdOrderProfitService.recalculate(jdOrder);
return jdOrderMapper.insertJDOrder(jdOrder);
}

View File

@@ -118,6 +118,15 @@ public class ProductJdConfigServiceImpl implements IProductJdConfigService
}
}
Object spd = map.get("sellingPriceDirect");
if (spd != null) {
config.setSellingPriceDirect(spd instanceof BigDecimal ? (BigDecimal) spd : new BigDecimal(spd.toString()));
}
Object spx = map.get("sellingPriceXianyu");
if (spx != null) {
config.setSellingPriceXianyu(spx instanceof BigDecimal ? (BigDecimal) spx : new BigDecimal(spx.toString()));
}
return config;
}
return null;
@@ -147,13 +156,28 @@ public class ProductJdConfigServiceImpl implements IProductJdConfigService
if (StringUtils.isEmpty(productJdConfig.getProductModel())) {
return 0;
}
String redisKey = PRODUCT_JD_CONFIG_KEY + productJdConfig.getProductModel();
Map<String, Object> map = new java.util.HashMap<>();
Map<String, Object> old = redisCache.getCacheMap(redisKey);
if (old != null && !old.isEmpty()) {
map.putAll(old);
}
map.put("productModel", productJdConfig.getProductModel());
map.put("jdUrl", productJdConfig.getJdUrl());
map.put("commission", productJdConfig.getCommission() != null ? productJdConfig.getCommission() : BigDecimal.ZERO);
map.put("commissionReceive", productJdConfig.getCommissionReceive() != null ? productJdConfig.getCommissionReceive() : BigDecimal.ZERO);
map.put("commissionPay", productJdConfig.getCommissionPay() != null ? productJdConfig.getCommissionPay() : BigDecimal.ZERO);
redisCache.setCacheMap(PRODUCT_JD_CONFIG_KEY + productJdConfig.getProductModel(), map);
if (productJdConfig.getSellingPriceDirect() != null) {
map.put("sellingPriceDirect", productJdConfig.getSellingPriceDirect());
} else {
map.remove("sellingPriceDirect");
}
if (productJdConfig.getSellingPriceXianyu() != null) {
map.put("sellingPriceXianyu", productJdConfig.getSellingPriceXianyu());
} else {
map.remove("sellingPriceXianyu");
}
redisCache.setCacheMap(redisKey, map);
return 1;
}

View File

@@ -37,6 +37,11 @@
<result property="reviewPostedDate" column="review_posted_date"/>
<result property="rebateRemarkJson" column="rebate_remark_json"/>
<result property="rebateRemarkHasAbnormal" column="rebate_remark_has_abnormal"/>
<result property="sellingPriceType" column="selling_price_type"/>
<result property="sellingPrice" column="selling_price"/>
<result property="profit" column="profit"/>
<result property="sellingPriceManual" column="selling_price_manual"/>
<result property="profitManual" column="profit_manual"/>
</resultMap>
<sql id="selectJDOrderBase">
@@ -44,7 +49,8 @@
address, logistics_link, order_id, buyer, order_time, create_time, update_time, status, is_count_enabled, third_party_order_no, jingfen_actual_price,
is_refunded, refund_date, is_refund_received, refund_received_date, is_rebate_received, rebate_received_date,
is_price_protected, price_protected_date, is_invoice_opened, invoice_opened_date, is_review_posted, review_posted_date,
rebate_remark_json, rebate_remark_has_abnormal
rebate_remark_json, rebate_remark_has_abnormal,
selling_price_type, selling_price, profit, selling_price_manual, profit_manual
from jd_order
</sql>
@@ -151,14 +157,16 @@
tencent_doc_pushed, tencent_doc_push_time,
order_id, buyer, order_time, create_time, update_time, status, is_count_enabled, third_party_order_no, jingfen_actual_price,
is_refunded, refund_date, is_refund_received, refund_received_date, is_rebate_received, rebate_received_date,
is_price_protected, price_protected_date, is_invoice_opened, invoice_opened_date, is_review_posted, review_posted_date
is_price_protected, price_protected_date, is_invoice_opened, invoice_opened_date, is_review_posted, review_posted_date,
selling_price_type, selling_price, profit, selling_price_manual, profit_manual
) values (
#{remark}, #{distributionMark}, #{modelNumber}, #{link},
#{paymentAmount}, #{rebateAmount}, #{address}, #{logisticsLink},
0, null,
#{orderId}, #{buyer}, #{orderTime}, now(), now(), #{status}, #{isCountEnabled}, #{thirdPartyOrderNo}, #{jingfenActualPrice},
#{isRefunded}, #{refundDate}, #{isRefundReceived}, #{refundReceivedDate}, #{isRebateReceived}, #{rebateReceivedDate},
#{isPriceProtected}, #{priceProtectedDate}, #{isInvoiceOpened}, #{invoiceOpenedDate}, #{isReviewPosted}, #{reviewPostedDate}
#{isPriceProtected}, #{priceProtectedDate}, #{isInvoiceOpened}, #{invoiceOpenedDate}, #{isReviewPosted}, #{reviewPostedDate},
#{sellingPriceType}, #{sellingPrice}, #{profit}, #{sellingPriceManual}, #{profitManual}
)
</insert>
@@ -196,6 +204,13 @@
<if test="reviewPostedDate != null"> review_posted_date = #{reviewPostedDate},</if>
<if test="rebateRemarkJson != null"> rebate_remark_json = #{rebateRemarkJson},</if>
<if test="rebateRemarkHasAbnormal != null"> rebate_remark_has_abnormal = #{rebateRemarkHasAbnormal},</if>
<if test="params != null and params.applyProfitFields != null and params.applyProfitFields == true">
selling_price_type = #{sellingPriceType,jdbcType=VARCHAR},
selling_price = #{sellingPrice,jdbcType=DOUBLE},
profit = #{profit,jdbcType=DOUBLE},
selling_price_manual = #{sellingPriceManual,jdbcType=INTEGER},
profit_manual = #{profitManual,jdbcType=INTEGER},
</if>
update_time = now()
</set>
where id = #{id}

View File

@@ -0,0 +1,6 @@
-- JD 订单:售价渠道、售价、利润及手动锁定标记(执行前请备份)
ALTER TABLE jd_order ADD COLUMN selling_price_type VARCHAR(16) NULL COMMENT '售价渠道 direct/xianyuF单用' AFTER rebate_remark_has_abnormal;
ALTER TABLE jd_order ADD COLUMN selling_price DOUBLE NULL COMMENT '售价' AFTER selling_price_type;
ALTER TABLE jd_order ADD COLUMN profit DOUBLE NULL COMMENT '利润' AFTER selling_price;
ALTER TABLE jd_order ADD COLUMN selling_price_manual TINYINT NULL DEFAULT 0 COMMENT '1售价手动锁定' AFTER profit;
ALTER TABLE jd_order ADD COLUMN profit_manual TINYINT NULL DEFAULT 0 COMMENT '1利润手动锁定' AFTER selling_price_manual;