统计 成本价 部分

This commit is contained in:
2025-11-25 11:49:40 +08:00
parent a995e8db25
commit ec46d876b1
3 changed files with 140 additions and 69 deletions

View File

@@ -14,9 +14,5 @@ import java.math.BigDecimal;
public class ProductCostAmountVO {
private Long productId;
private Long skuId;
//向上取整 保留两位小数
private BigDecimal costAmount;
private Long count;
private BigDecimal totalCostAmount;
}

View File

@@ -1,8 +1,10 @@
package com.czg.service.order.mapper;
import com.czg.order.vo.ProductCostAmountVO;
import com.czg.order.vo.TotalVo;
import com.mybatisflex.core.BaseMapper;
import com.czg.order.entity.ShopOrderStatistic;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Select;
import java.math.BigDecimal;
@@ -19,55 +21,56 @@ import java.util.Map;
public interface ShopOrderStatisticMapper extends BaseMapper<ShopOrderStatistic> {
@Select("SELECT"+
" SUM(pay_amount) AS payAmount,"+
" SUM(online_pay_amount) AS onlinePayAmount,"+
" SUM(member_pay_amount) AS memberPayAmount,"+
" SUM(member_pay_count) AS memberPayCount,"+
" SUM(refund_amount) AS refundAmount,"+
" SUM(online_refund_amount) AS onlineRefundAmount,"+
" SUM(recharge_amount) AS rechargeAmount,"+
" SUM(online_recharge_amount) AS onlineRechargeAmount,"+
" SUM(give_amount) AS giveAmount,"+
" SUM(recharge_refund_amount) AS rechargeRefundAmount,"+
" SUM(online_recharge_refund_amount) AS onlineRechargeRefundAmount,"+
" SUM(cash_recharge_refund_amount) AS cashRechargeRefundAmount,"+
" SUM(cash_recharge_amount) AS cashRechargeAmount,"+
" SUM(member_refund_amount) AS memberRefundAmount,"+
" SUM(cash_refund_amount) AS cashRefundAmount,"+
" SUM(new_member_count) AS newMemberCount,"+
" SUM(customer_count) AS customerCount,"+
" SUM(order_count) AS orderCount,"+
" SUM(table_count) AS tableCount,"+
" SUM(avg_pay_amount) AS avgPayAmount,"+
" SUM(turnover_rate) AS turnoverRate,"+
" SUM(profit_amount) AS profitAmount,"+
" SUM(product_cost_amount) AS productCostAmount,"+
" SUM(profit_rate) AS profitRate,"+
" SUM(net_profit_amount) AS netProfitAmount,"+
" SUM(net_profit_rate) AS netProfitRate,"+
" SUM(discount_amount) AS discountAmount,"+
" SUM(discount_count) AS discountCount,"+
" SUM(new_customer_discount_amount) AS newCustomerDiscountAmount,"+
" SUM(full_discount_amount) AS fullDiscountAmount,"+
" SUM(coupon_discount_amount) AS couponDiscountAmount,"+
" SUM(point_discount_amount) AS pointDiscountAmount,"+
" SUM(back_discount_amount) AS backDiscountAmount,"+
" SUM(member_discount_amount) AS memberDiscountAmount,"+
" SUM(order_price_discount_amount) AS orderPriceDiscountAmount,"+
" SUM(cash_pay_amount) AS cashPayAmount,"+
" SUM(wechat_pay_amount) AS wechatPayAmount,"+
" SUM(alipay_pay_amount) AS alipayPayAmount,"+
" SUM(back_scan_pay_amount) AS backScanPayAmount,"+
" SUM(main_scan_pay_amount) AS mainScanPayAmount,"+
" SUM(credit_pay_amount) AS creditPayAmount "+
"FROM"+
@Select("SELECT" +
" SUM(pay_amount) AS payAmount," +
" SUM(online_pay_amount) AS onlinePayAmount," +
" SUM(member_pay_amount) AS memberPayAmount," +
" SUM(member_pay_count) AS memberPayCount," +
" SUM(refund_amount) AS refundAmount," +
" SUM(online_refund_amount) AS onlineRefundAmount," +
" SUM(recharge_amount) AS rechargeAmount," +
" SUM(online_recharge_amount) AS onlineRechargeAmount," +
" SUM(give_amount) AS giveAmount," +
" SUM(recharge_refund_amount) AS rechargeRefundAmount," +
" SUM(online_recharge_refund_amount) AS onlineRechargeRefundAmount," +
" SUM(cash_recharge_refund_amount) AS cashRechargeRefundAmount," +
" SUM(cash_recharge_amount) AS cashRechargeAmount," +
" SUM(member_refund_amount) AS memberRefundAmount," +
" SUM(cash_refund_amount) AS cashRefundAmount," +
" SUM(new_member_count) AS newMemberCount," +
" SUM(customer_count) AS customerCount," +
" SUM(order_count) AS orderCount," +
" SUM(table_count) AS tableCount," +
" SUM(avg_pay_amount) AS avgPayAmount," +
" SUM(turnover_rate) AS turnoverRate," +
" SUM(profit_amount) AS profitAmount," +
" SUM(product_cost_amount) AS productCostAmount," +
" SUM(profit_rate) AS profitRate," +
" SUM(net_profit_amount) AS netProfitAmount," +
" SUM(net_profit_rate) AS netProfitRate," +
" SUM(discount_amount) AS discountAmount," +
" SUM(discount_count) AS discountCount," +
" SUM(new_customer_discount_amount) AS newCustomerDiscountAmount," +
" SUM(full_discount_amount) AS fullDiscountAmount," +
" SUM(coupon_discount_amount) AS couponDiscountAmount," +
" SUM(point_discount_amount) AS pointDiscountAmount," +
" SUM(back_discount_amount) AS backDiscountAmount," +
" SUM(member_discount_amount) AS memberDiscountAmount," +
" SUM(order_price_discount_amount) AS orderPriceDiscountAmount," +
" SUM(cash_pay_amount) AS cashPayAmount," +
" SUM(wechat_pay_amount) AS wechatPayAmount," +
" SUM(alipay_pay_amount) AS alipayPayAmount," +
" SUM(back_scan_pay_amount) AS backScanPayAmount," +
" SUM(main_scan_pay_amount) AS mainScanPayAmount," +
" SUM(credit_pay_amount) AS creditPayAmount " +
"FROM" +
" tb_shop_order_statistic " +
"WHERE " +
" shop_id = #{shopId} " +
" AND statistic_date BETWEEN #{start} AND #{end} ")
ShopOrderStatistic countStatistic(Long shopId, LocalDate start, LocalDate end);
//---------------------------------------------金额统计---------------------------------------------------
/**
* 订单金额统计 当日实时数据
*/
@@ -254,17 +257,44 @@ public interface ShopOrderStatisticMapper extends BaseMapper<ShopOrderStatistic>
/**
* 商品成本 productCostAmount
*/
@Select("SELECT" +
" sku.id AS skuId," +
" sku.cost_price AS costAmount " +
"FROM" +
" tb_product product" +
" LEFT JOIN tb_prod_sku sku ON product.id = sku.product_id " +
" AND product.shop_id = sku.shop_id " +
"WHERE" +
" product.shop_id = #{shopId} " +
" AND sku.cost_price > 0")
@MapKey("skuId")
Map<Long, BigDecimal> getSkuCostAmount(Long shopId);
@Select("SELECT" +
" relation.product_id as productId," +
" ROUND(relation.surplus_stock * cons.price, 2) AS costAmount" +
" FROM" +
" tb_prod_cons_relation relation " +
" INNER JOIN tb_cons_info cons on relation.cons_info_id = cons.id and relation.shop_id = cons.shop_id and price > 0" +
" WHERE" +
" relation.shop_id = #{shopId} " +
" order by relation.product_id")
@MapKey("productId")
Map<Long, BigDecimal> getConsCostAmount(Long shopId);
@Select("SELECT " +
" SUM(CASE WHEN sku.cost_price IS NULL OR sku.cost_price <= 0 THEN 0 ELSE sku.cost_price END) AS productCostAmount " +
" detail.product_id as productId," +
" detail.sku_id as skuId," +
" sum(detail.num - detail.return_num) as count " +
"FROM " +
" tb_order_info `order` " +
" INNER JOIN tb_order_detail detail ON `order`.id = detail.order_id " +
" AND is_temporary = 0 " +
" LEFT JOIN tb_prod_sku sku ON detail.sku_id = sku.id " +
" INNER JOIN tb_order_detail detail ON `order`.id = detail.order_id AND is_temporary = 0 " +
"WHERE " +
" `order`.shop_id = #{shopId} " +
" and trade_day = #{tradeDay} " +
" and paid_time is not null")
BigDecimal countProductCostAmount(Long shopId, LocalDate tradeDay);
" and paid_time is not null" +
" order by detail.product_id, detail.sku_id")
List<ProductCostAmountVO> getOrderDetailProduct(Long shopId, LocalDate tradeDay);
}

View File

@@ -2,11 +2,13 @@ package com.czg.service.order.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson2.JSONObject;
import com.czg.exception.CzgException;
import com.czg.order.entity.ShopOrderStatistic;
import com.czg.order.service.ShopOrderStatisticService;
import com.czg.order.vo.CountPayTypeVo;
import com.czg.order.vo.ProductCostAmountVO;
import com.czg.order.vo.TotalVo;
import com.czg.service.order.mapper.ShopOrderStatisticMapper;
import com.mybatisflex.core.query.QueryWrapper;
@@ -18,7 +20,9 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
@@ -53,6 +57,7 @@ public class ShopOrderStatisticServiceImpl extends ServiceImpl<ShopOrderStatisti
ShopOrderStatistic statDateRange = getStatDateRange(shopId, start, end);
ShopOrderStatistic shopOrderStatistic = ShopOrderStatistic.mergeStatistics(realTimeDataByDay, statDateRange);
shopOrderStatistic.setShopId(shopId);
calculateShopOrderStatistic(shopOrderStatistic);
return shopOrderStatistic;
}
}
@@ -106,16 +111,17 @@ public class ShopOrderStatisticServiceImpl extends ServiceImpl<ShopOrderStatisti
ShopOrderStatistic result = new ShopOrderStatistic();
ShopOrderStatistic onlineStat = mapper.getOnlineStatSingleDate(shopId, day);
log.info("onlineStat:{}", JSONObject.toJSONString(onlineStat));
// log.info("onlineStat:{}", JSONObject.toJSONString(onlineStat));
ShopOrderStatistic orderStat = mapper.getOrderStatSingleDate(shopId, day);
log.info("orderStat:{}", JSONObject.toJSONString(orderStat));
// log.info("orderStat:{}", JSONObject.toJSONString(orderStat));
ShopOrderStatistic userFlowStat = mapper.getShopUserFlowStatSingleDate(shopId, day);
log.info("userFlowStat:{}", JSONObject.toJSONString(userFlowStat));
// log.info("userFlowStat:{}", JSONObject.toJSONString(userFlowStat));
Long discountCount = mapper.countDiscountOrder(shopId, day);
BigDecimal discountAmount = mapper.countDiscountAmount(shopId, day);
Long tableCount = mapper.countShopTable(shopId);
Long newMemberCount = mapper.countNewMember(shopId, day);
BigDecimal productCostAmount = mapper.countProductCostAmount(shopId, day);
//获取商品成本金额
BigDecimal productCostAmount = getProductCostAmount(shopId, day);
// 合并结果
CopyOptions copyOptions = CopyOptions.create().setIgnoreNullValue(true);
if (onlineStat != null) {
@@ -134,11 +140,11 @@ public class ShopOrderStatisticServiceImpl extends ServiceImpl<ShopOrderStatisti
result.setShopId(shopId);
result.setStatisticDate(day);
result.setDiscountCount(discountCount);
result.setDiscountAmount(discountAmount);
result.setTableCount(tableCount);
result.setNewMemberCount(newMemberCount);
result.setProductCostAmount(productCostAmount);
result.setDiscountCount(Optional.ofNullable(discountCount).orElse(0L));
result.setDiscountAmount(Optional.ofNullable(discountAmount).orElse(BigDecimal.ZERO));
result.setNewMemberCount(Optional.ofNullable(newMemberCount).orElse(0L));
result.setTableCount(Optional.ofNullable(tableCount).orElse(0L));
result.setProductCostAmount(Optional.ofNullable(productCostAmount).orElse(BigDecimal.ZERO));
//会员充值退款 充值退款金额(线上退款+现金退款)
BigDecimal cashRechargeAmount = Objects.requireNonNullElse(result.getCashRechargeAmount(), BigDecimal.ZERO);
@@ -177,11 +183,49 @@ public class ShopOrderStatisticServiceImpl extends ServiceImpl<ShopOrderStatisti
.add(memberPayAmount)
.add(creditPayAmount);
result.setPayAmount(totalPayAmount);
//毛利润(订单实付金额-商品成本)
if (productCostAmount == null) {
productCostAmount = BigDecimal.ZERO;
calculateShopOrderStatistic(result);
return result;
}
/**
* 获取商品成本价
*/
private BigDecimal getProductCostAmount(Long shopId, LocalDate day) {
//获取商品sku成本价 productId skuId price
Map<Long, BigDecimal> skuCostAmountList = mapper.getSkuCostAmount(shopId);
// //获取商品耗材成本价 productId price
Map<Long, BigDecimal> consCostAmountList = mapper.getConsCostAmount(shopId);
//获取orderDetail信息 productId skuId 数量
BigDecimal productCostAmount = BigDecimal.ZERO;
if (CollUtil.isEmpty(skuCostAmountList) && CollUtil.isEmpty(consCostAmountList)) {
return productCostAmount;
}
result.setProfitAmount(result.getPayAmount().subtract(productCostAmount));
List<ProductCostAmountVO> orderDetailProduct = mapper.getOrderDetailProduct(shopId, day);
if (CollUtil.isEmpty(orderDetailProduct)) {
return productCostAmount;
}
for (ProductCostAmountVO productCostAmountVO : orderDetailProduct) {
if (skuCostAmountList.containsKey(productCostAmountVO.getSkuId())) {
BigDecimal costAmount = skuCostAmountList.get(productCostAmountVO.getSkuId());
productCostAmount = productCostAmount.add(costAmount.multiply(new BigDecimal(productCostAmountVO.getCount())).setScale(2, RoundingMode.HALF_UP));
} else if (consCostAmountList.containsKey(productCostAmountVO.getProductId())) {
BigDecimal costAmount = consCostAmountList.get(productCostAmountVO.getProductId());
productCostAmount = productCostAmount.add(costAmount.multiply(new BigDecimal(productCostAmountVO.getCount())).setScale(2, RoundingMode.HALF_UP));
}
}
return productCostAmount;
}
/**
* 计算 客单价 翻台率 净利润 净利率 毛利润 毛利率
*/
private void calculateShopOrderStatistic(ShopOrderStatistic result) {
//毛利润(订单实付金额-商品成本)
if (result.getProductCostAmount() == null) {
result.setProductCostAmount(BigDecimal.ZERO);
}
result.setProfitAmount(result.getPayAmount().subtract(result.getProductCostAmount()));
//毛利率(订单实付金额-商品成本)/订单实付金额*100%
if (result.getPayAmount().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal profitRate = result.getProfitAmount().divide(result.getPayAmount(), 4, RoundingMode.HALF_DOWN).multiply(BigDecimal.valueOf(100));
@@ -199,13 +243,14 @@ public class ShopOrderStatisticServiceImpl extends ServiceImpl<ShopOrderStatisti
result.setAvgPayAmount(result.getPayAmount());
}
//翻台率 (订单数-桌台数)/桌台数*100%
if (tableCount > 0) {
BigDecimal turnoverRate = new BigDecimal(result.getOrderCount()).subtract(new BigDecimal(tableCount)).divide(new BigDecimal(tableCount), 4, RoundingMode.HALF_DOWN).multiply(BigDecimal.valueOf(100));
if (result.getTableCount() != null && result.getTableCount() > 0) {
BigDecimal turnoverRate = new BigDecimal(
result.getOrderCount()).subtract(new BigDecimal(result.getTableCount()))
.divide(new BigDecimal(result.getTableCount()), 4, RoundingMode.HALF_DOWN).multiply(BigDecimal.valueOf(100));
result.setTurnoverRate(turnoverRate);
} else {
result.setTurnoverRate(BigDecimal.ZERO);
}
return result;
}