| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- package com.kym.service.impl;
- import com.kym.entity.*;
- import com.kym.entity.awoara.OrderInfo;
- import com.kym.service.*;
- import com.kym.service.cache.KymCache;
- import com.kym.service.factory.DiscountStrategyFactory;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import java.math.BigDecimal;
- import java.math.RoundingMode;
- import java.time.Duration;
- import java.time.LocalDateTime;
- import java.util.List;
- import java.util.Map;
- import java.util.UUID;
- import java.util.stream.Collectors;
- /**
- * 订单结算服务实现
- *
- * @author skyline
- */
- @Slf4j
- @Service
- public class OrderSettlementServiceImpl implements OrderSettlementService {
- @Value("${kym.domain}")
- private String DOMAIN;
- private final WashOrderService washOrderService;
- private final WalletDetailService walletDetailService;
- private final AccountService accountService;
- private final SplitRecordService splitRecordService;
- private final PayLogService payLogService;
- private final UserService userService;
- private final MpMsgTemplateService mpMsgTemplateService;
- public OrderSettlementServiceImpl(WashOrderService washOrderService, WalletDetailService walletDetailService,
- AccountService accountService, SplitRecordService splitRecordService,
- PayLogService payLogService, UserService userService,
- MpMsgTemplateService mpMsgTemplateService) {
- this.washOrderService = washOrderService;
- this.walletDetailService = walletDetailService;
- this.accountService = accountService;
- this.splitRecordService = splitRecordService;
- this.payLogService = payLogService;
- this.userService = userService;
- this.mpMsgTemplateService = mpMsgTemplateService;
- }
- @Override
- @Transactional
- public void settleOrder(WashOrder washOrder, OrderInfo orderInfo) {
- log.info("执行订单结算,订单:{},结算信息:{}", orderInfo.getOrder_id(), orderInfo);
- // 幂等保护:重新加载订单并检查是否已结算
- var freshOrder = washOrderService.lambdaQuery().eq(WashOrder::getId, washOrder.getId()).one();
- if (freshOrder != null && Integer.valueOf(WashOrder.PAY_STATUS_已支付).equals(freshOrder.getPayStatus())) {
- log.warn("订单:{},已结算,跳过重复结算", orderInfo.getOrder_id());
- return;
- }
- int rechargePayment;
- int grantsPayment = 0;
- var account = accountService.lambdaQuery().eq(Account::getUserId, washOrder.getUserId()).last("FOR UPDATE").one();
- int amountReceived = orderInfo.getAmount_received();
- if (account.getRechargeBalance() >= amountReceived) {
- rechargePayment = amountReceived;
- accountService.lambdaUpdate()
- .setSql("balance = balance - {0}, recharge_balance = recharge_balance - {1}",
- amountReceived, rechargePayment)
- .eq(Account::getUserId, washOrder.getUserId())
- .update();
- } else {
- rechargePayment = account.getRechargeBalance();
- grantsPayment = amountReceived - account.getRechargeBalance();
- accountService.lambdaUpdate()
- .setSql("balance = balance - {0}, recharge_balance = 0, grants_balance = grants_balance - {1}",
- amountReceived, grantsPayment)
- .eq(Account::getUserId, washOrder.getUserId())
- .update();
- }
- washOrder
- .setCloseType(orderInfo.getClose_type())
- .setAmount(orderInfo.getAmount())
- .setAmountReceivable(orderInfo.getAmount_receivable())
- .setAmountReceived(amountReceived)
- .setDiscountMoney(orderInfo.getDiscount_money())
- .setDetail(orderInfo.getDetail())
- .setEndTime(LocalDateTime.now())
- .setTotalSeconds(Duration.between(washOrder.getStartTime(), LocalDateTime.now()).toSeconds())
- .setRechargePayment(rechargePayment)
- .setGrantsPayment(grantsPayment)
- .setPayStatus(WashOrder.PAY_STATUS_已支付)
- .setOrderStatus(WashOrder.ORDER_STATUS_成功);
- washOrderService.updateById(washOrder);
- ensureUserStationBelonging(washOrder.getUserId(), washOrder.getStationId());
- backfillUnattributedRecharges(washOrder.getUserId());
- if (orderInfo.getAmount() == 0) {
- return;
- }
- if (washOrder.getIsCross()) {
- doCrossSplit(washOrder, KymCache.INSTANCE.getUserStationId(washOrder.getUserId()));
- } else {
- doLocalSplit(washOrder);
- }
- var walletDetail = new WalletDetail();
- walletDetail.setUserId(washOrder.getUserId());
- walletDetail.setType(WalletDetail.TYPE_消费);
- walletDetail.setOrderNo(washOrder.getOrderId());
- walletDetail.setAmount(amountReceived);
- walletDetail.setGrantsAmount(grantsPayment);
- walletDetail.setBeforeBalance(account.getBalance());
- walletDetail.setAfterBalance(account.getBalance() - amountReceived);
- walletDetail.setBeforeGrantsBalance(account.getGrantsBalance());
- walletDetail.setAfterGrantsBalance(account.getGrantsBalance() - grantsPayment);
- walletDetail.setTransactionId(washOrder.getId().toString());
- walletDetail.setTransactionTime(LocalDateTime.now());
- walletDetail.setStatus(WalletDetail.STATUS_已确认);
- walletDetailService.save(walletDetail);
- DiscountStrategyFactory.getDiscountStrategy(washOrder.getDiscountType()).computeDiscount(washOrder, account);
- washOrderService.updateById(washOrder);
- if (orderInfo.getAmount() >= KymCache.INSTANCE.getParkingCouponMinAmountByStationId(washOrder.getStationId())) {
- var parkingCouponUrl = KymCache.INSTANCE.getParkingQrCodeUrlByStationId(washOrder.getStationId());
- var code = UUID.randomUUID().toString();
- KymCache.INSTANCE.setParkingCouponCode(code, parkingCouponUrl, 3600 * 2L);
- var url = DOMAIN + "/api/parking-coupon?code=" + code;
- mpMsgTemplateService.sendParkingCouponMsg(washOrder, url);
- } else {
- mpMsgTemplateService.sendOrderCompletedMsg(washOrder, account.getBalance());
- }
- }
- @Transactional
- protected void doLocalSplit(WashOrder washOrder) {
- log.info("订单:{},本店消费,V2方案无需分账", washOrder.getOrderId());
- }
- @Transactional
- protected void doCrossSplit(WashOrder washOrder, String userStationId) {
- int rechargePayment = washOrder.getRechargePayment();
- int grantsPayment = washOrder.getGrantsPayment();
- int totalPayment = rechargePayment + grantsPayment;
- if (totalPayment == 0) {
- log.info("订单:{},支付金额为0,不产生跨店转账", washOrder.getOrderId());
- return;
- }
- BigDecimal crossRate = new BigDecimal("0.7");
- int crossAmount = crossRate.multiply(BigDecimal.valueOf(totalPayment)).setScale(0, RoundingMode.DOWN).intValue();
- log.info("订单:{},执行跨店分账,归属站:{},消费站:{},充值款实收:{},赠款实收:{},转账金额:{}",
- washOrder.getOrderId(), userStationId, washOrder.getStationId(), rechargePayment, grantsPayment, crossAmount);
- var crossExpend = new SplitRecord()
- .setFromStationId(userStationId)
- .setToStationId(washOrder.getStationId())
- .setTradeNo(washOrder.getOrderId())
- .setAmount(crossAmount)
- .setType(SplitRecord.TYPE_CROSS_EXPEND);
- var crossIncome = new SplitRecord()
- .setFromStationId(userStationId)
- .setToStationId(washOrder.getStationId())
- .setTradeNo(washOrder.getOrderId())
- .setAmount(crossAmount)
- .setType(SplitRecord.TYPE_CROSS_INCOME);
- splitRecordService.saveBatch(List.of(crossExpend, crossIncome));
- log.info("订单:{},跨店分账完成", washOrder.getOrderId());
- }
- /**
- * 兜底保障:如果 createOrder 的自动归属没有生效,在结算时补齐。
- * IoT 回调路径没有用户 Session,同步写 Redis 缓存供后续使用。
- */
- private void ensureUserStationBelonging(Long userId, String orderStationId) {
- var user = userService.getById(userId);
- if (user == null || user.getStationId() != null) {
- return;
- }
- userService.lambdaUpdate()
- .set(User::getStationId, orderStationId)
- .eq(User::getId, userId)
- .update();
- KymCache.INSTANCE.putUserId2StationId(Map.of(userId, orderStationId));
- log.info("兜底归属:用户={} 归属站点={}", userId, orderStationId);
- }
- private void backfillUnattributedRecharges(Long userId) {
- var userStationId = KymCache.INSTANCE.getUserStationId(userId);
- if (userStationId == null) {
- var user = userService.getById(userId);
- userStationId = user != null ? user.getStationId() : null;
- }
- if (userStationId == null) {
- return;
- }
- var walletDetails = walletDetailService.lambdaQuery()
- .eq(WalletDetail::getUserId, userId)
- .eq(WalletDetail::getType, WalletDetail.TYPE_充值)
- .eq(WalletDetail::getStatus, WalletDetail.STATUS_已确认)
- .list();
- if (walletDetails.isEmpty()) {
- return;
- }
- var outTradeNos = walletDetails.stream().map(WalletDetail::getOrderNo).toList();
- var payLogs = payLogService.lambdaQuery().in(PayLog::getOutTradeNo, outTradeNos).list();
- if (payLogs.isEmpty()) {
- return;
- }
- var allTxIds = payLogs.stream().map(PayLog::getTransactionId).collect(Collectors.toSet());
- var existingTxIds = splitRecordService.lambdaQuery()
- .in(SplitRecord::getTradeNo, allTxIds)
- .eq(SplitRecord::getType, SplitRecord.TYPE_RECHARGE)
- .list()
- .stream()
- .map(SplitRecord::getTradeNo)
- .collect(Collectors.toSet());
- for (var payLog : payLogs) {
- if (!existingTxIds.contains(payLog.getTransactionId())) {
- var splitRecord = new SplitRecord()
- .setFromStationId(userStationId)
- .setToStationId(userStationId)
- .setTradeNo(payLog.getTransactionId())
- .setAmount(payLog.getTotal())
- .setType(SplitRecord.TYPE_RECHARGE);
- splitRecordService.save(splitRecord);
- log.info("追溯补建充值分账:用户={}, 站点={}, 交易号={}, 金额={}",
- userId, userStationId, payLog.getTransactionId(), payLog.getTotal());
- }
- }
- }
- }
|