|
|
|
@ -6,11 +6,14 @@ import cc.hiver.mall.billing.entity.BillingUsageRecord; |
|
|
|
import cc.hiver.mall.billing.mapper.BillingUsageRecordMapper; |
|
|
|
import cc.hiver.mall.dao.mapper.*; |
|
|
|
import cc.hiver.mall.entity.*; |
|
|
|
import cc.hiver.mall.service.mybatis.WorkerDailyBalanceRecordService; |
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|
|
|
import io.swagger.annotations.Api; |
|
|
|
import io.swagger.annotations.ApiModelProperty; |
|
|
|
import io.swagger.annotations.ApiOperation; |
|
|
|
import lombok.Data; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.web.bind.annotation.PostMapping; |
|
|
|
@ -29,7 +32,9 @@ import java.util.Date; |
|
|
|
import java.util.HashSet; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
@Slf4j |
|
|
|
@RestController |
|
|
|
@Api(tags = "后台管理系统数据统计接口") |
|
|
|
@RequestMapping("/hiver/mall/admin/dataStatistics") |
|
|
|
@ -59,6 +64,8 @@ public class AdminDataStatisticsController { |
|
|
|
private BillingUsageRecordMapper billingUsageRecordMapper; |
|
|
|
@Autowired |
|
|
|
private MallSettlementRecordMapper mallSettlementRecordMapper; |
|
|
|
@Autowired |
|
|
|
private WorkerDailyBalanceRecordService workerDailyBalanceRecordService; |
|
|
|
|
|
|
|
@Data |
|
|
|
public static class DataStatisticsQuery { |
|
|
|
@ -87,7 +94,10 @@ public class AdminDataStatisticsController { |
|
|
|
private Boolean actualIncomeAvailable; |
|
|
|
private BigDecimal settledAmount; |
|
|
|
private BigDecimal headquartersDeductionAmount; |
|
|
|
private BigDecimal wechatReceiptAmount; |
|
|
|
private BigDecimal wechatFeeAmount; |
|
|
|
private BigDecimal deliveryFeeAmount; |
|
|
|
private Long workerDebtCount; |
|
|
|
private Long smsCount; |
|
|
|
private Long contentAuditCount; |
|
|
|
private Long realNameAuthCount; |
|
|
|
@ -101,6 +111,14 @@ public class AdminDataStatisticsController { |
|
|
|
private Long orderUserCount; |
|
|
|
} |
|
|
|
|
|
|
|
@Data |
|
|
|
public static class WorkerDebtQuery extends DataStatisticsQuery { |
|
|
|
@ApiModelProperty(value = "页码") |
|
|
|
private int pageNumber = 1; |
|
|
|
@ApiModelProperty(value = "每页条数") |
|
|
|
private int pageSize = 10; |
|
|
|
} |
|
|
|
|
|
|
|
@PostMapping("/summary") |
|
|
|
@ApiOperation(value = "按校区和日期范围获取数据统计汇总") |
|
|
|
public Result<DataStatisticsSummaryVo> summary(@RequestBody DataStatisticsQuery query) { |
|
|
|
@ -143,12 +161,16 @@ public class AdminDataStatisticsController { |
|
|
|
.ge(BillingUsageRecord::getBillingDate, range.startDate) |
|
|
|
.le(BillingUsageRecord::getBillingDate, range.endDate)); |
|
|
|
|
|
|
|
List<WorkerDailyBalanceRecord> workerBalanceRecords = workerDailyBalanceRecordService.list(new LambdaQueryWrapper<WorkerDailyBalanceRecord>() |
|
|
|
.eq(StringUtils.isNotBlank(regionId), WorkerDailyBalanceRecord::getRegionId, regionId) |
|
|
|
.ge(WorkerDailyBalanceRecord::getBalanceDate, range.startDate) |
|
|
|
.le(WorkerDailyBalanceRecord::getBalanceDate, range.endDate)); |
|
|
|
|
|
|
|
DataStatisticsSummaryVo vo = new DataStatisticsSummaryVo(); |
|
|
|
vo.setStartDate(range.startDate.toString()); |
|
|
|
vo.setEndDate(range.endDate.toString()); |
|
|
|
|
|
|
|
Set<String> refundedOrderIds = new HashSet<>(); |
|
|
|
Set<String> deliveryRefundOrderIds = new HashSet<>(); |
|
|
|
for (MallRefundRecord refund : refunds) { |
|
|
|
if (StringUtils.isBlank(refund.getOrderId())) { |
|
|
|
continue; |
|
|
|
@ -156,24 +178,12 @@ public class AdminDataStatisticsController { |
|
|
|
if (refund.getStatus() != null && refund.getStatus() == 1) { |
|
|
|
refundedOrderIds.add(refund.getOrderId()); |
|
|
|
} |
|
|
|
if (refund.getRefundTypeStatus() != null && (refund.getRefundTypeStatus() == 2 || refund.getRefundTypeStatus() == 3 || refund.getRefundTypeStatus() == 4)) { |
|
|
|
deliveryRefundOrderIds.add(refund.getOrderId()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
BigDecimal goodsAndPackage = BigDecimal.ZERO; |
|
|
|
BigDecimal grossDeliveryFee = BigDecimal.ZERO; |
|
|
|
BigDecimal settlementDeliveryFee = BigDecimal.ZERO; |
|
|
|
BigDecimal koiDiscount = BigDecimal.ZERO; |
|
|
|
Set<String> shopIds = new HashSet<>(); |
|
|
|
Set<String> userIds = new HashSet<>(); |
|
|
|
for (MallOrder order : orders) { |
|
|
|
goodsAndPackage = goodsAndPackage.add(amount(order.getGoodsAmount())).add(amount(order.getPackageFee())); |
|
|
|
BigDecimal orderDeliveryFee = amount(order.getDeliveryFee()); |
|
|
|
grossDeliveryFee = grossDeliveryFee.add(orderDeliveryFee); |
|
|
|
if (!deliveryRefundOrderIds.contains(order.getId())) { |
|
|
|
settlementDeliveryFee = settlementDeliveryFee.add(orderDeliveryFee); |
|
|
|
} |
|
|
|
if (!isRefundedOrder(order, refundedOrderIds)) { |
|
|
|
koiDiscount = koiDiscount.add(amount(order.getFreeAmount())); |
|
|
|
} |
|
|
|
@ -184,13 +194,50 @@ public class AdminDataStatisticsController { |
|
|
|
userIds.add(order.getUserId()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
BigDecimal deliveryFeeAmount = subtractWechatFee(settlementDeliveryFee); |
|
|
|
BigDecimal grossFlow = subtractWechatFee(goodsAndPackage.add(grossDeliveryFee)); |
|
|
|
/* |
|
|
|
* 原配送费总额逻辑:从订单配送费反推,并按退款/售后、平台追加配送费、未取货全退等规则修正。 |
|
|
|
* 现改为查询 worker_daily_balance_record,累加所选日期内 daily_balance > 0 的余额快照。 |
|
|
|
* |
|
|
|
* BigDecimal settlementDeliveryFee = BigDecimal.ZERO; |
|
|
|
* Set<String> settlementDeliveryOrderIds = new HashSet<>(); |
|
|
|
* for (MallOrder order : orders) { |
|
|
|
* BigDecimal orderDeliveryFee = amount(order.getDeliveryFee()); |
|
|
|
* if (!deliveryRefundOrderIds.contains(order.getId())) { |
|
|
|
* settlementDeliveryFee = settlementDeliveryFee.add(orderDeliveryFee); |
|
|
|
* if (StringUtils.isNotBlank(order.getId())) { |
|
|
|
* settlementDeliveryOrderIds.add(order.getId()); |
|
|
|
* } |
|
|
|
* } |
|
|
|
* } |
|
|
|
* if (!settlementDeliveryOrderIds.isEmpty()) { |
|
|
|
* List<MallDeliveryOrder> settlementDeliveries = mallDeliveryOrderMapper.selectList(new LambdaQueryWrapper<MallDeliveryOrder>() |
|
|
|
* .eq(StringUtils.isNotBlank(regionId), MallDeliveryOrder::getRegionId, regionId) |
|
|
|
* .in(MallDeliveryOrder::getOrderId, settlementDeliveryOrderIds)); |
|
|
|
* BigDecimal marketplaceDeliveryFee = settlementDeliveries.stream() |
|
|
|
* .map(MallDeliveryOrder::getDeliveryFeeMarketplace) |
|
|
|
* .map(this::amount) |
|
|
|
* .reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
* BigDecimal unpickedMerchantFullRefundDeliveryFee = settlementDeliveries.stream() |
|
|
|
* .filter(item -> merchantFullRefundOrderIds.contains(item.getOrderId())) |
|
|
|
* .filter(item -> item.getGetTime() == null) |
|
|
|
* .map(this::deliverySettlementAmount) |
|
|
|
* .reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
* settlementDeliveryFee = settlementDeliveryFee.add(marketplaceDeliveryFee).subtract(unpickedMerchantFullRefundDeliveryFee); |
|
|
|
* } |
|
|
|
* BigDecimal deliveryFeeAmount = settlementDeliveryFee; |
|
|
|
*/ |
|
|
|
BigDecimal deliveryFeeAmount = sumPositiveWorkerBalance(workerBalanceRecords); |
|
|
|
long workerDebtCount = workerBalanceRecords.stream() |
|
|
|
.filter(item -> amount(item.getDailyBalance()).compareTo(BigDecimal.ZERO) < 0) |
|
|
|
.count(); |
|
|
|
BigDecimal grossFlow = orders.stream() |
|
|
|
.map(MallOrder::getTotalAmount) |
|
|
|
.map(this::amount) |
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
BigDecimal refundAmount = sumRefundAmount(refunds, 1); |
|
|
|
BigDecimal afterSaleAmount = sumRefundAmount(refunds, 4); |
|
|
|
BigDecimal effectiveFlow = subtractWechatFee(goodsAndPackage); |
|
|
|
BigDecimal headquartersDeduction = effectiveFlow.multiply(ONE_PERCENT); |
|
|
|
BigDecimal effectiveFlow = grossFlow.subtract(refundAmount).subtract(afterSaleAmount); |
|
|
|
BigDecimal headquartersDeduction = effectiveFlow.subtract(deliveryFeeAmount).multiply(ONE_PERCENT); |
|
|
|
|
|
|
|
BigDecimal collegeRankReward = sumDealingsAmount(rewardRecords, REWARD_COLLEGE_RANK); |
|
|
|
BigDecimal deliveryRankReward = sumDealingsAmount(rewardRecords, REWARD_DELIVERY_RANK); |
|
|
|
@ -199,6 +246,8 @@ public class AdminDataStatisticsController { |
|
|
|
.map(this::amount) |
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
BigDecimal drawPoolAmount = sumDealingsAmount(rewardRecords, REWARD_DRAW_POOL); |
|
|
|
BigDecimal wechatReceiptAmount = effectiveFlow; |
|
|
|
BigDecimal wechatFee = wechatReceiptAmount.multiply(WECHAT_FEE_RATE); |
|
|
|
|
|
|
|
long smsCount = billingRecords.stream().filter(item -> "sms".equals(item.getRecordType())).count(); |
|
|
|
long contentAuditCount = billingRecords.stream().filter(item -> "content_audit".equals(item.getRecordType())).count(); |
|
|
|
@ -209,13 +258,10 @@ public class AdminDataStatisticsController { |
|
|
|
BigDecimal rewardTotalAmount = collegeRankReward.add(drawPoolAmount).add(deliveryRankReward); |
|
|
|
boolean actualIncomeAvailable = isActualIncomeAvailable(range); |
|
|
|
BigDecimal settledAmount = actualIncomeAvailable ? sumSettledAmount(regionId, range) : BigDecimal.ZERO; |
|
|
|
BigDecimal actualIncome = grossFlow |
|
|
|
BigDecimal actualIncome = wechatReceiptAmount |
|
|
|
.subtract(deliveryFeeAmount) |
|
|
|
.subtract(headquartersDeduction) |
|
|
|
.subtract(koiDiscount) |
|
|
|
.subtract(refundAmount) |
|
|
|
.subtract(afterSaleAmount) |
|
|
|
.subtract(couponDiscount) |
|
|
|
.subtract(wechatFee) |
|
|
|
.subtract(smsCost) |
|
|
|
.subtract(contentAuditCost) |
|
|
|
.subtract(realNameCost) |
|
|
|
@ -225,14 +271,17 @@ public class AdminDataStatisticsController { |
|
|
|
vo.setOrderCount((long) orders.size()); |
|
|
|
vo.setGrossFlowAmount(money(grossFlow)); |
|
|
|
vo.setDeliveryFeeAmount(money(deliveryFeeAmount)); |
|
|
|
vo.setWorkerDebtCount(workerDebtCount); |
|
|
|
vo.setRefundAmount(money(refundAmount)); |
|
|
|
vo.setAfterSaleAmount(money(afterSaleAmount)); |
|
|
|
vo.setEffectiveFlowAmount(money(effectiveFlow)); |
|
|
|
vo.setKoiDiscountAmount(money(koiDiscount)); |
|
|
|
vo.setCouponDiscountAmount(money(couponDiscount)); |
|
|
|
vo.setOnTimeRate(onTimeRate(deliveries)); |
|
|
|
vo.setCustomerAverageAmount(orders.isEmpty() ? BigDecimal.ZERO : money(goodsAndPackage.divide(BigDecimal.valueOf(orders.size()), 4, RoundingMode.HALF_UP))); |
|
|
|
vo.setCustomerAverageAmount(orders.isEmpty() ? BigDecimal.ZERO : money(grossFlow.divide(BigDecimal.valueOf(orders.size()), 4, RoundingMode.HALF_UP))); |
|
|
|
vo.setHeadquartersDeductionAmount(money(headquartersDeduction)); |
|
|
|
vo.setWechatReceiptAmount(money(wechatReceiptAmount)); |
|
|
|
vo.setWechatFeeAmount(money(wechatFee)); |
|
|
|
vo.setActualIncomeAmount(actualIncomeAvailable ? money(actualIncome) : BigDecimal.ZERO); |
|
|
|
vo.setActualIncomeAvailable(actualIncomeAvailable); |
|
|
|
vo.setSettledAmount(money(settledAmount)); |
|
|
|
@ -250,6 +299,31 @@ public class AdminDataStatisticsController { |
|
|
|
return new ResultUtil<DataStatisticsSummaryVo>().setData(vo); |
|
|
|
} |
|
|
|
|
|
|
|
@PostMapping("/workerDebtPage") |
|
|
|
@ApiOperation(value = "分页查询欠款配送员每日余额记录") |
|
|
|
public Result<Page<WorkerDailyBalanceRecord>> workerDebtPage(@RequestBody WorkerDebtQuery query) { |
|
|
|
DateRange range = resolveDateRange(query); |
|
|
|
String regionId = query == null ? null : query.getRegionId(); |
|
|
|
Page<WorkerDailyBalanceRecord> page = workerDailyBalanceRecordService.page( |
|
|
|
new Page<>(pageNumber(query), pageSize(query)), |
|
|
|
new LambdaQueryWrapper<WorkerDailyBalanceRecord>() |
|
|
|
.eq(StringUtils.isNotBlank(regionId), WorkerDailyBalanceRecord::getRegionId, regionId) |
|
|
|
.ge(WorkerDailyBalanceRecord::getBalanceDate, range.startDate) |
|
|
|
.le(WorkerDailyBalanceRecord::getBalanceDate, range.endDate) |
|
|
|
.lt(WorkerDailyBalanceRecord::getDailyBalance, BigDecimal.ZERO) |
|
|
|
.orderByAsc(WorkerDailyBalanceRecord::getBalanceDate) |
|
|
|
.orderByAsc(WorkerDailyBalanceRecord::getWorkerName)); |
|
|
|
return new ResultUtil<Page<WorkerDailyBalanceRecord>>().setData(page); |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal sumPositiveWorkerBalance(List<WorkerDailyBalanceRecord> records) { |
|
|
|
return records.stream() |
|
|
|
.map(WorkerDailyBalanceRecord::getDailyBalance) |
|
|
|
.map(this::amount) |
|
|
|
.filter(amount -> amount.compareTo(BigDecimal.ZERO) > 0) |
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal sumRefundAmount(List<MallRefundRecord> refunds, int status) { |
|
|
|
return refunds.stream() |
|
|
|
.filter(item -> item.getStatus() != null && item.getStatus() == status) |
|
|
|
@ -258,6 +332,13 @@ public class AdminDataStatisticsController { |
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal deliverySettlementAmount(MallDeliveryOrder delivery) { |
|
|
|
if (delivery == null) { |
|
|
|
return BigDecimal.ZERO; |
|
|
|
} |
|
|
|
return amount(delivery.getDeliveryFee()).add(amount(delivery.getDeliveryFeeMarketplace())); |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal sumDealingsAmount(List<DealingsRecord> records, String dealingsWay) { |
|
|
|
return records.stream() |
|
|
|
.filter(item -> dealingsWay.equals(item.getDealingsWay())) |
|
|
|
@ -266,18 +347,39 @@ public class AdminDataStatisticsController { |
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
} |
|
|
|
|
|
|
|
private void printSettlementDeliveryOrderIds(Set<String> orderIds) { |
|
|
|
if (orderIds == null || orderIds.isEmpty()) { |
|
|
|
log.info("数据统计结算配送订单ID为空"); |
|
|
|
return; |
|
|
|
} |
|
|
|
String orderIdSqlIn = orderIds.stream() |
|
|
|
.filter(StringUtils::isNotBlank) |
|
|
|
.collect(Collectors.joining("','", "'", "'")); |
|
|
|
log.info("数据统计结算配送订单ID, count={}, sql=select * from mall_delivery_order where order_id in ({});", orderIds.size(), orderIdSqlIn); |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal sumSettledAmount(String regionId, DateRange range) { |
|
|
|
Date settlementStart = Date.from(range.start.toInstant().plus(1, ChronoUnit.DAYS)); |
|
|
|
Date settlementEnd = Date.from(range.end.toInstant().plus(1, ChronoUnit.DAYS)); |
|
|
|
List<MallSettlementRecord> settlementRecords = mallSettlementRecordMapper.selectList(new LambdaQueryWrapper<MallSettlementRecord>() |
|
|
|
.eq(StringUtils.isNotBlank(regionId), MallSettlementRecord::getRegionId, regionId) |
|
|
|
.eq(MallSettlementRecord::getStatus, 1) |
|
|
|
.ge(MallSettlementRecord::getCreateTime, range.start) |
|
|
|
.le(MallSettlementRecord::getCreateTime, range.end)); |
|
|
|
.ge(MallSettlementRecord::getCreateTime, settlementStart) |
|
|
|
.le(MallSettlementRecord::getCreateTime, settlementEnd)); |
|
|
|
return settlementRecords.stream() |
|
|
|
.map(MallSettlementRecord::getSettlementAmount) |
|
|
|
.map(this::amount) |
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add); |
|
|
|
} |
|
|
|
|
|
|
|
private int pageNumber(WorkerDebtQuery query) { |
|
|
|
return query == null ? 1 : Math.max(query.getPageNumber(), 1); |
|
|
|
} |
|
|
|
|
|
|
|
private int pageSize(WorkerDebtQuery query) { |
|
|
|
return query == null ? 10 : Math.max(query.getPageSize(), 10); |
|
|
|
} |
|
|
|
|
|
|
|
private boolean isRefundedOrder(MallOrder order, Set<String> refundedOrderIds) { |
|
|
|
return (order.getStatus() != null && order.getStatus() == 8) |
|
|
|
|| refundedOrderIds.contains(order.getId()); |
|
|
|
@ -301,10 +403,6 @@ public class AdminDataStatisticsController { |
|
|
|
.divide(BigDecimal.valueOf(deliveries.size()), 2, RoundingMode.HALF_UP); |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal subtractWechatFee(BigDecimal value) { |
|
|
|
return amount(value).multiply(BigDecimal.ONE.subtract(WECHAT_FEE_RATE)); |
|
|
|
} |
|
|
|
|
|
|
|
private BigDecimal amount(BigDecimal value) { |
|
|
|
return value == null ? BigDecimal.ZERO : value; |
|
|
|
} |
|
|
|
|