From 55642c818f14bf8cf52c98e6858014bd8dc3d3a7 Mon Sep 17 00:00:00 2001
From: rk <94314517@qq.com>
Date: 星期四, 16 四月 2026 20:10:58 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java | 2739 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 2,733 insertions(+), 6 deletions(-)
diff --git a/server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java b/server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
index 414e48e..5224b0f 100644
--- a/server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
+++ b/server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -4,23 +4,57 @@
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.doumee.biz.system.AreasBiz;
+import com.doumee.biz.system.OperationConfigBiz;
+import com.doumee.biz.system.SystemDictDataBiz;
+import com.doumee.config.wx.WxMiniConfig;
+import com.doumee.config.wx.WxMiniUtilService;
import com.doumee.core.constants.Constants;
import com.doumee.core.constants.ResponseStatus;
import com.doumee.core.exception.BusinessException;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.DateUtil;
+import com.doumee.core.utils.Tencent.MapUtil;
import com.doumee.core.utils.Utils;
-import com.doumee.dao.business.OrdersMapper;
-import com.doumee.dao.business.model.Category;
-import com.doumee.dao.business.model.Orders;
+import com.doumee.dao.business.*;
+import com.doumee.dao.business.model.*;
+import com.doumee.dao.system.SystemUserMapper;
+import com.doumee.dao.system.model.SystemDictData;
+import com.doumee.dao.system.model.SystemUser;
+import com.doumee.dao.dto.CalculateLocalPriceDTO;
+import com.doumee.dao.dto.CalculateRemotePriceDTO;
+import com.doumee.dao.dto.CommentOrderDTO;
+import com.doumee.dao.dto.CreateOrderDTO;
+import com.doumee.dao.dto.DispatchDTO;
+import com.doumee.dao.dto.MyOrderDTO;
+import com.doumee.dao.dto.OrderItemDTO;
+import com.doumee.dao.vo.*;
+import com.doumee.service.business.OrderLogService;
import com.doumee.service.business.OrdersService;
+import com.doumee.service.business.AreasService;
+import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
-import java.util.List;
-import java.util.Objects;
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* 瀵勫瓨璁㈠崟淇℃伅Service瀹炵幇
@@ -32,6 +66,62 @@
@Autowired
private OrdersMapper ordersMapper;
+
+ @Autowired
+ private MemberMapper memberMapper;
+
+ @Autowired
+ private ShopInfoMapper shopInfoMapper;
+
+ @Autowired
+ private DriverInfoMapper driverInfoMapper;
+
+ @Autowired
+ private CategoryMapper categoryMapper;
+
+ @Autowired
+ private MultifileMapper multifileMapper;
+
+ @Autowired
+ private OrdersDetailMapper ordersDetailMapper;
+
+ @Autowired
+ private SystemDictDataBiz systemDictDataBiz;
+
+ @Autowired
+ private OrderLogService orderLogService;
+
+ @Autowired
+ private OrdersRefundMapper ordersRefundMapper;
+
+ @Autowired
+ private OtherOrdersMapper otherOrdersMapper;
+
+ @Autowired
+ private OrderCommentMapper orderCommentMapper;
+
+ @Autowired
+ private RevenueMapper revenueMapper;
+
+ @Autowired
+ private WxMiniUtilService wxMiniUtilService;
+
+ @Autowired
+ private SystemUserMapper systemUserMapper;
+
+ @Autowired
+ private PricingRuleMapper pricingRuleMapper;
+ @Autowired
+ private RedisTemplate<String, Object> redisTemplate;
+
+ @Autowired
+ private AreasBiz areasBiz;
+
+ @Autowired
+ private OperationConfigBiz operationConfigBiz;
+
+ @Autowired
+ private AreasService areasService;
@Override
public Integer create(Orders orders) {
@@ -102,11 +192,18 @@
MPJLambdaWrapper<Orders> queryWrapper = new MPJLambdaWrapper<Orders>()
.selectAll(Orders.class)
.selectAs(Category::getDetail, Orders::getOrderLevel)
- .leftJoin(Category.class, Category::getId, Orders::getGoodType);
+ .select("s1.name", Orders::getDepositShopName)
+ .leftJoin(Category.class, Category::getId, Orders::getGoodType)
+ .leftJoin(DriverInfo.class, DriverInfo::getId, Orders::getAcceptDriver)
+ .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
+ .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID");
+ ;
Utils.MP.blankToNull(pageWrap.getModel());
pageWrap.getModel().setDeleted(Constants.ZERO);
queryWrapper.eq(pageWrap.getModel().getDeleted() != null, Orders::getDeleted, pageWrap.getModel().getDeleted());
queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getCode()), Orders::getCode, pageWrap.getModel().getCode());
+ queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getDepositShopName()), "s1.name", pageWrap.getModel().getDepositShopName());
+ queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getTakeShopName()), "s2.name", pageWrap.getModel().getTakeShopName());
queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getGoodsInfo()), Orders::getGoodsInfo, pageWrap.getModel().getGoodsInfo());
queryWrapper.ge(pageWrap.getModel().getCreateStartTime() != null, Orders::getCreateTime, Utils.Date.getStart(pageWrap.getModel().getCreateStartTime()));
queryWrapper.le(pageWrap.getModel().getCreateEndTime() != null, Orders::getCreateTime, Utils.Date.getEnd(pageWrap.getModel().getCreateEndTime()));
@@ -114,6 +211,8 @@
queryWrapper.eq(pageWrap.getModel().getType() != null, Orders::getType, pageWrap.getModel().getType());
queryWrapper.eq(pageWrap.getModel().getStatus() != null, Orders::getStatus, pageWrap.getModel().getStatus());
queryWrapper.eq(pageWrap.getModel().getTakeShopId() != null, Orders::getTakeShopId, pageWrap.getModel().getTakeShopId());
+ queryWrapper.and(pageWrap.getModel().getDriverKeyword() != null, i->i.like(DriverInfo::getName, pageWrap.getModel().getDriverKeyword())
+ .or().like(DriverInfo::getTelephone, pageWrap.getModel().getDriverKeyword()));
for (PageWrap.SortData sortData : pageWrap.getSorts()) {
if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) {
queryWrapper.orderByDesc(sortData.getProperty());
@@ -125,9 +224,2637 @@
}
@Override
+ public OrderSummaryVO findSummary(PageWrap<Orders> pageWrap) {
+ // 鏋勫缓涓巉indPage鐩稿悓鐨勬煡璇㈡潯浠�
+ MPJLambdaWrapper<Orders> queryWrapper = new MPJLambdaWrapper<Orders>()
+ .leftJoin(Category.class, Category::getId, Orders::getGoodType)
+ .leftJoin(DriverInfo.class, DriverInfo::getId, Orders::getAcceptDriver)
+ .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
+ .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID");
+ Utils.MP.blankToNull(pageWrap.getModel());
+ pageWrap.getModel().setDeleted(Constants.ZERO);
+ queryWrapper.eq(pageWrap.getModel().getDeleted() != null, Orders::getDeleted, pageWrap.getModel().getDeleted());
+ queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getCode()), Orders::getCode, pageWrap.getModel().getCode());
+ queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getDepositShopName()), "s1.name", pageWrap.getModel().getDepositShopName());
+ queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getTakeShopName()), "s2.name", pageWrap.getModel().getTakeShopName());
+ queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getGoodsInfo()), Orders::getGoodsInfo, pageWrap.getModel().getGoodsInfo());
+ queryWrapper.ge(pageWrap.getModel().getCreateStartTime() != null, Orders::getCreateTime, Utils.Date.getStart(pageWrap.getModel().getCreateStartTime()));
+ queryWrapper.le(pageWrap.getModel().getCreateEndTime() != null, Orders::getCreateTime, Utils.Date.getEnd(pageWrap.getModel().getCreateEndTime()));
+ queryWrapper.eq(pageWrap.getModel().getDepositShopId() != null, Orders::getDepositShopId, pageWrap.getModel().getDepositShopId());
+ queryWrapper.eq(pageWrap.getModel().getType() != null, Orders::getType, pageWrap.getModel().getType());
+ queryWrapper.eq(pageWrap.getModel().getStatus() != null, Orders::getStatus, pageWrap.getModel().getStatus());
+ queryWrapper.eq(pageWrap.getModel().getTakeShopId() != null, Orders::getTakeShopId, pageWrap.getModel().getTakeShopId());
+ queryWrapper.and(pageWrap.getModel().getDriverKeyword() != null, i->i.like(DriverInfo::getName, pageWrap.getModel().getDriverKeyword())
+ .or().like(DriverInfo::getTelephone, pageWrap.getModel().getDriverKeyword()));
+
+ queryWrapper.select(
+ "IFNULL(SUM(t.total_amount), 0) as total_amount_sum",
+ "IFNULL(SUM(CASE WHEN t.settlement_status = 1 THEN t.total_amount ELSE 0 END), 0) as settled_total_amount_sum",
+ "IFNULL(SUM(t.driver_fee), 0) as driver_fee_sum",
+ "IFNULL(SUM(CASE WHEN t.settlement_status = 1 THEN t.driver_fee ELSE 0 END), 0) as settled_driver_fee_sum"
+ );
+ queryWrapper.groupBy("1=1");
+
+ List<Map<String, Object>> result = ordersMapper.selectJoinMaps(queryWrapper);
+ OrderSummaryVO vo = new OrderSummaryVO();
+ if (result != null && !result.isEmpty()) {
+ Map<String, Object> row = result.get(0);
+ vo.setTotalAmountSum(toLong(row.get("total_amount_sum")));
+ vo.setSettledTotalAmountSum(toLong(row.get("settled_total_amount_sum")));
+ vo.setDriverFeeSum(toLong(row.get("driver_fee_sum")));
+ vo.setSettledDriverFeeSum(toLong(row.get("settled_driver_fee_sum")));
+ } else {
+ vo.setTotalAmountSum(0L);
+ vo.setSettledTotalAmountSum(0L);
+ vo.setDriverFeeSum(0L);
+ vo.setSettledDriverFeeSum(0L);
+ }
+ return vo;
+ }
+
+ private Long toLong(Object val) {
+ if (val == null) return 0L;
+ if (val instanceof Number) return ((Number) val).longValue();
+ return Long.parseLong(val.toString());
+ }
+
+ @Override
+ public BigDecimal calculateInsuranceFee(BigDecimal declaredValue) {
+ if (declaredValue == null || declaredValue.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ZERO;
+ }
+ String rateStr = systemDictDataBiz.queryByCode(Constants.OPERATION_CONFIG, Constants.OP_INSURANCE_RATE).getCode();
+ BigDecimal rate = new BigDecimal(rateStr);
+ return declaredValue.multiply(rate).setScale(2, BigDecimal.ROUND_HALF_UP);
+ }
+
+ /**
+ * 璁$畻灏卞湴瀛樺彇棰勪及璐圭敤
+ *
+ * 璁$畻瑙勫垯锛�
+ * 1. 鏍规嵁鍩庡競+鐗╁搧绫诲瀷 鏌ヨ pricing_rule(type=0)锛宖ieldA=categoryId, fieldB=鍗曚环(鍒�/澶�)
+ * 2. 姣忛」灏忚 = 鍗曚环 脳 鏁伴噺 脳 澶╂暟
+ * 3. 鐗╁搧浠锋牸 = 鍚勯」灏忚涔嬪拰
+ * 4. 淇濅环璐圭敤 = 鎶ヤ环閲戦 脳 淇濅环璐圭巼锛堝瓧鍏� INSURANCE_RATE锛夛紝鍏冭浆鍒�
+ * 5. 鎬讳环鏍� = 鐗╁搧浠锋牸 + 淇濅环璐圭敤
+ *
+ * @param dto 灏卞湴瀛樺彇璁′环璇锋眰鍙傛暟
+ * @return 浠锋牸璁$畻缁撴灉
+ */
+ @Override
+ public PriceCalculateVO calculateLocalPrice(CalculateLocalPriceDTO dto) {
+ // 澶╂暟鏍¢獙锛屾渶灏�1澶�
+ int days = dto.getEstimatedDepositDays() != null && dto.getEstimatedDepositDays() > 0
+ ? dto.getEstimatedDepositDays() : 1;
+
+ // 鏀堕泦鎵�鏈夌墿鍝佺被鍨婭D
+ List<Integer> categoryIds = new ArrayList<>();
+ for (OrderItemDTO item : dto.getItems()) {
+ categoryIds.add(item.getCategoryId());
+ }
+
+ // 鎵归噺鏌ヨ璁′环瑙勫垯 pricing_rule type=0锛歠ieldA=categoryId, fieldB=鍗曚环(鍒�/澶�)
+ List<String> fieldAList = new ArrayList<>();
+ for (Integer cid : categoryIds) {
+ fieldAList.add(String.valueOf(cid));
+ }
+ List<PricingRule> rules = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
+ .eq(PricingRule::getDeleted, Constants.ZERO)
+ .eq(PricingRule::getType, Constants.ZERO)
+ .eq(PricingRule::getCityId, dto.getCityId())
+ .in(PricingRule::getFieldA, fieldAList));
+ Map<String, PricingRule> ruleMap = new HashMap<>();
+ for (PricingRule r : rules) {
+ ruleMap.put(r.getFieldA(), r);
+ }
+
+ // 鎵归噺鏌ヨ鐗╁搧绫诲瀷鍚嶇О
+ List<Category> categories = categoryMapper.selectBatchIds(categoryIds);
+ Map<Integer, String> categoryNameMap = new HashMap<>();
+ Map<Integer, String> categoryDetailMap = new HashMap<>();
+ for (Category c : categories) {
+ categoryNameMap.put(c.getId(), c.getName());
+ categoryDetailMap.put(c.getId(), c.getDetail());
+ }
+
+ // 璁$畻姣忛」鐗╁搧璐圭敤锛氬皬璁� = 鍗曚环 脳 鏁伴噺 脳 澶╂暟
+ List<ItemPriceVO> itemList = new ArrayList<>();
+ long itemPriceTotal = 0L;
+
+ for (OrderItemDTO item : dto.getItems()) {
+ PricingRule rule = ruleMap.get(String.valueOf(item.getCategoryId()));
+ if (rule == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(),
+ "鏈壘鍒拌鍩庡競鐗╁搧绫诲瀷鐨勮浠疯鍒�");
+ }
+
+ long unitPrice = Long.parseLong(rule.getFieldB());
+ long subtotal = unitPrice * item.getQuantity() * days;
+
+ ItemPriceVO vo = new ItemPriceVO();
+ vo.setCategoryId(item.getCategoryId());
+ vo.setCategoryName(categoryNameMap.getOrDefault(item.getCategoryId(), ""));
+ vo.setDetail(categoryDetailMap.get(item.getCategoryId()));
+ vo.setQuantity(item.getQuantity());
+ vo.setUnitPrice(unitPrice);
+ vo.setLocallyPrice(unitPrice);
+ vo.setSubtotal(subtotal);
+ itemList.add(vo);
+
+ itemPriceTotal += subtotal;
+ }
+
+ // 淇濅环璐圭敤锛氭姤浠烽噾棰� 脳 淇濅环璐圭巼(瀛楀吀 INSURANCE_RATE)锛屽厓鈫掑垎
+ long insuranceFeeFen = 0L;
+ if (Boolean.TRUE.equals(dto.getInsured()) && dto.getDeclaredAmount() != null) {
+ BigDecimal insuranceFeeYuan = calculateInsuranceFee(dto.getDeclaredAmount());
+ insuranceFeeFen = insuranceFeeYuan.multiply(new BigDecimal(100)).longValue();
+ }
+
+ // 鎬讳环鏍� = 鐗╁搧浠锋牸 + 淇濅环璐圭敤
+ long totalPrice = itemPriceTotal + insuranceFeeFen;
+
+ PriceCalculateVO result = new PriceCalculateVO();
+ result.setItemList(itemList);
+ result.setItemPrice(itemPriceTotal);
+ result.setInsuranceFee(insuranceFeeFen);
+ result.setTotalPrice(totalPrice);
+ result.setDays(days);
+ result.setUrgentFee(0L);
+ return result;
+ }
+
+ /**
+ * 璁$畻寮傚湴瀛樺彇棰勪及璐圭敤
+ *
+ * 璁$畻瑙勫垯锛�
+ * 1. 璋冪敤鑵捐鍦板浘API璁$畻瀵勪欢鐐逛笌鍙栦欢鐐圭殑椹捐溅璺濈(绫斥啋鍏噷)
+ * 2. 鏍规嵁鍩庡競+鐗╁搧绫诲瀷 鏌ヨ pricing_rule(type=1)锛�
+ * fieldB=璧锋璺濈(km), fieldC=璧锋浠�(鍒�), fieldD=瓒呭嚭璺濈鍗曚綅(km), fieldE=瓒呭嚭璺濈鍗曚环(鍒�)
+ * 3. 姣忛」杩愯垂鍗曚环锛�
+ * - 璺濈 鈮� 璧锋璺濈 鈫� 鍗曚环 = 璧锋浠�
+ * - 璺濈 > 璧锋璺濈 鈫� 鍗曚环 = 璧锋浠� + ceil((璺濈-璧锋璺濈)/瓒呭嚭璺濈鍗曚綅) 脳 瓒呭嚭璺濈鍗曚环
+ * 4. 灏忚 = 杩愯垂鍗曚环 脳 鏁伴噺
+ * 5. 鐗╁搧浠锋牸 = 鍚勯」灏忚涔嬪拰
+ * 6. 淇濅环璐圭敤 = 鎶ヤ环閲戦 脳 淇濅环璐圭巼锛堝瓧鍏� INSURANCE_RATE锛夛紝鍏冭浆鍒�
+ * 7. 鍔犳�ヨ垂鐢� = 鐗╁搧浠锋牸 脳 鍔犳�ョ郴鏁帮紙瀛楀吀 URGENT_COEFFICIENT锛�
+ * 8. 鎬讳环鏍� = 鐗╁搧浠锋牸 + 淇濅环璐圭敤 + 鍔犳�ヨ垂鐢�
+ *
+ * @param dto 寮傚湴瀛樺彇璁′环璇锋眰鍙傛暟
+ * @return 浠锋牸璁$畻缁撴灉
+ */
+ @Override
+ public PriceCalculateVO calculateRemotePrice(CalculateRemotePriceDTO dto) {
+ // 1. 璋冪敤鑵捐鍦板浘璺濈鐭╅樀API璁$畻椹捐溅璺濈
+ String from = dto.getFromLat() + "," + dto.getFromLgt();
+ String to = dto.getToLat() + "," + dto.getToLgt();
+ JSONObject distanceResult = MapUtil.distanceSingle("driving", from, to);
+ BigDecimal distance = distanceResult.getBigDecimal("distance");
+ // distance 鍗曚綅涓虹背锛岃浆涓哄叕閲�
+ BigDecimal distanceKm = distance.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP);
+
+ // 鏀堕泦鎵�鏈夌墿鍝佺被鍨婭D
+ List<Integer> categoryIds = new ArrayList<>();
+ for (OrderItemDTO item : dto.getItems()) {
+ categoryIds.add(item.getCategoryId());
+ }
+
+ // 2. 鎵归噺鏌ヨ閰嶉�佽浠疯鍒� pricing_rule type=1
+ List<String> fieldAList = new ArrayList<>();
+ for (Integer cid : categoryIds) {
+ fieldAList.add(String.valueOf(cid));
+ }
+ List<PricingRule> rules = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
+ .eq(PricingRule::getDeleted, Constants.ZERO)
+ .eq(PricingRule::getType, Constants.ONE)
+ .eq(PricingRule::getCityId, dto.getCityId())
+ .in(PricingRule::getFieldA, fieldAList));
+ Map<String, PricingRule> ruleMap = new HashMap<>();
+ for (PricingRule r : rules) {
+ ruleMap.put(r.getFieldA(), r);
+ }
+
+ // 鏌ヨ灏卞湴瀛樺彇璁′环瑙勫垯 pricing_rule type=0锛岀敤浜庤幏鍙� locallyPrice
+ List<PricingRule> localRules = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
+ .eq(PricingRule::getDeleted, Constants.ZERO)
+ .eq(PricingRule::getType, Constants.ZERO)
+ .eq(PricingRule::getCityId, dto.getCityId())
+ .in(PricingRule::getFieldA, fieldAList));
+ Map<String, PricingRule> localRuleMap = new HashMap<>();
+ for (PricingRule r : localRules) {
+ localRuleMap.put(r.getFieldA(), r);
+ }
+
+ // 鎵归噺鏌ヨ鐗╁搧绫诲瀷鍚嶇О
+ List<Category> categories = categoryMapper.selectBatchIds(categoryIds);
+ Map<Integer, String> categoryNameMap = new HashMap<>();
+ Map<Integer, String> categoryDetailMap = new HashMap<>();
+ for (Category c : categories) {
+ categoryNameMap.put(c.getId(), c.getName());
+ categoryDetailMap.put(c.getId(), c.getDetail());
+ }
+
+ // 3. 閫愰」璁$畻杩愯垂锛氳捣姝ヤ环 + 瓒呭嚭閮ㄥ垎闃舵浠�
+ List<ItemPriceVO> itemList = new ArrayList<>();
+ long itemPriceTotal = 0L;
+
+ for (OrderItemDTO item : dto.getItems()) {
+ PricingRule rule = ruleMap.get(String.valueOf(item.getCategoryId()));
+ if (rule == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(),
+ "鏈壘鍒拌鍩庡競鐗╁搧绫诲瀷鐨勯厤閫佽浠疯鍒�");
+ }
+
+ // fieldB=璧锋璺濈(km), fieldC=璧锋浠�(鍒�), fieldD=瓒呭嚭璺濈鍗曚綅(km), fieldE=瓒呭嚭璺濈鍗曚环(鍒�)
+ BigDecimal startDistance = new BigDecimal(rule.getFieldB());
+ long startPrice = Long.parseLong(rule.getFieldC());
+ BigDecimal extraDistanceUnit = new BigDecimal(rule.getFieldD());
+ long extraPricePerUnit = Long.parseLong(rule.getFieldE());
+
+ // 闃舵璁′环锛氳窛绂� 鈮� 璧锋璺濈鍙栬捣姝ヤ环锛岃秴鍑烘寜 ceil(瓒呭嚭璺濈/鍗曚綅) 脳 鍗曚环绱姞
+ long unitPrice;
+ if (distanceKm.compareTo(startDistance) <= 0) {
+ unitPrice = startPrice;
+ } else {
+ BigDecimal extraKm = distanceKm.subtract(startDistance);
+ BigDecimal extraCount = extraKm.divide(extraDistanceUnit, 0, RoundingMode.CEILING);
+ unitPrice = startPrice + extraCount.longValue() * extraPricePerUnit;
+ }
+
+ long subtotal = unitPrice * item.getQuantity();
+
+ // 灏卞湴瀛樺彇鍗曚环
+ PricingRule localRule = localRuleMap.get(String.valueOf(item.getCategoryId()));
+ Long locallyPrice = localRule != null ? Long.parseLong(localRule.getFieldB()) : null;
+
+ ItemPriceVO vo = new ItemPriceVO();
+ vo.setCategoryId(item.getCategoryId());
+ vo.setCategoryName(categoryNameMap.getOrDefault(item.getCategoryId(), ""));
+ vo.setDetail(categoryDetailMap.get(item.getCategoryId()));
+ vo.setQuantity(item.getQuantity());
+ vo.setUnitPrice(unitPrice);
+ vo.setLocallyPrice(locallyPrice);
+ vo.setSubtotal(subtotal);
+ vo.setStartDistance(startDistance);
+ vo.setStartPrice(startPrice);
+ vo.setExtraDistance(extraDistanceUnit);
+ vo.setExtraPrice(extraPricePerUnit);
+ itemList.add(vo);
+
+ itemPriceTotal += subtotal;
+ }
+
+ // 4. 淇濅环璐圭敤锛氭姤浠烽噾棰� 脳 淇濅环璐圭巼(瀛楀吀 INSURANCE_RATE)锛屽厓鈫掑垎
+ long insuranceFeeFen = 0L;
+ if (Boolean.TRUE.equals(dto.getInsured()) && dto.getDeclaredAmount() != null) {
+ BigDecimal insuranceFeeYuan = calculateInsuranceFee(dto.getDeclaredAmount());
+ insuranceFeeFen = insuranceFeeYuan.multiply(new BigDecimal(100)).longValue();
+ }
+
+ // 5. 鍔犳�ヨ垂鐢細鐗╁搧浠锋牸 脳 鍔犳�ョ郴鏁�(瀛楀吀 URGENT_COEFFICIENT)
+ long urgentFeeFen = 0L;
+ if (Boolean.TRUE.equals(dto.getUrgent())) {
+ String urgentRateStr = systemDictDataBiz.queryByCode(
+ Constants.OPERATION_CONFIG, Constants.OP_URGENT_COEFFICIENT).getCode();
+ BigDecimal urgentRate = new BigDecimal(urgentRateStr);
+ urgentFeeFen = new BigDecimal(itemPriceTotal).multiply(urgentRate)
+ .setScale(0, RoundingMode.HALF_UP).longValue();
+ }
+
+ // 6. 鎬讳环鏍� = 鐗╁搧浠锋牸 + 淇濅环璐圭敤 + 鍔犳�ヨ垂鐢�
+ long totalPrice = itemPriceTotal + insuranceFeeFen + urgentFeeFen;
+
+ PriceCalculateVO result = new PriceCalculateVO();
+ result.setItemList(itemList);
+ result.setItemPrice(itemPriceTotal);
+ result.setInsuranceFee(insuranceFeeFen);
+ result.setUrgentFee(urgentFeeFen);
+ result.setTotalPrice(totalPrice);
+ result.setDistance(distanceKm);
+ return result;
+ }
+
+ @Override
public long count(Orders orders) {
QueryWrapper<Orders> wrapper = new QueryWrapper<>(orders);
return ordersMapper.selectCount(wrapper);
}
+ /**
+ * 鍒涘缓璁㈠崟
+ *
+ * 涓氬姟娴佺▼锛�
+ * 1. 鍙傛暟鏍¢獙锛氬繀濉瓧娈点�佹椂闂撮『搴忋�佺墿鍝佷笉閲嶅銆佸紓鍦板繀濉湇鍔℃椂鏁�
+ * 2. 鏌ヨ瀵勪欢搴楅摵淇℃伅锛堣幏鍙栫粡绾害銆佸湴鍧�锛�
+ * 3. 璋冪敤璁′环鎺ュ彛璁$畻璐圭敤锛坈alculateLocalPrice / calculateRemotePrice锛�
+ * 4. 鐢熸垚璁㈠崟缂栧彿锛欽C + yyyyMMddHHmmss + 4浣嶉殢鏈烘暟
+ * 5. 鍒涘缓璁㈠崟涓昏〃 Orders锛堢姸鎬�=寰呮敮浠橈級
+ * 6. 鍒涘缓璁㈠崟鏄庣粏琛� OrdersDetail锛堟瘡椤圭墿鍝佸昂瀵革級
+ * 7. 鍒涘缓闄勪欢璁板綍 Multifile锛堢墿鍝佸浘鐗� objType=12锛�
+ *
+ * @param dto 鍒涘缓璁㈠崟璇锋眰鍙傛暟
+ * @param memberId 褰撳墠鐧诲綍浼氬憳ID
+ * @return 璁㈠崟ID
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public PayResponse createOrder(CreateOrderDTO dto, Integer memberId) {
+ String lockKey = Constants.GOODS_ORDER_CREATE_LOCK + memberId;
+ //鍒ゆ柇鍓嶇鏄惁鍦ㄥ悓涓�椤甸潰鍒涘缓浜嗕袱娆¤鍗�
+ if (redisTemplate.hasKey(lockKey)) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"姝e湪鍒涘缓璁㈠崟锛岃鍕块噸澶嶈皟鐢紒");
+ } else {
+ redisTemplate.opsForValue().set(lockKey, "", 5, TimeUnit.SECONDS);
+ }
+ Date now = new Date();
+ // ========== 1. 鍙傛暟鏍¢獙 ==========
+ // 棰勮鍒板簵瀛樹欢鏃堕棿蹇呴』灏忎簬棰勮鍒板簵鍙栦欢鏃堕棿
+ java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm");
+ Date depositTime;
+ Date takeTime;
+ try {
+ depositTime = sdf.parse(dto.getExpectedDepositTime());
+ takeTime = sdf.parse(dto.getExpectedTakeTime());
+ } catch (java.text.ParseException e) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏃堕棿鏍煎紡閿欒锛屾纭牸寮忥細yyyy-MM-dd HH:mm");
+ }
+ if (!depositTime.before(takeTime)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "棰勮鍒板簵瀛樹欢鏃堕棿蹇呴』灏忎簬棰勮鍒板簵鍙栦欢鏃堕棿");
+ }
+
+ // 鐗╁搧灏哄涓嶈兘閲嶅
+ List<Integer> categoryIds = new ArrayList<>();
+ for (OrderItemDTO item : dto.getItems()) {
+ if (categoryIds.contains(item.getCategoryId())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐗╁搧灏哄涓嶈兘閲嶅");
+ }
+ categoryIds.add(item.getCategoryId());
+ }
+
+ // 鐗╁搧鍥剧墖鏈�澶�3寮�
+ if (dto.getGoodsImages() != null && dto.getGoodsImages().size() > 3) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐗╁搧鍥剧墖鏈�澶�3寮�");
+ }
+
+ // ========== 2. 鏍¢獙鐗╁搧绫诲瀷 ==========
+ Category goodTypeCategory = categoryMapper.selectById(dto.getGoodType());
+ if (goodTypeCategory == null || Constants.equalsInteger(goodTypeCategory.getDeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐗╁搧绫诲瀷涓嶅瓨鍦�");
+ }
+ if (!Constants.equalsInteger(goodTypeCategory.getType(), Constants.TWO)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐗╁搧绫诲瀷鍙傛暟閿欒");
+ }
+
+ // ========== 3. 鏌ヨ瀵勪欢搴楅摵淇℃伅 ==========
+ ShopInfo depositShop = shopInfoMapper.selectById(dto.getDepositShopId());
+ if (depositShop == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "瀵勪欢搴楅摵涓嶅瓨鍦�");
+ }
+
+ // ========== 4. 璁$畻璐圭敤 ==========
+
+ // 寮傚湴瀵勫瓨锛氭牎楠屽彇浠剁偣
+ BigDecimal takeLat = null;
+ BigDecimal takeLgt = null;
+ String takeLocationValue = null;
+ ShopInfo takeShop = null;
+ if (Constants.ONE.equals(dto.getType())) {
+ // 寮傚湴蹇呭~鏈嶅姟鏃舵晥
+ if (dto.getIsUrgent() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "寮傚湴瀵勫瓨鏈嶅姟鏃舵晥涓嶈兘涓虹┖");
+ }
+ // 鍙栦欢鐐癸細搴楅摵 or 鑷�夌偣锛岃嚦灏戞彁渚涗竴缁�
+ if (dto.getTakeShopId() != null) {
+ takeShop = shopInfoMapper.selectById(dto.getTakeShopId());
+ if (takeShop == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鍙栦欢搴楅摵涓嶅瓨鍦�");
+ }
+ takeLat = BigDecimal.valueOf(takeShop.getLatitude());
+ takeLgt = BigDecimal.valueOf(takeShop.getLongitude());
+ takeLocationValue = takeShop.getAddress();
+ } else if (dto.getTakeLat() != null && dto.getTakeLgt() != null && StringUtils.isNotBlank(dto.getTakeLocation())) {
+ takeLat = dto.getTakeLat();
+ takeLgt = dto.getTakeLgt();
+ takeLocationValue = dto.getTakeLocation();
+ } else {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇烽�夋嫨鍙栦欢搴楅摵鎴栬緭鍏ヨ嚜閫夊彇浠跺湴鍧�");
+ }
+ } else {
+ // 灏卞湴瀛樺彇锛氬彇浠堕棬搴楀悓瀵勪欢闂ㄥ簵
+ takeShop = depositShop;
+ }
+
+ // ========== 3. 璁$畻璐圭敤 ==========
+ PriceCalculateVO priceResult;
+ if (Constants.ZERO.equals(dto.getType())) {
+ // 灏卞湴瀵勫瓨锛氳绠楀ぉ鏁�
+ long diffMs = takeTime.getTime() - depositTime.getTime();
+ int days = (int) Math.max(1, (diffMs / (1000 * 60 * 60 * 24)) + 1);
+
+ CalculateLocalPriceDTO priceDTO = new CalculateLocalPriceDTO();
+ priceDTO.setCityId(dto.getCityId());
+ priceDTO.setEstimatedDepositDays(days);
+ priceDTO.setItems(dto.getItems());
+ priceDTO.setInsured(dto.getDeclaredAmount() != null && dto.getDeclaredAmount().compareTo(BigDecimal.ZERO) > 0);
+ priceDTO.setDeclaredAmount(dto.getDeclaredAmount());
+ priceResult = calculateLocalPrice(priceDTO);
+ } else {
+ // 寮傚湴瀵勫瓨
+ CalculateRemotePriceDTO priceDTO = new CalculateRemotePriceDTO();
+ priceDTO.setCityId(dto.getCityId());
+ priceDTO.setFromLat(BigDecimal.valueOf(depositShop.getLatitude()));
+ priceDTO.setFromLgt(BigDecimal.valueOf(depositShop.getLongitude()));
+ priceDTO.setToLat(takeLat);
+ priceDTO.setToLgt(takeLgt);
+ priceDTO.setItems(dto.getItems());
+ priceDTO.setInsured(dto.getDeclaredAmount() != null && dto.getDeclaredAmount().compareTo(BigDecimal.ZERO) > 0);
+ priceDTO.setDeclaredAmount(dto.getDeclaredAmount());
+ priceDTO.setUrgent(Constants.ONE.equals(dto.getIsUrgent()));
+ priceResult = calculateRemotePrice(priceDTO);
+ }
+
+ // ========== 5. 鐢熸垚璁㈠崟缂栧彿 ==========
+ String orderCode = "JC" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(now)
+ + String.format("%04d", new java.util.Random().nextInt(10000));
+ // 鐢熸垚32浣嶅敮涓�绗笁鏂硅鍗曠紪鍙�
+ String orderTradeNo = generateOrderTradeNo();
+
+ // ========== 6. 鍒涘缓璁㈠崟涓昏〃 ==========
+ Orders orders = new Orders();
+ orders.setCode(orderCode);
+ orders.setOutTradeNo(orderTradeNo);
+ orders.setMemberId(memberId);
+ orders.setType(dto.getType());
+ orders.setCityId(String.valueOf(dto.getCityId()));
+ orders.setStatus(Constants.ZERO); // 寰呮敮浠�
+ orders.setPayStatus(Constants.ZERO); // 鏈敮浠�
+ orders.setCommentStatus(Constants.ZERO); // 鏈瘎浠�
+ orders.setSettlementStatus(Constants.ZERO); // 鏈粨绠�
+ orders.setDeleted(Constants.ZERO);
+ orders.setCreateTime(now);
+ orders.setUpdateTime(now);
+
+ // 瀵勪欢淇℃伅
+ orders.setDepositShopId(dto.getDepositShopId());
+ // 瀛樹欢鍦扮偣锛氱渷甯傚尯鍏ㄨ矾寰� + 鍦板潃鎻忚堪
+ String depositLocationRemark = depositShop.getAddress();
+ if (depositShop.getAreaId() != null) {
+ Areas depositArea = areasBiz.resolveArea(depositShop.getAreaId());
+ if (depositArea != null) {
+ depositLocationRemark = depositArea.getProvinceName() + depositArea.getCityName() + depositArea.getName() + depositShop.getAddress();
+ }
+ }
+ orders.setDepositLocation(depositLocationRemark);
+ orders.setDepositLocationRemark(depositShop.getAddress());
+ orders.setDepositLat(BigDecimal.valueOf(depositShop.getLatitude()));
+ orders.setDepositLgt(BigDecimal.valueOf(depositShop.getLongitude()));
+
+ // 鍙栦欢淇℃伅
+ orders.setTakeUser(dto.getTakeUser());
+ orders.setTakePhone(dto.getTakePhone());
+ orders.setExpectedDepositTime(depositTime);
+ orders.setExpectedTakeTime(takeTime);
+ // 璁$畻棰勮瀛樻斁澶╂暟
+ long dayDiff = (takeTime.getTime() - depositTime.getTime()) / (1000 * 60 * 60 * 24);
+ orders.setEstimatedDepositDays((int) Math.max(1, dayDiff + 1));
+
+ if (Constants.ONE.equals(dto.getType())) {
+ // 寮傚湴锛氬彇浠剁偣淇℃伅
+ orders.setTakeShopId(dto.getTakeShopId());
+ orders.setTakeLocation(takeLocationValue);
+ orders.setTakeLat(takeLat);
+ orders.setTakeLgt(takeLgt);
+ orders.setIsUrgent(dto.getIsUrgent());
+ } else {
+ // 灏卞湴锛氬彇浠剁偣鍚屽瘎浠跺簵閾�
+ orders.setTakeShopId(dto.getDepositShopId());
+ orders.setTakeLocation(depositShop.getAddress());
+ orders.setTakeLat(BigDecimal.valueOf(depositShop.getLatitude()));
+ orders.setTakeLgt(BigDecimal.valueOf(depositShop.getLongitude()));
+ orders.setIsUrgent(Constants.ZERO);
+ }
+
+ // 鐗╁搧淇℃伅
+ orders.setGoodType(dto.getGoodType());
+ // 鎷兼帴鐗╁搧淇℃伅锛氱墿鍝佺被鍨嬪悕绉般�佸昂瀵稿悕绉�*鏁伴噺锛堟暟缁勫瓧绗︿覆锛�
+ List<String> goodsParts = new ArrayList<>();
+ for (ItemPriceVO itemVO : priceResult.getItemList()) {
+ goodsParts.add(itemVO.getCategoryName() + "*" + itemVO.getQuantity());
+ }
+ orders.setGoodsInfo(goodTypeCategory.getName() + "銆�" + String.join("锛�", goodsParts));
+ orders.setRemark(dto.getRemark());
+ orders.setSelfTake(Constants.ZERO);
+
+ // 璐圭敤淇℃伅(鍒�)
+ orders.setBasicAmount(priceResult.getItemPrice());
+ orders.setEstimatedAmount(priceResult.getTotalPrice());
+ orders.setTotalAmount(priceResult.getTotalPrice());
+ orders.setUrgentAmount(priceResult.getUrgentFee());
+ if (dto.getDeclaredAmount() != null && dto.getDeclaredAmount().compareTo(BigDecimal.ZERO) > 0) {
+ orders.setDeclaredAmount(dto.getDeclaredAmount().multiply(new BigDecimal(100)).longValue());
+ } else {
+ orders.setDeclaredAmount(0L);
+ }
+ orders.setDeclaredFee(priceResult.getInsuranceFee());
+ orders.setPrice(priceResult.getItemPrice());
+
+ // 钖叕璁$畻涓庡崰姣斿瓨鍌�
+ calculateAndSetFeeAllocation(orders, depositShop, takeShop);
+
+ ordersMapper.insert(orders);
+ Integer orderId = orders.getId();
+
+ // ========== 7. 鍒涘缓璁㈠崟鏄庣粏 ==========
+ for (ItemPriceVO itemVO : priceResult.getItemList()) {
+ OrdersDetail detail = new OrdersDetail();
+ detail.setOrderId(orderId);
+ detail.setLuggageId(itemVO.getCategoryId());
+ detail.setLuggageName(itemVO.getCategoryName());
+ detail.setLuggageDetail(itemVO.getDetail());
+ detail.setNum(itemVO.getQuantity());
+ detail.setUnitPrice(itemVO.getUnitPrice());
+ detail.setStartDistance(itemVO.getStartDistance());
+ detail.setStartPrice(itemVO.getStartPrice());
+ detail.setExtraDistance(itemVO.getExtraDistance());
+ detail.setExtraPrice(itemVO.getExtraPrice());
+ detail.setLocallyPrice(itemVO.getLocallyPrice());
+ detail.setDeleted(Constants.ZERO);
+ detail.setCreateTime(now);
+ ordersDetailMapper.insert(detail);
+ }
+ // ========== 8. 淇濆瓨鐗╁搧鍥剧墖闄勪欢 ==========
+ if (dto.getGoodsImages() != null && !dto.getGoodsImages().isEmpty()) {
+ int sortNum = 1;
+ for (String imgUrl : dto.getGoodsImages()) {
+ Multifile multifile = new Multifile();
+ multifile.setObjId(orderId);
+ multifile.setObjType(Constants.FileType.ORDER_FILE.getKey());
+ multifile.setType(Constants.ZERO);
+ multifile.setFileurl(imgUrl);
+ multifile.setIsdeleted(Constants.ZERO);
+ multifile.setCreateDate(now);
+ multifile.setSortnum(sortNum++);
+ multifileMapper.insert(multifile);
+ }
+ }
+ // ========== 9. 鍞よ捣寰俊鏀粯 ==========
+ Member member = memberMapper.selectById(memberId);
+ if (member == null || StringUtils.isBlank(member.getOpenid())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐢ㄦ埛淇℃伅寮傚父锛屾棤娉曞彂璧锋敮浠�");
+ }
+ PayResponse payResponse = wxPay(orders, member.getOpenid(), Constants.OrdersAttach.STORAGE_ORDER);
+ payResponse.setLockKey(lockKey);
+ return payResponse;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public PayResponse continuePay(Integer orderId, Integer memberId) {
+ // 1. 鏌ヨ璁㈠崟
+ Orders orders = ordersMapper.selectById(orderId);
+ if (Objects.isNull(orders) || Constants.equalsInteger(orders.getDeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+ // 2. 鏍¢獙璁㈠崟褰掑睘
+ if (!Constants.equalsInteger(orders.getMemberId(), memberId)) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鏃犳潈鎿嶄綔璇ヨ鍗�");
+ }
+ // 3. 鏍¢獙璁㈠崟鐘舵�侊細浠呭緟鏀粯鍙户缁敮浠�
+ if (!Constants.equalsInteger(orders.getStatus(), Constants.ZERO)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鏀寔缁х画鏀粯");
+ }
+ // 4. 鏍¢獙鏀粯閲戦
+ if (orders.getTotalAmount() == null || orders.getTotalAmount() <= 0) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟閲戦寮傚父锛屾棤娉曞彂璧锋敮浠�");
+ }
+ // 5. 閲嶆柊鐢熸垚绗笁鏂硅鍗曠紪鍙凤紙閬垮厤閲嶅锛�
+ String orderTradeNo = generateOrderTradeNo();
+ orders.setOutTradeNo(orderTradeNo);
+ orders.setUpdateTime(new Date());
+ ordersMapper.updateById(orders);
+ // 6. 鍞よ捣寰俊鏀粯
+ Member member = memberMapper.selectById(memberId);
+ if (member == null || StringUtils.isBlank(member.getOpenid())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐢ㄦ埛淇℃伅寮傚父锛屾棤娉曞彂璧锋敮浠�");
+ }
+ return wxPay(orders, member.getOpenid(), Constants.OrdersAttach.STORAGE_ORDER);
+ }
+
+ /**
+ * 鍞よ捣寰俊灏忕▼搴忔敮浠�
+ *
+ * @param orders 璁㈠崟瀹炰綋锛堥渶瑕� code銆乼otalAmount锛�
+ * @param openid 鐢ㄦ埛寰俊openid
+ * @param ordersAttach 璁㈠崟鏀粯绫诲瀷
+ * @return PayResponse 鍖呭惈寰俊璋冭捣鍙傛暟鍜岃鍗曚富閿�
+ */
+ private PayResponse wxPay(Orders orders, String openid, Constants.OrdersAttach ordersAttach) {
+ try {
+ WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
+ request.setBody(ordersAttach.getName());
+ request.setAttach(ordersAttach.getKey());
+ request.setOutTradeNo(orders.getOutTradeNo());
+ // totalAmount 鍗曚綅涓哄垎锛學eChat Pay setTotalFee 涔熸槸鍒嗭紝鐩存帴杞琲nt
+ long totalFee = orders.getTotalAmount() != null ? orders.getTotalAmount() : 0L;
+ request.setTotalFee((int) totalFee);
+ request.setTimeStart(DateUtil.DateToString(new Date(), "yyyyMMddHHmmss"));
+ request.setSpbillCreateIp(Constants.getIpAddr());
+ request.setOpenid(openid);
+
+ Object response = WxMiniConfig.wxPayService.createOrder(request);
+
+ PayResponse payResponse = new PayResponse();
+ payResponse.setResponse(response);
+ payResponse.setOrderId(orders.getId());
+ return payResponse;
+ } catch (WxPayException e) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏀粯璋冭捣澶辫触锛�" + e.getMessage());
+ }
+ }
+
+
+
+
+
+ @Override
+ public OrderDetailVO findDetail(Integer id) {
+ Orders order = ordersMapper.selectById(id);
+ if (Objects.isNull(order)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+
+ OrderDetailVO vo = new OrderDetailVO();
+ vo.setOrder(order);
+
+ // 鍥剧墖璺緞鍓嶇紑
+ String imgPrefix = getOrdersPrefix();
+
+ // 涓嬪崟鍥剧墖 (type=12)
+ vo.setOrderFiles(getFileUrls(id, Constants.FileType.ORDER_FILE.getKey(), imgPrefix));
+
+ // 浼氬憳淇℃伅
+ if (order.getMemberId() != null) {
+ Member member = memberMapper.selectById(order.getMemberId());
+ if (member != null) {
+ vo.setMemberName(member.getName());
+ vo.setMemberPhone(member.getTelephone());
+ }
+ }
+
+ // 瀵勫瓨闂ㄥ簵淇℃伅
+ if (order.getDepositShopId() != null) {
+ ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
+ if (depositShop != null) {
+ vo.setDepositShopName(depositShop.getName());
+ vo.setDepositShopPhone(depositShop.getLinkPhone());
+ }
+ }
+
+ // 鍙栦欢闂ㄥ簵淇℃伅
+ if (order.getTakeShopId() != null) {
+ ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
+ if (takeShop != null) {
+ vo.setTakeShopName(takeShop.getName());
+ vo.setTakeShopAddress(takeShop.getAddress());
+ vo.setTakeShopPhone(takeShop.getLinkPhone());
+ }
+ }
+
+ // 鎺ュ崟鍙告満淇℃伅
+ if (order.getAcceptDriver() != null) {
+ DriverInfo driverInfo = driverInfoMapper.selectById(order.getAcceptDriver());
+ if (driverInfo != null) {
+ vo.setDriverName(driverInfo.getName());
+ }
+ }
+
+ // 閰嶉�侀檮浠跺浘鐗�
+ vo.setDepositImages(getFileUrls(id, Constants.FileType.ORDER_DEPOSIT.getKey(), imgPrefix));
+ vo.setStoreInImages(getFileUrls(id, Constants.FileType.ORDER_TAKE.getKey(), imgPrefix));
+ vo.setDriverTakeImages(getFileUrls(id, Constants.FileType.DRIVER_TAKE.getKey(), imgPrefix));
+ vo.setDriverDoneImages(getFileUrls(id, Constants.FileType.DRIVER_DONE.getKey(), imgPrefix));
+ vo.setStoreOutImages(getFileUrls(id, Constants.FileType.STORE_OUT.getKey(), imgPrefix));
+ // 鐗╁搧鏄庣粏
+ vo.setDetailList(buildDetailList(id));
+ Category category = categoryMapper.selectById(order.getGoodType());
+ if(CollectionUtils.isNotEmpty(vo.getDetailList())&&Objects.nonNull(category)){
+ for (OrderItemVO v:vo.getDetailList()) {
+ v.setTypeName(category.getName());
+ }
+ }
+
+ // 鍙栨秷/閫�娆剧姸鎬佹椂鏌ヨ閫�娆捐褰�
+ Integer status = order.getStatus();
+ if (status != null && (status == Constants.OrderStatus.overdue.getStatus()
+ || status == Constants.OrderStatus.closed.getStatus()
+ || status == Constants.OrderStatus.cancelOverdue.getStatus()
+ || status == Constants.OrderStatus.cancelling.getStatus()
+ || status == Constants.OrderStatus.cancelled.getStatus())) {
+ OrdersRefund ordersRefund = ordersRefundMapper.selectOne(
+ new QueryWrapper<OrdersRefund>().lambda()
+ .eq(OrdersRefund::getOrderId, id)
+ .eq(OrdersRefund::getDeleted, Constants.ZERO)
+ .orderByDesc(OrdersRefund::getCreateTime)
+ .last("limit 1"));
+ if (ordersRefund != null) {
+ vo.setOrdersRefund(ordersRefund);
+ // 閫�娆炬柟寮忥細1=骞冲彴鐩存帴鍙栨秷 鈫� 杩斿洖骞冲彴鎿嶄綔浜哄悕绉�
+ if (Constants.equalsInteger(ordersRefund.getType(), Constants.ONE) && ordersRefund.getUserId() != null) {
+ // userId 鍏宠仈 system_user 琛紝鏌ヨ鎿嶄綔浜哄悕绉�
+ vo.setPlatformUserName(getPlatformUserName(ordersRefund.getUserId()));
+ }
+ // 閫�娆炬柟寮忥細2=宸插瓨浠剁敵璇峰彇娑� 鈫� 杩斿洖閫�娆惧彇浠跺浘鐗� (multifile objType=14)
+ if (Constants.equalsInteger(ordersRefund.getType(), Constants.TWO)) {
+ vo.setRefundTakeImages(getFileUrls(id, Constants.FileType.REFUND_TAKE.getKey(), imgPrefix));
+ }
+ }
+ }
+
+ return vo;
+ }
+
+ @Override
+ public OrderDispatchVO findDispatchInfo(Integer id) {
+ Orders order = ordersMapper.selectById(id);
+ if (Objects.isNull(order)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+
+ OrderDispatchVO vo = new OrderDispatchVO();
+ vo.setCode(order.getCode());
+ vo.setPayAmountYuan(order.getPayAmount() != null ? Constants.getFormatMoney(order.getPayAmount()) : 0);
+ vo.setType(order.getType());
+ vo.setTypeDesc(order.getType() != null && order.getType() == Constants.ONE ? "寮傚湴瀛樺彇" : "灏卞湴瀛樺彇");
+ vo.setDetailList(buildDetailList(id));
+
+ return vo;
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void dispatch(DispatchDTO dto) {
+ // 鍙傛暟鏍¢獙
+ if (dto == null || dto.getOrderId() == null || dto.getUrgentFee() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟涓婚敭鍜屽姞鎬ヨ垂鐢ㄤ笉鑳戒负绌�");
+ }
+
+ Orders order = ordersMapper.selectById(dto.getOrderId());
+ if (Objects.isNull(order)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+
+ // 鍓嶇疆鏉′欢鏍¢獙锛氬紓鍦板瓨鍙� + 宸插瘎瀛�
+ if (!Constants.ONE.equals(order.getType())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "浠呮敮鎸佸紓鍦板瓨鍙栬鍗曟淳鍗�");
+ }
+ if (!Integer.valueOf(Constants.OrderStatus.deposited.getStatus()).equals(order.getStatus())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "浠呭凡瀵勫瓨鐘舵�佽鍗曞彲娲惧崟");
+ }
+
+ String optUserName = getCurrentUserName();
+
+ // 鍔犳�ヨ垂鏃ュ織锛堟瘡娆″崟鐙褰曟湰娆″姞鎬ヨ垂锛�
+ Constants.OrderLogType urgentLogType = Constants.OrderLogType.urgent;
+ OrderLog feeLog = new OrderLog();
+ feeLog.setOrderId(order.getId());
+ feeLog.setTitle(urgentLogType.getTitle());
+ feeLog.setLogInfo(urgentLogType.getStatusInfo().replace("{param}", dto.getUrgentFee().toPlainString()));
+ feeLog.setObjType(urgentLogType.getStatus());
+ feeLog.setOrderStatus(order.getStatus());
+ feeLog.setOptUserType(3);
+ feeLog.setOptUserName(optUserName);
+ feeLog.setCreateTime(new Date());
+ feeLog.setDeleted(Constants.ZERO);
+ orderLogService.create(feeLog);
+
+ // 鍔犳�ヨ垂鐢� 鍏冣啋鍒�
+ long urgentFeeFen = dto.getUrgentFee().multiply(new BigDecimal(100)).longValue();
+
+ // 浣跨敤 UpdateWrapper 绮剧‘鏇存柊锛屼笉褰卞搷鍏朵粬瀛楁
+ com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<Orders> updateWrapper =
+ new UpdateWrapper<Orders>().lambda()
+ .eq(Orders::getId, order.getId())
+ .set(Orders::getIsUrgent, Constants.ONE)
+ .set(Orders::getPlatformRewardAmount, urgentFeeFen)
+ .set(Orders::getUpdateTime, new Date());
+
+ // 寮傚湴瀵勫瓨涓旀湁鍙栦欢闂ㄥ簵鏃讹紝鐢熸垚鍙告満鏍搁攢鐮�
+ if (order.getTakeShopId() != null) {
+ String driverVerifyCode = generateVerifyCode();
+ updateWrapper.set(Orders::getDriverVerifyCode, driverVerifyCode);
+ }
+
+ // 澶囨敞
+ if (StringUtils.isNotBlank(dto.getRemark())) {
+ updateWrapper.set(Orders::getRemark, dto.getRemark());
+ }
+
+ // 鎸囨淳鍙告満锛堥潪蹇呭~锛�
+ if (dto.getDriverId() != null) {
+ updateWrapper.set(Orders::getAssignDriverId, dto.getDriverId());
+
+ Member driver = memberMapper.selectById(dto.getDriverId());
+ String driverName = driver != null ? driver.getName() : String.valueOf(dto.getDriverId());
+
+ Constants.OrderLogType dispatchLogType = Constants.OrderLogType.dispatch;
+ OrderLog driverLog = new OrderLog();
+ driverLog.setOrderId(order.getId());
+ driverLog.setTitle(dispatchLogType.getTitle());
+ driverLog.setLogInfo(dispatchLogType.getStatusInfo().replace("{param}", driverName));
+ driverLog.setObjType(dispatchLogType.getStatus());
+ driverLog.setOrderStatus(order.getStatus());
+ driverLog.setOptUserType(3);
+ driverLog.setOptUserName(optUserName);
+ driverLog.setCreateTime(new Date());
+ driverLog.setDeleted(Constants.ZERO);
+ orderLogService.create(driverLog);
+ }
+
+ ordersMapper.update(updateWrapper);
+ }
+
+ private String getCurrentUserName() {
+ try {
+ com.doumee.core.model.LoginUserInfo user =
+ (com.doumee.core.model.LoginUserInfo) org.apache.shiro.SecurityUtils.getSubject().getPrincipal();
+ return user != null ? user.getUsername() : "绯荤粺";
+ } catch (Exception e) {
+ return "绯荤粺";
+ }
+ }
+
+ /**
+ * 鏋勫缓璁㈠崟鐗╁搧鏄庣粏鍒楄〃
+ */
+ private List<OrderItemVO> buildDetailList(Integer orderId) {
+ List<OrdersDetail> details = ordersDetailMapper.selectList(
+ new QueryWrapper<OrdersDetail>().lambda()
+ .eq(OrdersDetail::getOrderId, orderId)
+ .eq(OrdersDetail::getDeleted, Constants.ZERO));
+ return buildDetailList(details);
+ }
+
+ /**
+ * 鏍规嵁宸叉煡璇㈢殑鏄庣粏鏋勫缓鐗╁搧鍒楄〃锛堥伩鍏嶉噸澶嶆煡璇級
+ */
+ private List<OrderItemVO> buildDetailList(List<OrdersDetail> details) {
+ List<OrderItemVO> items = new ArrayList<>();
+ if (details != null) {
+ for (OrdersDetail d : details) {
+ OrderItemVO item = new OrderItemVO();
+ item.setLuggageName(d.getLuggageName());
+ item.setLuggageDetail(d.getLuggageDetail());
+ item.setNum(d.getNum());
+ double unitPriceYuan = d.getUnitPrice() != null ? Constants.getFormatMoney(d.getUnitPrice()) : 0;
+ item.setUnitPriceYuan(unitPriceYuan);
+ item.setSubtotal(unitPriceYuan * (d.getNum() != null ? d.getNum() : 0));
+ items.add(item);
+ }
+ }
+ return items;
+ }
+
+ /**
+ * 鐢熸垚32浣嶅敮涓�绗笁鏂硅鍗曠紪鍙凤紙鏃堕棿鎴�17浣� + 闅忔満鏁�15浣嶏級
+ */
+ private String generateOrderTradeNo() {
+ return new java.text.SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date())
+ + String.format("%015d", Math.abs(new java.util.Random().nextLong() % 1000000000000000L));
+ }
+
+ /**
+ * 鐢熸垚6浣嶆暟瀛楁牳閿�鐮侊紙Redis楠岄噸锛�
+ * 浣跨敤 SETNX 鎶㈠崰锛屼繚璇佸敮涓�锛涗娇鐢ㄥ畬姣曞悗璋冪敤 releaseVerifyCode 浠� Redis 绉婚櫎
+ */
+ private String generateVerifyCode() {
+ Random random = new Random();
+ String redisKey = Constants.REDIS_VERIFY_CODE_KEY;
+ for (int i = 0; i < 200; i++) {
+ String code = String.format("%06d", random.nextInt(1000000));
+ Boolean success = redisTemplate.opsForValue().setIfAbsent(redisKey + code, "1", 24, TimeUnit.HOURS);
+ if (success != null && success) {
+ return code;
+ }
+ }
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏍搁攢鐮佺敓鎴愬け璐ワ紝璇烽噸璇�");
+ }
+
+ /**
+ * 閲婃斁鏍搁攢鐮佸崰浣嶏紙鏍搁攢瀹屾垚鍚庤皟鐢紝绉婚櫎 Redis 涓殑 key锛�
+ */
+ public void releaseVerifyCode(String code) {
+ if (StringUtils.isNotBlank(code)) {
+ redisTemplate.delete(Constants.REDIS_VERIFY_CODE_KEY + code);
+ }
+ }
+
+ private String getOrdersPrefix() {
+ try {
+ return systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
+ + systemDictDataBiz.queryByCode(Constants.OSS, Constants.ORDERS_FILES).getCode();
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ private String getPlatformUserName(Integer userId) {
+ SystemUser user = systemUserMapper.selectById(userId);
+ return user != null ? user.getRealname() : null;
+ }
+
+ private List<String> getFileUrls(Integer orderId, int objType, String prefix) {
+ List<Multifile> files = multifileMapper.selectList(
+ new QueryWrapper<Multifile>().lambda()
+ .eq(Multifile::getObjId, orderId)
+ .eq(Multifile::getObjType, objType)
+ .eq(Multifile::getIsdeleted, Constants.ZERO)
+ .orderByAsc(Multifile::getSortnum));
+ List<String> urls = new ArrayList<>();
+ if (files != null) {
+ for (Multifile f : files) {
+ if (StringUtils.isNotBlank(f.getFileurl())) {
+ urls.add(prefix + f.getFileurl());
+ }
+ }
+ }
+ return urls;
+ }
+
+ /**
+ * 璁$畻骞惰缃鍗曡柂閰垎閰嶏紙鍙告満銆佸瓨浠堕棬搴椼�佸彇浠堕棬搴楋級
+ * 浠� pricing_rule (type=4) 璇诲彇鍒嗘垚姣斾緥锛屾牴鎹棬搴椾紒涓�/涓汉绫诲瀷鍖哄垎
+ *
+ * @param orders 璁㈠崟瀹炰綋锛堥渶瑕� totalAmount銆乧ityId 宸茶缃級
+ * @param depositShop 瀵勪欢闂ㄥ簵锛堥渶瑕� companyType锛�
+ * @param takeShop 鍙栦欢闂ㄥ簵锛堥渶瑕� companyType锛屽氨鍦板瓨鍙栨椂涓� depositShop 鐩稿悓锛�
+ */
+ private void calculateAndSetFeeAllocation(Orders orders, ShopInfo depositShop, ShopInfo takeShop) {
+ Long totalAmount = orders.getTotalAmount() != null ? orders.getTotalAmount() : 0L;
+ if (totalAmount <= 0) {
+ orders.setDriverFee(0L);
+ orders.setDepositShopFee(0L);
+ orders.setTakeShopFee(0L);
+ orders.setDriverFeeRata(BigDecimal.ZERO);
+ orders.setDepositShopFeeRata(BigDecimal.ZERO);
+ orders.setTakeShopFeeRata(BigDecimal.ZERO);
+ return;
+ }
+ Integer cityId = Integer.valueOf(orders.getCityId());
+
+ // 鍙告満鍗犳瘮锛歠ieldA=4锛堥厤閫佸憳锛�
+ BigDecimal driverRata = getRevenueShareRata(cityId, Constants.FOUR);
+ // 瀵勪欢闂ㄥ簵鍗犳瘮锛歠ieldA=0(浼佷笟瀵�)/1(涓汉瀵�)
+ int depositFieldA = Constants.equalsInteger(depositShop.getCompanyType(), Constants.ONE) ? Constants.ZERO : Constants.ONE;
+ BigDecimal depositShopRata = getRevenueShareRata(cityId, depositFieldA);
+ // 鍙栦欢闂ㄥ簵鍗犳瘮锛歠ieldA=2(浼佷笟鍙�)/3(涓汉鍙�)
+ int takeFieldA = Constants.equalsInteger(takeShop.getCompanyType(), Constants.ONE) ? Constants.TWO : Constants.THREE;
+ BigDecimal takeShopRata = getRevenueShareRata(cityId, takeFieldA);
+
+ // 璁$畻钖叕锛堝垎锛夛細totalAmount 涓哄垎锛宺ata 涓烘瘮渚嬪�硷紙濡� 0.15 琛ㄧず 15%锛�
+ long driverFee = new BigDecimal(totalAmount).multiply(driverRata).longValue();
+ long depositShopFee = new BigDecimal(totalAmount).multiply(depositShopRata).longValue();
+ long takeShopFee = totalAmount - driverFee - depositShopFee;
+
+ orders.setDriverFee(driverFee);
+ orders.setDepositShopFee(depositShopFee);
+ orders.setTakeShopFee(takeShopFee);
+ orders.setDriverFeeRata(driverRata);
+ orders.setDepositShopFeeRata(depositShopRata);
+ orders.setTakeShopFeeRata(takeShopRata);
+ }
+
+ /**
+ * 浠� pricing_rule 琛ㄨ幏鍙栧垎鎴愭瘮渚嬶紙type=4锛�
+ *
+ * @param cityId 鍩庡競涓婚敭
+ * @param fieldA 绫诲瀷锛�0=浼佷笟瀵�, 1=涓汉瀵�, 2=浼佷笟鍙�, 3=涓汉鍙�, 4=閰嶉�佸憳
+ * @return 鍒嗘垚姣斾緥锛堝 0.15 琛ㄧず 15%锛�
+ */
+ private BigDecimal getRevenueShareRata(Integer cityId, int fieldA) {
+ PricingRule rule = pricingRuleMapper.selectOne(new QueryWrapper<PricingRule>().lambda()
+ .eq(PricingRule::getDeleted, Constants.ZERO)
+ .eq(PricingRule::getType, Constants.FOUR)
+ .eq(PricingRule::getCityId, cityId)
+ .eq(PricingRule::getFieldA, String.valueOf(fieldA))
+ .last("limit 1"));
+ if (rule != null && StringUtils.isNotBlank(rule.getFieldC())) {
+ return new BigDecimal(rule.getFieldC());
+ }
+ return BigDecimal.ZERO;
+ }
+
+ @Override
+ public PageData<MyOrderVO> findMyOrderPage(PageWrap<MyOrderDTO> pageWrap, Integer memberId) {
+ MyOrderDTO model = pageWrap.getModel();
+ Integer status = model != null ? model.getStatus() : null;
+ Integer combinedStatus = model != null ? model.getCombinedStatus() : null;
+
+ // 瑙f瀽鍚堝苟鐘舵�佷负鍏蜂綋鐘舵�佸垪琛�
+ List<Integer> statusList = null;
+ if (combinedStatus != null) {
+ Constants.OrderCombinedStatus combined = Constants.OrderCombinedStatus.getByKey(combinedStatus);
+ if (combined != null) {
+ statusList = new ArrayList<>();
+ for (int s : combined.getStatuses()) {
+ statusList.add(s);
+ }
+ }
+ }
+
+ IPage<Orders> p = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+ MPJLambdaWrapper<Orders> wrapper = new MPJLambdaWrapper<Orders>()
+ .selectAll(Orders.class)
+ .select("s1.name", Orders::getDepositShopName)
+ .select("s1.link_name", Orders::getDepositShopLinkName)
+ .select("s1.link_phone", Orders::getDepositShopLinkPhone)
+ .select("s2.name", Orders::getTakeShopName)
+ .select("s2.address", Orders::getTakeShopAddress)
+ .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
+ .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID")
+ .eq(Orders::getDeleted, Constants.ZERO)
+ .eq(Orders::getMemberId, memberId)
+ .eq(status != null, Orders::getStatus, status)
+ .in(statusList != null, Orders::getStatus, statusList)
+ .orderByDesc(Orders::getCreateTime);
+
+ IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper);
+ List<MyOrderVO> voList = new ArrayList<>();
+ if (orderPage != null && orderPage.getRecords() != null) {
+
+ for (Orders o : orderPage.getRecords()) {
+ MyOrderVO vo = new MyOrderVO();
+ vo.setId(o.getId());
+ vo.setCode(o.getCode());
+ vo.setType(o.getType());
+ vo.setStatus(o.getStatus());
+ vo.setCreateTime(o.getCreateTime());
+ vo.setExpectedTakeTime(o.getExpectedTakeTime());
+
+ // 瀛樹欢闂ㄥ簵锛堝叧鑱旀煡璇㈢洿鎺ュ彇鍊硷級
+ vo.setDepositShopName(o.getDepositShopName());
+ vo.setDepositShopLinkName(o.getDepositShopLinkName());
+ vo.setDepositShopPhone(o.getDepositShopLinkPhone());
+
+ // 鍙栦欢淇℃伅锛氭湁鍙栦欢闂ㄥ簵鍙栭棬搴楋紝鏃犲垯鍙栫敤鎴疯嚜閫夊彇浠剁偣
+ if (o.getTakeShopId() != null) {
+ vo.setTakeShopName(o.getTakeShopName());
+ vo.setTakeShopAddress(o.getTakeShopAddress());
+ } else {
+ vo.setTakeLocation(o.getTakeLocation());
+ vo.setTakeLocationRemark(o.getTakeLocationRemark());
+ }
+
+ // 鍙栦欢鑱旂郴浜�
+ vo.setTakeUser(o.getTakeUser());
+ vo.setTakePhone(o.getTakePhone());
+
+ // 璐圭敤锛堝垎锛�
+ vo.setDeclaredFee(o.getDeclaredFee());
+ vo.setEstimatedAmount(o.getEstimatedAmount());
+
+ // 鏌ヨ鐗╁搧鏄庣粏锛堜竴娆℃煡璇紝鍚屾椂鐢ㄤ簬鐗╁搧鍒楄〃鍜岄�炬湡璁$畻锛�
+ List<OrdersDetail> details = ordersDetailMapper.selectList(
+ new QueryWrapper<OrdersDetail>().lambda()
+ .eq(OrdersDetail::getOrderId, o.getId())
+ .eq(OrdersDetail::getDeleted, Constants.ZERO));
+
+ // 鐗╁搧鏄庣粏
+ vo.setDetailList(buildDetailList(details));
+
+ // 閫炬湡淇℃伅锛堜粎寰呭彇浠剁姸鎬佽绠楋級
+ if (Integer.valueOf(Constants.OrderStatus.arrived.getStatus()).equals(o.getStatus())) {
+ OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(o, details);
+ vo.setOverdue(overdueInfo.getOverdue());
+ vo.setOverdueDays(overdueInfo.getOverdueDays());
+ vo.setOverdueFee(overdueInfo.getOverdueFee());
+ }
+ voList.add(vo);
+ }
+ }
+
+ IPage<MyOrderVO> vPage = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+ PageData<MyOrderVO> pageData = PageData.from(vPage);
+ pageData.setRecords(voList);
+ pageData.setTotal(orderPage.getTotal());
+ pageData.setPage(orderPage.getCurrent());
+ pageData.setCapacity(orderPage.getSize());
+ return pageData;
+ }
+
+ @Override
+ public PageData<MyOrderVO> findShopOrderPage(PageWrap<MyOrderDTO> pageWrap, Integer shopId) {
+ MyOrderDTO model = pageWrap.getModel();
+ Integer status = model != null ? model.getStatus() : null;
+ Integer combinedStatus = model != null ? model.getCombinedStatus() : null;
+
+ // 瑙f瀽鍚堝苟鐘舵�佷负鍏蜂綋鐘舵�佸垪琛�
+ List<Integer> statusList = null;
+ if (combinedStatus != null) {
+ Constants.OrderCombinedStatus combined = Constants.OrderCombinedStatus.getByKey(combinedStatus);
+ if (combined != null) {
+ statusList = new ArrayList<>();
+ for (int s : combined.getStatuses()) {
+ statusList.add(s);
+ }
+ }
+ }
+
+ IPage<Orders> p = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+ MPJLambdaWrapper<Orders> wrapper = new MPJLambdaWrapper<Orders>()
+ .selectAll(Orders.class)
+ .select("s1.name", Orders::getDepositShopName)
+ .select("s1.link_name", Orders::getDepositShopLinkName)
+ .select("s1.link_phone", Orders::getDepositShopLinkPhone)
+ .select("s2.name", Orders::getTakeShopName)
+ .select("s2.address", Orders::getTakeShopAddress)
+ .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
+ .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID")
+ .eq(Orders::getPayStatus, Constants.ONE)
+ .and(w -> w.eq(Orders::getDepositShopId, shopId).or().eq(Orders::getTakeShopId, shopId))
+ .eq(status != null, Orders::getStatus, status)
+ .in(statusList != null, Orders::getStatus, statusList)
+ .orderByDesc(Orders::getCreateTime);
+
+ IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper);
+ List<MyOrderVO> voList = new ArrayList<>();
+ if (orderPage != null && orderPage.getRecords() != null) {
+ for (Orders o : orderPage.getRecords()) {
+ MyOrderVO vo = new MyOrderVO();
+ vo.setId(o.getId());
+ vo.setCode(o.getCode());
+ vo.setType(o.getType());
+ vo.setStatus(o.getStatus());
+ vo.setCreateTime(o.getCreateTime());
+ vo.setExpectedTakeTime(o.getExpectedTakeTime());
+
+ vo.setDepositShopName(o.getDepositShopName());
+ vo.setDepositShopLinkName(o.getDepositShopLinkName());
+ vo.setDepositShopPhone(o.getDepositShopLinkPhone());
+
+ // 闂ㄥ簵瑙掕壊锛氬瓨浠堕棬搴�=1锛屽彇浠堕棬搴�=2
+ if (Constants.equalsInteger(o.getDepositShopId(), shopId)) {
+ vo.setShopRole(Constants.ONE);
+ } else if (Constants.equalsInteger(o.getTakeShopId(), shopId)) {
+ vo.setShopRole(Constants.TWO);
+ }
+
+ if (o.getTakeShopId() != null) {
+ vo.setTakeShopName(o.getTakeShopName());
+ vo.setTakeShopAddress(o.getTakeShopAddress());
+ } else {
+ vo.setTakeLocation(o.getTakeLocation());
+ vo.setTakeLocationRemark(o.getTakeLocationRemark());
+ }
+
+ vo.setTakeUser(o.getTakeUser());
+ vo.setTakePhone(o.getTakePhone());
+ vo.setDeclaredFee(o.getDeclaredFee());
+ vo.setEstimatedAmount(o.getEstimatedAmount());
+
+ List<OrdersDetail> details = ordersDetailMapper.selectList(
+ new QueryWrapper<OrdersDetail>().lambda()
+ .eq(OrdersDetail::getOrderId, o.getId())
+ .eq(OrdersDetail::getDeleted, Constants.ZERO));
+
+ vo.setDetailList(buildDetailList(details));
+
+ if (Integer.valueOf(Constants.OrderStatus.arrived.getStatus()).equals(o.getStatus())) {
+ OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(o, details);
+ vo.setOverdue(overdueInfo.getOverdue());
+ vo.setOverdueDays(overdueInfo.getOverdueDays());
+ vo.setOverdueFee(overdueInfo.getOverdueFee());
+ }
+ voList.add(vo);
+ }
+ }
+
+ IPage<MyOrderVO> vPage = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+ PageData<MyOrderVO> pageData = PageData.from(vPage);
+ pageData.setRecords(voList);
+ pageData.setTotal(orderPage.getTotal());
+ pageData.setPage(orderPage.getCurrent());
+ pageData.setCapacity(orderPage.getSize());
+ return pageData;
+ }
+
+ @Override
+ public MyOrderDetailVO findMyOrderDetail(Integer id, Integer memberId) {
+ Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+ .eq(Orders::getId, id)
+ .eq(Orders::getMemberId, memberId)
+ .eq(Orders::getDeleted, Constants.ZERO));
+ if (order == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+ return buildOrderDetailVO(order, true);
+ }
+
+ @Override
+ public MyOrderDetailVO findShopOrderDetail(Integer orderId, String verifyCode) {
+ Orders order = null;
+ if (orderId != null) {
+ order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+ .eq(Orders::getId, orderId)
+ .eq(Orders::getDeleted, Constants.ZERO));
+ } else if (StringUtils.isNotBlank(verifyCode)) {
+ order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+ .eq(Orders::getMemberVerifyCode, verifyCode)
+ .eq(Orders::getDeleted, Constants.ZERO)
+ .last("limit 1"));
+ if (order == null) {
+ order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+ .eq(Orders::getDriverVerifyCode, verifyCode)
+ .eq(Orders::getDeleted, Constants.ZERO)
+ .last("limit 1"));
+ }
+ }
+ if (order == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+ }
+ return buildOrderDetailVO(order, false);
+ }
+
+ /**
+ * 鏋勫缓璁㈠崟璇︽儏VO锛堜細鍛樼/闂ㄥ簵绔鐢級
+ *
+ * @param order 璁㈠崟瀹炰綋
+ * @param memberViewMode true=浼氬憳绔紙鎸夋潯浠惰繑鍥炴牳閿�鐮侊級锛宖alse=闂ㄥ簵绔紙濮嬬粓杩斿洖鏍搁攢鐮侊級
+ */
+ private MyOrderDetailVO buildOrderDetailVO(Orders order, boolean memberViewMode) {
+ MyOrderDetailVO vo = new MyOrderDetailVO();
+ vo.setId(order.getId());
+ vo.setStatus(order.getStatus());
+ vo.setType(order.getType());
+ vo.setCode(order.getCode());
+ vo.setOutTradeNo(order.getOutTradeNo());
+ vo.setRemark(order.getRemark());
+ vo.setCreateTime(order.getCreateTime());
+ vo.setPayTime(order.getPayTime());
+ vo.setExpectedDepositTime(order.getExpectedDepositTime());
+ vo.setExpectedTakeTime(order.getExpectedTakeTime());
+ vo.setArriveTime(order.getArriveTime());
+
+ // 璐圭敤锛堝垎锛�
+ vo.setBasicAmount(order.getBasicAmount());
+ vo.setDeclaredAmount(order.getDeclaredAmount());
+ vo.setDeclaredFee(order.getDeclaredFee());
+ vo.setUrgentAmount(order.getUrgentAmount());
+ vo.setActualPayAmount(order.getPayAmount());
+
+ // 鏍囪
+ vo.setExceptionStatus(order.getExceptionStatus());
+
+ // 鏄惁瓒呭嚭鍙栦欢鏃堕棿
+ vo.setPastTakeTime(order.getExpectedTakeTime() != null && new Date().after(order.getExpectedTakeTime()));
+
+ // 璁㈠崟鐘舵�佹弿杩� + 鍊掕鏃�
+ vo.setStatusDesc(buildStatusDesc(order));
+ if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.waitPay.getStatus())) {
+ vo.setPayCountdownMs(calcPayCountdownMs(order));
+ }
+
+ // 瀛樹欢闂ㄥ簵
+ if (order.getDepositShopId() != null) {
+ ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
+ if (depositShop != null) {
+ vo.setDepositShopName(depositShop.getName());
+ vo.setDepositShopLinkName(depositShop.getLinkName());
+ vo.setDepositShopPhone(depositShop.getLinkPhone());
+ vo.setDepositShopAddress(depositShop.getAddress());
+ }
+ }
+
+ // 鍙栦欢淇℃伅
+ if (order.getTakeShopId() != null) {
+ ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
+ if (takeShop != null) {
+ vo.setTakeShopName(takeShop.getName());
+ vo.setTakeShopAddress(takeShop.getAddress());
+ }
+ } else {
+ vo.setTakeLocation(order.getTakeLocation());
+ vo.setTakeLocationRemark(order.getTakeLocationRemark());
+ }
+
+ // 鍙栦欢鑱旂郴浜�
+ vo.setTakeUser(order.getTakeUser());
+ vo.setTakePhone(order.getTakePhone());
+
+ // 鐗╁搧绫诲瀷鍚嶇О
+ if (order.getGoodType() != null) {
+ Category category = categoryMapper.selectById(order.getGoodType());
+ if (category != null) {
+ vo.setGoodTypeName(category.getName());
+ }
+ }
+
+ // 涓嬪崟鐓х墖
+ String imgPrefix = getOrdersPrefix();
+ vo.setOrderImages(getFileUrls(order.getId(), Constants.FileType.ORDER_FILE.getKey(), imgPrefix));
+
+ // 鐗╁搧鏄庣粏
+ List<OrdersDetail> details = ordersDetailMapper.selectList(
+ new QueryWrapper<OrdersDetail>().lambda()
+ .eq(OrdersDetail::getOrderId, order.getId())
+ .eq(OrdersDetail::getDeleted, Constants.ZERO));
+ vo.setDetailList(buildDetailList(details));
+
+ // 閫炬湡淇℃伅
+ OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(order, details);
+ vo.setOverdue(overdueInfo.getOverdue());
+ vo.setOverdueDays(overdueInfo.getOverdueDays());
+ vo.setOverdueFee(overdueInfo.getOverdueFee());
+
+ // 鏍搁攢鐮�
+ Integer status = order.getStatus();
+ if (memberViewMode) {
+ // 浼氬憳绔細寰呭瘎瀛�(1)杩斿洖锛涘緟鍙栦欢(5)鏃讹紝灏卞湴瀵勫瓨鏃犲彇浠堕棬搴椾笉杩斿洖
+ boolean returnCode = false;
+ if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
+ returnCode = true;
+ } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
+ if (!(Constants.equalsInteger(order.getType(), Constants.ZERO) && order.getTakeShopId() == null)) {
+ returnCode = true;
+ }
+ }
+ if (returnCode) {
+ vo.setMemberVerifyCode(order.getMemberVerifyCode());
+ }
+ } else {
+ // 闂ㄥ簵绔細濮嬬粓杩斿洖浼氬憳鏍搁攢鐮�
+ vo.setMemberVerifyCode(order.getMemberVerifyCode());
+ }
+
+ return vo;
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void cancelOrder(Integer orderId, Integer memberId, String reason) {
+ Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+ .eq(Orders::getId, orderId)
+ .eq(Orders::getMemberId, memberId)
+ .eq(Orders::getDeleted, Constants.ZERO));
+ if (order == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+
+ // 浠呭紓鍦板瘎瀛樺彲鍙栨秷
+ if (!Constants.equalsInteger(order.getType(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "浠呭紓鍦板瘎瀛樿鍗曞彲鍙栨秷");
+ }
+
+ Integer status = order.getStatus();
+ if (status == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟鐘舵�佸紓甯�");
+ }
+
+ Date now = new Date();
+
+ // 寰呮敮浠橈細鐩存帴鍙栨秷
+ if (Constants.equalsInteger(status, Constants.OrderStatus.waitPay.getStatus())) {
+ order.setStatus(Constants.OrderStatus.cancelled.getStatus());
+ order.setCancelTime(now);
+ ordersMapper.updateById(order);
+ saveCancelLog(order, "浼氬憳鍙栨秷璁㈠崟锛堝緟鏀粯锛�", reason, memberId);
+ return;
+ }
+
+ // 寰呭瘎瀛橈細鐩存帴鍙栨秷锛屽叏棰濋��娆�
+ if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
+ // 璁板綍閫�娆句俊鎭�
+ OrdersRefund refund = new OrdersRefund();
+ refund.setOrderId(orderId);
+ refund.setType(0); // 鏈瘎瀛樼洿鎺ュ彇娑�
+ refund.setCancelInfo(reason);
+ refund.setCreateTime(now);
+ refund.setDeleted(Constants.ZERO);
+
+ // 璋冪敤寰俊閫�娆撅紝鍏ㄩ閫�娆�
+ String refundCode = wxMiniUtilService.wxRefund(order.getOutTradeNo(), order.getPayAmount(), order.getPayAmount());
+ refund.setRefundCode(refundCode);
+ refund.setRefundTime(new Date());
+ ordersRefundMapper.insert(refund);
+
+ order.setStatus(Constants.OrderStatus.cancelled.getStatus());
+ order.setCancelTime(now);
+ order.setRefundAmount(order.getPayAmount());
+ ordersMapper.updateById(order);
+
+ saveCancelLog(order, "浼氬憳鍙栨秷璁㈠崟锛堝緟瀵勫瓨锛屽叏棰濋��娆撅級", reason, memberId);
+ return;
+ }
+
+ // 宸插瘎瀛�/宸叉帴鍗曪細杩涘叆鍙栨秷涓姸鎬�
+ if (Constants.equalsInteger(status, Constants.OrderStatus.deposited.getStatus())
+ || Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
+ order.setStatus(Constants.OrderStatus.cancelling.getStatus());
+ order.setCancelTime(now);
+ ordersMapper.updateById(order);
+ saveCancelLog(order, "浼氬憳鐢宠鍙栨秷璁㈠崟锛堝凡瀵勫瓨/宸叉帴鍗曪級", reason, memberId);
+ return;
+ }
+
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽鍙栨秷");
+ }
+
+ /**
+ * 淇濆瓨鍙栨秷璁㈠崟鎿嶄綔鏃ュ織
+ */
+ private void saveCancelLog(Orders order, String title, String reason, Integer memberId) {
+ OrderLog log = new OrderLog();
+ log.setOrderId(order.getId());
+ log.setTitle(title);
+ log.setLogInfo(reason);
+ log.setObjType(Constants.ORDER_LOG_CANCEL);
+ log.setOrderStatus(order.getStatus());
+ log.setOptUserId(memberId);
+ log.setOptUserType(0); // 0=鐢ㄦ埛
+ log.setCreateTime(new Date());
+ log.setDeleted(Constants.ZERO);
+ orderLogService.create(log);
+ }
+
+ /**
+ * 淇濆瓨闂ㄥ簵鏍搁攢鏃ュ織
+ */
+ private void saveShopVerifyLog(Orders order, String title, String logInfo, String remark, Integer shopId) {
+ OrderLog log = new OrderLog();
+ log.setOrderId(order.getId());
+ log.setTitle(title);
+ log.setLogInfo(logInfo);
+ log.setRemark(remark);
+ log.setOrderStatus(order.getStatus());
+ log.setOptUserId(shopId);
+ log.setOptUserType(2); // 2=闂ㄥ簵
+ log.setCreateTime(new Date());
+ log.setDeleted(Constants.ZERO);
+ orderLogService.create(log);
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void handleStorageOrderPayNotify(String outTradeNo, String wxTradeNo) {
+ Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+ .eq(Orders::getOutTradeNo, outTradeNo)
+ .eq(Orders::getDeleted, Constants.ZERO)
+ .last("limit 1"));
+ if (order == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�: " + outTradeNo);
+ }
+ // 骞傜瓑锛氬凡鏀粯鍒欒烦杩�
+ if (Constants.equalsInteger(order.getPayStatus(), Constants.ONE)) {
+ return;
+ }
+ Date now = new Date();
+ order.setStatus(Constants.OrderStatus.waitDeposit.getStatus()); // 寰呭瘎瀛�
+ order.setPayStatus(Constants.ONE); // 宸叉敮浠�
+ order.setPayTime(now);
+ order.setWxExternalNo(wxTradeNo);
+ order.setUpdateTime(now);
+ // 鐢熸垚浼氬憳鏍搁攢鐮�
+ order.setMemberVerifyCode(generateVerifyCode());
+ ordersMapper.updateById(order);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public PayResponse payOverdueFee(Integer orderId, Integer memberId) {
+ // 1. 鏌ヨ瀵勫瓨璁㈠崟
+ Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+ .eq(Orders::getId, orderId)
+ .eq(Orders::getMemberId, memberId)
+ .eq(Orders::getDeleted, Constants.ZERO));
+ if (order == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+ }
+ // 2. 鏍¢獙鐘舵�侊細寰呭彇浠�(5) + 閫炬湡(1)
+ if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鏀寔閫炬湡鏀粯");
+ }
+ if (!Constants.equalsInteger(order.getOverdueStatus(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉瀛樺湪閫炬湡璐圭敤");
+ }
+ if (order.getOverdueAmount() == null || order.getOverdueAmount() <= 0) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "閫炬湡璐圭敤寮傚父锛屾棤娉曞彂璧锋敮浠�");
+ }
+ // 3. 鏌ヨ浼氬憳
+ Member member = memberMapper.selectById(memberId);
+ if (member == null || StringUtils.isBlank(member.getOpenid())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐢ㄦ埛淇℃伅寮傚父锛屾棤娉曞彂璧锋敮浠�");
+ }
+ // 4. 鍒涘缓閫炬湡璐圭敤璁㈠崟
+ String outTradeNo = generateOrderTradeNo();
+ Date now = new Date();
+ OtherOrders otherOrders = new OtherOrders();
+ otherOrders.setType(Constants.TWO); // 2=閫炬湡璐圭敤璁㈠崟
+ otherOrders.setMemberId(memberId);
+ otherOrders.setOrderId(orderId);
+ otherOrders.setPayAccount(order.getOverdueAmount());
+ otherOrders.setPayStatus(Constants.ZERO);
+ otherOrders.setCode("OD" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(now) + orderId);
+ otherOrders.setOutTradeNo(outTradeNo);
+ otherOrders.setDeleted(Constants.ZERO);
+ otherOrders.setCreateTime(now);
+ otherOrdersMapper.insert(otherOrders);
+
+ // 5. 鍞よ捣寰俊鏀粯
+ return wxPayForOtherOrder(otherOrders, member.getOpenid(), Constants.OrdersAttach.OVERDUE_FEE);
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void handleOverdueFeePayNotify(String outTradeNo, String wxTradeNo) {
+ // 1. 鏌ユ壘閫炬湡璐圭敤璁㈠崟
+ OtherOrders otherOrders = otherOrdersMapper.selectOne(new QueryWrapper<OtherOrders>().lambda()
+ .eq(OtherOrders::getOutTradeNo, outTradeNo)
+ .eq(OtherOrders::getDeleted, Constants.ZERO)
+ .last("limit 1"));
+ if (otherOrders == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "閫炬湡璐圭敤璁㈠崟涓嶅瓨鍦�: " + outTradeNo);
+ }
+ // 2. 骞傜瓑锛氬凡鏀粯鍒欒烦杩�
+ if (Constants.equalsInteger(otherOrders.getPayStatus(), Constants.ONE)) {
+ return;
+ }
+ Date now = new Date();
+ // 3. 鏇存柊閫炬湡璐圭敤璁㈠崟鐘舵��
+ otherOrders.setPayStatus(Constants.ONE);
+ otherOrders.setPayTime(now);
+ otherOrders.setWxExternalNo(wxTradeNo);
+ otherOrders.setUpdateTime(now);
+ otherOrdersMapper.updateById(otherOrders);
+
+ // 4. 鏇存柊瀵勫瓨璁㈠崟閫炬湡鐘舵�佷负宸叉敮浠�(2)锛屾洿鏂版�婚噾棰濓紝閲嶇畻涓夋柟鏀剁泭
+ if (otherOrders.getOrderId() != null) {
+ Orders order = ordersMapper.selectById(otherOrders.getOrderId());
+ if (order != null) {
+ order.setOverdueStatus(Constants.TWO); // 2=宸叉敮浠�
+ // 鎬婚噾棰� = 鍘熼噾棰� + 閫炬湡璐圭敤
+ Long overdueFee = otherOrders.getPayAccount() != null ? otherOrders.getPayAccount() : 0L;
+ long newTotal = (order.getTotalAmount() != null ? order.getTotalAmount() : 0L) + overdueFee;
+ order.setTotalAmount(newTotal);
+ order.setUpdateTime(now);
+ ordersMapper.updateById(order);
+ // 閲嶇畻涓夋柟鏀剁泭
+ calculateAndSaveOrderFees(order.getId());
+ }
+ }
+ }
+
+ @Override
+ public void deleteMyOrder(Integer orderId, Integer memberId) {
+ Orders order = ordersMapper.selectById(orderId);
+ if (order == null || !Constants.equalsInteger(order.getDeleted(), Constants.ZERO)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+ if (!Constants.equalsInteger(order.getMemberId(), memberId)) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鏃犳潈鎿嶄綔姝よ鍗�");
+ }
+ // 浠呭凡瀹屾垚(7)銆佸凡鍙栨秷(99)銆佸凡閫�娆�(96)鍙垹闄�
+ int status = Constants.formatIntegerNum(order.getStatus());
+ if (status != Constants.OrderStatus.finished.getStatus()
+ && status != Constants.OrderStatus.cancelled.getStatus()
+ && status != Constants.OrderStatus.closed.getStatus()) {
+ throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍙垹闄�");
+ }
+ ordersMapper.update(new UpdateWrapper<Orders>().lambda()
+ .set(Orders::getDeleted, Constants.ONE)
+ .set(Orders::getUpdateTime, new Date())
+ .eq(Orders::getId, orderId));
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public PayResponse payShopDeposit(Integer shopId) {
+ // 1. 鏌ヨ闂ㄥ簵淇℃伅
+ ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
+ if (shopInfo == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "闂ㄥ簵涓嶅瓨鍦�");
+ }
+ // 2. 鏍¢獙鐘舵�侊細瀹℃壒閫氳繃(1)鎵嶈兘鏀粯鎶奸噾
+ if (!Constants.equalsInteger(shopInfo.getAuditStatus(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠闂ㄥ簵鐘舵�佷笉鏀寔鏀粯鎶奸噾");
+ }
+ if (shopInfo.getDepositAmount() == null || shopInfo.getDepositAmount() <= 0) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鎶奸噾閲戦寮傚父锛屾棤娉曞彂璧锋敮浠�");
+ }
+ // 3. 鏌ヨ浼氬憳openid
+ Member member = memberMapper.selectById(shopInfo.getRegionMemberId());
+ if (member == null || StringUtils.isBlank(member.getOpenid())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐢ㄦ埛淇℃伅寮傚父锛屾棤娉曞彂璧锋敮浠�");
+ }
+ // 4. 鍒涘缓鎶奸噾璁㈠崟
+ String outTradeNo = generateOrderTradeNo();
+ Date now = new Date();
+ OtherOrders otherOrders = new OtherOrders();
+ otherOrders.setType(Constants.ZERO); // 0=搴楅摵鎶奸噾璁㈠崟
+ otherOrders.setMemberId(shopInfo.getRegionMemberId());
+ otherOrders.setPayAccount(shopInfo.getDepositAmount());
+ otherOrders.setPayStatus(Constants.ZERO);
+ otherOrders.setCode("SD" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(now) + shopId);
+ otherOrders.setOutTradeNo(outTradeNo);
+ otherOrders.setDeleted(Constants.ZERO);
+ otherOrders.setCreateTime(now);
+ otherOrdersMapper.insert(otherOrders);
+
+ // 5. 鍞よ捣寰俊鏀粯
+ return wxPayForOtherOrder(otherOrders, member.getOpenid(), Constants.OrdersAttach.SHOP_DEPOSIT);
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void handleShopDepositPayNotify(String outTradeNo, String wxTradeNo) {
+ // 1. 鏌ユ壘鎶奸噾璁㈠崟
+ OtherOrders otherOrders = otherOrdersMapper.selectOne(new QueryWrapper<OtherOrders>().lambda()
+ .eq(OtherOrders::getOutTradeNo, outTradeNo)
+ .eq(OtherOrders::getDeleted, Constants.ZERO)
+ .last("limit 1"));
+ if (otherOrders == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鎶奸噾璁㈠崟涓嶅瓨鍦�: " + outTradeNo);
+ }
+ // 2. 骞傜瓑锛氬凡鏀粯鍒欒烦杩�
+ if (Constants.equalsInteger(otherOrders.getPayStatus(), Constants.ONE)) {
+ return;
+ }
+ Date now = new Date();
+ // 3. 鏇存柊鎶奸噾璁㈠崟鐘舵��
+ otherOrders.setPayStatus(Constants.ONE);
+ otherOrders.setPayTime(now);
+ otherOrders.setWxExternalNo(wxTradeNo);
+ otherOrders.setUpdateTime(now);
+ otherOrdersMapper.updateById(otherOrders);
+
+ // 4. 鏌ヨ闂ㄥ簵淇℃伅锛堥�氳繃娉ㄥ唽浼氬憳涓婚敭鍏宠仈锛�
+ ShopInfo shopInfo = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda()
+ .eq(ShopInfo::getRegionMemberId, otherOrders.getMemberId())
+ .eq(ShopInfo::getDeleted, Constants.ZERO)
+ .last("limit 1"));
+ if (shopInfo == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "闂ㄥ簵涓嶅瓨鍦�");
+ }
+ // 5. 鏇存柊闂ㄥ簵鐘舵�侊細宸叉敮浠樻娂閲�
+ shopInfo.setAuditStatus(Constants.THREE); // 3=宸叉敮浠樻娂閲�
+ shopInfo.setPayStatus(Constants.ONE);
+ shopInfo.setPayTime(now);
+ shopInfo.setWxExternalNo(wxTradeNo);
+ shopInfo.setCode(otherOrders.getCode());
+ Member member = memberMapper.selectById(otherOrders.getMemberId());
+ if (member != null) {
+ shopInfo.setPayMemberOpenId(member.getOpenid());
+ }
+ shopInfo.setUpdateTime(now);
+ shopInfoMapper.updateById(shopInfo);
+
+ // 6. 鎶奸噾鏀粯瀹屾垚鍚庯紝鑻ュ煄甯傛湭寮�閫氬垯鑷姩寮�閫�
+ if (shopInfo.getAreaId() != null) {
+ Areas shopArea = areasBiz.resolveArea(shopInfo.getAreaId());
+ if (shopArea != null && shopArea.getParentId() != null) {
+ Areas cityArea = areasBiz.resolveArea(shopArea.getParentId());
+ if (cityArea != null && !Constants.equalsInteger(cityArea.getStatus(), Constants.ONE)) {
+ cityArea.setStatus(Constants.ONE);
+ cityArea.setEditDate(now);
+ areasService.updateById(cityArea);
+ areasService.cacheData();
+ }
+ }
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void settleOrders() {
+ // 1. 璇诲彇缁撶畻澶╂暟閰嶇疆
+ SystemDictData settlementConfig = systemDictDataBiz.queryByCode(Constants.OPERATION_CONFIG, Constants.OP_SETTLEMENT_DATE);
+ if (settlementConfig == null || StringUtils.isBlank(settlementConfig.getCode())) {
+ return;
+ }
+ int days = Integer.parseInt(settlementConfig.getCode());
+ // 缁撶畻鎴鏃堕棿 = 褰撳墠鏃堕棿 - N澶�
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DAY_OF_MONTH, -days);
+ Date deadline = cal.getTime();
+
+ // 2. 鏌ヨ宸插畬鎴愮殑寰呯粨绠楄鍗曪紙瀹屾垚鏃堕棿 <= 鎴鏃堕棿锛�
+ List<Orders> ordersList = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
+ .eq(Orders::getDeleted, Constants.ZERO)
+ .eq(Orders::getStatus, Constants.OrderStatus.finished.getStatus())
+ .eq(Orders::getSettlementStatus, Constants.ZERO)
+ .le(Orders::getFinishTime, deadline));
+ if (ordersList == null || ordersList.isEmpty()) {
+ return;
+ }
+
+ Date now = new Date();
+ for (Orders order : ordersList) {
+ // 3. 鏇存柊璁㈠崟缁撶畻鐘舵��
+ ordersMapper.update(new UpdateWrapper<Orders>().lambda()
+ .set(Orders::getSettlementStatus, Constants.ONE)
+ .set(Orders::getSettlementTime, now)
+ .set(Orders::getUpdateTime, now)
+ .eq(Orders::getId, order.getId()));
+
+ // 4. 鏌ヨ鍏宠仈鐨勫緟鍏ヨ处 Revenue 璁板綍
+ List<Revenue> revenues = revenueMapper.selectList(new QueryWrapper<Revenue>().lambda()
+ .eq(Revenue::getObjId, order.getId())
+ .eq(Revenue::getObjType, Constants.ZERO)
+ .eq(Revenue::getVaildStatus, Constants.ZERO)
+ .eq(Revenue::getDeleted, Constants.ZERO));
+
+ for (Revenue revenue : revenues) {
+ Long amount = revenue.getAmount() != null ? revenue.getAmount() : 0L;
+ // 鏇存柊 Revenue 涓哄凡鍏ヨ处
+ revenueMapper.update(new UpdateWrapper<Revenue>().lambda()
+ .set(Revenue::getVaildStatus, Constants.ONE)
+ .set(Revenue::getUpdateTime, now)
+ .eq(Revenue::getId, revenue.getId()));
+
+ // 鏍规嵁 memberType 鏇存柊浣欓
+ if (Constants.equalsInteger(revenue.getMemberType(), Constants.ONE)) {
+ // 鍙告満锛氶�氳繃 memberId 鏌� DriverInfo锛屾洿鏂� balance / totalBalance
+ DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
+ .eq(DriverInfo::getMemberId, revenue.getMemberId())
+ .eq(DriverInfo::getDeleted, Constants.ZERO)
+ .last("limit 1"));
+ if (driver != null) {
+ driverInfoMapper.update(new UpdateWrapper<DriverInfo>().lambda()
+ .setSql(" BALANCE = IFNULL(BALANCE, 0) + " + amount)
+ .setSql(" TOTAL_BALANCE = IFNULL(TOTAL_BALANCE, 0) + " + amount)
+ .eq(DriverInfo::getId, driver.getId()));
+ }
+ } else if (Constants.equalsInteger(revenue.getMemberType(), Constants.TWO)) {
+ // 闂ㄥ簵锛氶�氳繃 memberId 鏌� ShopInfo(regionMemberId)锛屾洿鏂� balance / totalBalance
+ ShopInfo shop = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda()
+ .eq(ShopInfo::getRegionMemberId, revenue.getMemberId())
+ .eq(ShopInfo::getDeleted, Constants.ZERO)
+ .last("limit 1"));
+ if (shop != null) {
+ shopInfoMapper.update(new UpdateWrapper<ShopInfo>().lambda()
+ .setSql(" BALANCE = IFNULL(BALANCE, 0) + " + amount)
+ .setSql(" TOTAL_BALANCE = IFNULL(TOTAL_BALANCE, 0) + " + amount)
+ .eq(ShopInfo::getId, shop.getId()));
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void commentOrder(CommentOrderDTO dto, Integer memberId) {
+ // 1. 鏍¢獙璁㈠崟
+ Orders order = ordersMapper.selectById(dto.getOrderId());
+ if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+ }
+ if (!Constants.equalsInteger(order.getMemberId(), memberId)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏃犳潈璇勪环璇ヨ鍗�");
+ }
+ if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.finished.getStatus())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鏀寔璇勪环");
+ }
+ if (Constants.equalsInteger(order.getCommentStatus(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曞凡璇勪环");
+ }
+
+ // 2. 寮傚湴瀵勫瓨璁㈠崟锛氬彇浠堕棬搴楀拰鍙告満璇勫垎鏍¢獙
+ boolean isRemote = Constants.equalsInteger(order.getType(), Constants.ONE);
+ if (isRemote) {
+ if (dto.getDriverScore() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "寮傚湴瀵勫瓨璁㈠崟蹇呴』璇勪环鍙告満");
+ }
+ if (order.getTakeShopId() != null && dto.getTakeScore() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇疯瘎浠峰彇浠堕棬搴�");
+ }
+ }
+
+ Date now = new Date();
+
+ // 3. 鏇存柊璁㈠崟璇勪环鐘舵��
+ order.setCommentStatus(Constants.ONE);
+ order.setCommentInfo(dto.getContent());
+ order.setCommentDepositLevel(dto.getDepositScore());
+ order.setCommentTakeLevel(dto.getTakeScore());
+ order.setCommentDriverLevel(dto.getDriverScore());
+ order.setCommentTime(now);
+ order.setUpdateTime(now);
+ ordersMapper.updateById(order);
+
+ // 4. 鍒涘缓璇勪环璁板綍
+ // 4.1 瀛樹欢闂ㄥ簵
+ OrderComment depositComment = new OrderComment();
+ depositComment.setOrderId(order.getId());
+ depositComment.setOrderCode(order.getCode());
+ depositComment.setMemberId(memberId);
+ depositComment.setTargetType(Constants.ONE); // 1=瀛樹欢闂ㄥ簵
+ depositComment.setTargetId(order.getDepositShopId());
+ depositComment.setScore(dto.getDepositScore());
+ depositComment.setContent(dto.getContent());
+ depositComment.setDeleted(Constants.ZERO);
+ depositComment.setCreateTime(now);
+ orderCommentMapper.insert(depositComment);
+
+ // 4.2 鍙栦欢闂ㄥ簵锛堝紓鍦板瘎瀛樹笖鏈夊彇浠堕棬搴楋級
+ if (isRemote && order.getTakeShopId() != null && dto.getTakeScore() != null) {
+ OrderComment takeComment = new OrderComment();
+ takeComment.setOrderId(order.getId());
+ takeComment.setOrderCode(order.getCode());
+ takeComment.setMemberId(memberId);
+ takeComment.setTargetType(Constants.TWO); // 2=鍙栦欢闂ㄥ簵
+ takeComment.setTargetId(order.getTakeShopId());
+ takeComment.setScore(dto.getTakeScore());
+ takeComment.setContent(dto.getContent());
+ takeComment.setDeleted(Constants.ZERO);
+ takeComment.setCreateTime(now);
+ orderCommentMapper.insert(takeComment);
+ }
+
+ // 4.3 鍙告満锛堝紓鍦板瘎瀛橈級
+ if (isRemote && order.getAcceptDriver() != null && dto.getDriverScore() != null) {
+ OrderComment driverComment = new OrderComment();
+ driverComment.setOrderId(order.getId());
+ driverComment.setOrderCode(order.getCode());
+ driverComment.setMemberId(memberId);
+ driverComment.setTargetType(Constants.THREE); // 3=鍙告満
+ driverComment.setTargetId(order.getAcceptDriver());
+ driverComment.setScore(dto.getDriverScore());
+ driverComment.setContent(dto.getContent());
+ driverComment.setDeleted(Constants.ZERO);
+ driverComment.setCreateTime(now);
+ orderCommentMapper.insert(driverComment);
+ }
+
+ // 5. 鏇存柊闂ㄥ簵/鍙告満骞冲潎璇勫垎
+ updateTargetScore(Constants.ONE, order.getDepositShopId());
+ if (isRemote && order.getTakeShopId() != null) {
+ updateTargetScore(Constants.TWO, order.getTakeShopId());
+ }
+ if (isRemote && order.getAcceptDriver() != null) {
+ updateTargetScore(Constants.THREE, order.getAcceptDriver());
+ }
+ }
+
+ /**
+ * 鏇存柊璇勪环瀵硅薄锛堥棬搴�/鍙告満锛夌殑骞冲潎璇勫垎
+ */
+ private void updateTargetScore(Integer targetType, Integer targetId) {
+ List<OrderComment> comments = orderCommentMapper.selectList(new QueryWrapper<OrderComment>().lambda()
+ .eq(OrderComment::getDeleted, Constants.ZERO)
+ .eq(OrderComment::getTargetType, targetType)
+ .eq(OrderComment::getTargetId, targetId));
+ if (comments.isEmpty()) {
+ return;
+ }
+ double avg = comments.stream()
+ .mapToInt(OrderComment::getScore)
+ .average()
+ .orElse(0.0);
+ BigDecimal score = BigDecimal.valueOf(avg).setScale(1, BigDecimal.ROUND_HALF_UP);
+ Date now = new Date();
+ if (Constants.equalsInteger(targetType, Constants.ONE) || Constants.equalsInteger(targetType, Constants.TWO)) {
+ ShopInfo shopInfo = shopInfoMapper.selectById(targetId);
+ if (shopInfo != null) {
+ shopInfo.setScore(score);
+ shopInfo.setUpdateTime(now);
+ shopInfoMapper.updateById(shopInfo);
+ }
+ } else if (Constants.equalsInteger(targetType, Constants.THREE)) {
+ DriverInfo driverInfo = driverInfoMapper.selectById(targetId);
+ if (driverInfo != null) {
+ driverInfo.setScore(score);
+ driverInfo.setUpdateTime(now);
+ driverInfoMapper.updateById(driverInfo);
+ }
+ }
+ }
+
+ /**
+ * 鍞よ捣寰俊鏀粯锛堝叾浠栬鍗曪級
+ */
+ private PayResponse wxPayForOtherOrder(OtherOrders otherOrders, String openid, Constants.OrdersAttach ordersAttach) {
+ try {
+ WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
+ request.setBody(ordersAttach.getName());
+ request.setAttach(ordersAttach.getKey());
+ request.setOutTradeNo(otherOrders.getOutTradeNo());
+ long totalFee = otherOrders.getPayAccount() != null ? otherOrders.getPayAccount() : 0L;
+ request.setTotalFee((int) totalFee);
+ request.setTimeStart(DateUtil.DateToString(new Date(), "yyyyMMddHHmmss"));
+ request.setSpbillCreateIp(Constants.getIpAddr());
+ request.setOpenid(openid);
+
+ Object response = WxMiniConfig.wxPayService.createOrder(request);
+
+ PayResponse payResponse = new PayResponse();
+ payResponse.setResponse(response);
+ payResponse.setOrderId(otherOrders.getId());
+ return payResponse;
+ } catch (WxPayException e) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏀粯璋冭捣澶辫触锛�" + e.getMessage());
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void shopVerifyOrder(String verifyCode, Integer shopId, List<String> images, String remark) {
+ if (StringUtils.isBlank(verifyCode)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏍搁攢鐮佷笉鑳戒负绌�");
+ }
+ // 鏍规嵁鏍搁攢鐮佹煡鎵捐鍗曪紙浼氬憳鏍搁攢鐮侊級
+ Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+ .eq(Orders::getMemberVerifyCode, verifyCode)
+ .eq(Orders::getDeleted, Constants.ZERO)
+ .last("limit 1"));
+ if (order == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鏍搁攢鐮佹棤鏁�");
+ }
+
+ // 鏌ヨ闂ㄥ簵鍚嶇О鐢ㄤ簬鏃ュ織
+ String shopName = "";
+ ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
+ if (shopInfo != null) {
+ shopName = shopInfo.getName() != null ? shopInfo.getName() : "";
+ }
+
+ Integer status = order.getStatus();
+ Date now = new Date();
+ if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
+ // 寰呭瘎瀛�(1) 鈫� 宸插瘎瀛�(2)锛屼袱绉嶇被鍨嬮�氱敤
+ // 鏍¢獙褰撳墠闂ㄥ簵鏄惁涓鸿鍗曠殑瀛樹欢闂ㄥ簵
+ if (!shopId.equals(order.getDepositShopId())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵锛屾棤娉曟牳閿�");
+ }
+ order.setStatus(Constants.OrderStatus.deposited.getStatus());
+ order.setDepositTime(now);
+ // 閲婃斁褰撳墠鏍搁攢鐮侊紝鐢熸垚鏂扮殑鏍搁攢鐮佷緵鍙栦欢鏃朵娇鐢�
+ releaseVerifyCode(verifyCode);
+ order.setMemberVerifyCode(generateVerifyCode());
+ ordersMapper.updateById(order);
+ // 淇濆瓨瀵勫瓨鍥剧墖锛坥bj_type=2 璁㈠崟瀵勫瓨鍥剧墖锛屾渶澶�3寮狅級
+ saveVerifyImages(order.getId(), images, Constants.FileType.ORDER_DEPOSIT.getKey(), shopId);
+ // 璁板綍璁㈠崟鏃ュ織
+ saveShopVerifyLog(order, "闂ㄥ簵纭瀵勫瓨", "闂ㄥ簵銆�" + shopName + "銆戠‘璁ゅ瘎瀛�", remark, shopId);
+ } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
+ // 寮傚湴瀵勫瓨 + 鏃犲彇浠堕棬搴� 鈫� 鏃犳硶鏍搁攢锛堝鎴疯嚜鍙栵紝鏃犻棬搴楁搷浣滐級
+ if (Constants.equalsInteger(order.getType(), Constants.ONE) && order.getTakeShopId() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曟棤鍙栦欢闂ㄥ簵锛屾棤娉曟牳閿�");
+ }
+ // 鏍¢獙鍙栦欢闂ㄥ簵涓庡綋鍓嶇櫥褰曢棬搴椾竴鑷�
+ if (!shopId.equals(order.getTakeShopId())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵锛屾棤娉曟牳閿�");
+ }
+ // 寰呭彇浠�(5) 鈫� 宸插畬鎴�(7)
+ order.setStatus(Constants.OrderStatus.finished.getStatus());
+ order.setConfirmArriveTime(now);
+ ordersMapper.updateById(order);
+ // 璁㈠崟瀹屾垚锛岄噴鏀炬牳閿�鐮�
+ releaseVerifyCode(verifyCode);
+ // 淇濆瓨鍑哄簱鍥剧墖锛坥bj_type=13 闂ㄥ簵鍑哄簱鍥剧墖锛屾渶澶�3寮狅級
+ saveVerifyImages(order.getId(), images, Constants.FileType.STORE_OUT.getKey(), shopId);
+ // 鐢熸垚鏀剁泭璁板綍
+ calculateAndSaveOrderFees(order.getId());
+ generateRevenueRecords(order.getId());
+ // 璁板綍璁㈠崟鏃ュ織
+ saveShopVerifyLog(order, "闂ㄥ簵纭鍙栦欢", "闂ㄥ簵銆�" + shopName + "銆戠‘璁ゅ彇浠讹紝璁㈠崟瀹屾垚", remark, shopId);
+ } else {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽鏍搁攢");
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void confirmStoreOut(Integer orderId, Integer shopId, List<String> images, String remark) {
+ // 1. 鏌ヨ璁㈠崟
+ Orders order = ordersMapper.selectById(orderId);
+ if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+ }
+ // 2. 鏍¢獙鐘舵�侊細寰呭彇浠�(5)
+ if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽鍑哄簱");
+ }
+ // 3. 鏍¢獙閫炬湡鐘舵�侊細0=鏈�炬湡 鎴� 2=宸叉敮浠�
+ if (order.getOverdueStatus() != null && Constants.equalsInteger(order.getOverdueStatus(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟瀛樺湪閫炬湡鏈敮浠樿垂鐢紝璇峰厛瀹屾垚閫炬湡璐圭敤鏀粯");
+ }
+ // 4. 鏍¢獙纭鍒板簵鏃堕棿涓嶄负绌�
+ if (order.getConfirmArriveTime() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟灏氭湭纭鍒板簵锛屾棤娉曞嚭搴�");
+ }
+ // 5. 鏍¢獙闂ㄥ簵涓庤鍗曞叧绯�
+ if (Constants.equalsInteger(order.getType(), Constants.ZERO)) {
+ // 灏卞湴瀵勫瓨锛氬彇浠堕棬搴楀嵆瀛樹欢闂ㄥ簵
+ if (!shopId.equals(order.getDepositShopId())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵");
+ }
+ } else {
+ // 寮傚湴瀵勫瓨锛氭牎楠屽彇浠堕棬搴�
+ if (order.getTakeShopId() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曟棤鍙栦欢闂ㄥ簵锛屾棤娉曞嚭搴�");
+ }
+ if (!shopId.equals(order.getTakeShopId())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵");
+ }
+ }
+
+ // 6. 鏌ヨ闂ㄥ簵鍚嶇О
+ String shopName = "";
+ ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
+ if (shopInfo != null) {
+ shopName = shopInfo.getName() != null ? shopInfo.getName() : "";
+ }
+
+ // 7. 鏇存柊璁㈠崟鐘舵�佷负宸插畬鎴�
+ Date now = new Date();
+ order.setStatus(Constants.OrderStatus.finished.getStatus());
+ order.setFinishTime(now);
+ order.setUpdateTime(now);
+ ordersMapper.updateById(order);
+
+ // 8. 閲婃斁鏍搁攢鐮�
+ if (StringUtils.isNotBlank(order.getMemberVerifyCode())) {
+ releaseVerifyCode(order.getMemberVerifyCode());
+ }
+
+ // 9. 淇濆瓨鍑哄簱鍥剧墖锛坥bj_type=13 闂ㄥ簵鍑哄簱鍥剧墖锛屾渶澶�3寮狅級
+ saveVerifyImages(order.getId(), images, Constants.FileType.STORE_OUT.getKey(), shopId);
+
+ // 10. 濡傛灉瀛樺湪閫�娆鹃噾棰濓紝鍏堜繚瀛橀��娆捐褰曞啀璋冪敤寰俊閫�娆�
+ // 閫�娆捐褰曞湪閫�娆捐皟鐢ㄥ墠钀藉簱锛岄伩鍏嶉��娆炬垚鍔熶絾鏈湴寮傚父瀵艰嚧鏃犺褰�
+ if (order.getRefundAmount() != null && order.getRefundAmount() > 0
+ && StringUtils.isNotBlank(order.getOutTradeNo())
+ && order.getPayAmount() != null && order.getPayAmount() > 0) {
+ OrdersRefund refundRecord = new OrdersRefund();
+ refundRecord.setOrderId(orderId);
+ refundRecord.setType(3); // 鍑哄簱閫�娆�
+ refundRecord.setCreateTime(now);
+ refundRecord.setRefundRemark(remark);
+ refundRecord.setDeleted(Constants.ZERO);
+ ordersRefundMapper.insert(refundRecord);
+
+ // 璋冪敤寰俊閫�娆撅紙鏀惧湪鏈�鍚庯紝纭繚鍓嶇疆鎿嶄綔鍏ㄩ儴鎴愬姛锛�
+ String refundCode = wxMiniUtilService.wxRefund(
+ order.getOutTradeNo(), order.getPayAmount(), order.getRefundAmount());
+
+ // 閫�娆炬垚鍔熷悗鍥炲~閫�娆惧崟鍙峰拰鏃堕棿
+ refundRecord.setRefundCode(refundCode);
+ refundRecord.setRefundTime(new Date());
+ ordersRefundMapper.updateById(refundRecord);
+ }
+
+ // 11. 鐢熸垚鏀剁泭璁板綍
+ calculateAndSaveOrderFees(orderId);
+ generateRevenueRecords(orderId);
+
+ // 12. 璁板綍璁㈠崟鏃ュ織
+ String logInfo = "闂ㄥ簵銆�" + shopName + "銆戠‘璁ゅ嚭搴擄紝璁㈠崟瀹屾垚";
+ if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
+ logInfo += "锛岄��娆�" + Constants.getFormatMoney(order.getRefundAmount()) + "鍏�";
+ }
+ saveShopVerifyLog(order, "闂ㄥ簵纭鍑哄簱", logInfo, remark, shopId);
+ }
+
+ @Override
+ public void calculateAndSaveOrderFees(Integer orderId) {
+ Orders order = ordersMapper.selectById(orderId);
+ if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+ }
+
+ Long totalAmount = order.getTotalAmount() != null ? order.getTotalAmount() : 0L;
+ // 璐圭巼锛堜负绌烘椂榛樿0锛�
+ BigDecimal depositRate = order.getDepositShopFeeRata() != null ? order.getDepositShopFeeRata() : BigDecimal.ZERO;
+ BigDecimal takeRate = order.getTakeShopFeeRata() != null ? order.getTakeShopFeeRata() : BigDecimal.ZERO;
+ BigDecimal driverRate = order.getDriverFeeRata() != null ? order.getDriverFeeRata() : BigDecimal.ZERO;
+ Long exceptionFeeVal = order.getExceptionFee() != null ? order.getExceptionFee() : 0L;
+
+ //瀛樹欢闂ㄥ簵鏀剁泭
+ Long depositShopFee = new BigDecimal(totalAmount)
+ .multiply(depositRate)
+ .setScale(0, RoundingMode.HALF_UP)
+ .longValue();
+
+ Long takeShopFee = 0L;
+ Long driverFee = 0L;
+
+ if (Constants.equalsInteger(order.getType(), Constants.TWO)) {
+ // 寮傚湴瀵勫瓨锛氬瓨浠堕棬搴� + 鍙告満
+ driverFee = new BigDecimal(totalAmount)
+ .multiply(driverRate)
+ .setScale(0, RoundingMode.HALF_UP)
+ .longValue()
+ + exceptionFeeVal;
+
+ // 寮傚湴瀵勫瓨涓旀湁鍙栦欢闂ㄥ簵锛氬姞涓婂彇浠堕棬搴楁敹鐩�
+ if (order.getTakeShopId() != null) {
+ takeShopFee = new BigDecimal(totalAmount)
+ .multiply(takeRate)
+ .setScale(0, RoundingMode.HALF_UP)
+ .longValue();
+ }
+ }
+
+ ordersMapper.update(new UpdateWrapper<Orders>().lambda()
+ .eq(Orders::getId, orderId)
+ .set(Orders::getDepositShopFee, depositShopFee)
+ .set(Orders::getTakeShopFee, takeShopFee)
+ .set(Orders::getDriverFee, driverFee)
+ .set(Orders::getUpdateTime, new Date()));
+ }
+
+ /**
+ * 鐢熸垚闂ㄥ簵/鍙告満鏀剁泭璁板綍锛堟湭缁撶畻锛�
+ * 璁㈠崟瀹屾垚鏃惰皟鐢紝璇诲彇璁㈠崟涓婂凡璁$畻濂界殑璐圭敤瀛楁
+ */
+ private void generateRevenueRecords(Integer orderId) {
+ Orders order = ordersMapper.selectById(orderId);
+ if (order == null) {
+ return;
+ }
+ Date now = new Date();
+ Long depositShopFee = order.getDepositShopFee() != null ? order.getDepositShopFee() : 0L;
+ Long takeShopFee = order.getTakeShopFee() != null ? order.getTakeShopFee() : 0L;
+ Long driverFee = order.getDriverFee() != null ? order.getDriverFee() : 0L;
+
+ // 瀛樹欢闂ㄥ簵鏀剁泭
+ if (depositShopFee > 0 && order.getDepositShopId() != null) {
+ ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
+ if (depositShop != null && depositShop.getRegionMemberId() != null) {
+ revenueMapper.insert(buildRevenue(depositShop.getRegionMemberId(), Constants.TWO,
+ depositShopFee, orderId, order.getCode()));
+ }
+ }
+
+ // 鍙栦欢闂ㄥ簵鏀剁泭锛堝紓鍦板瘎瀛樹笖鏈夊彇浠堕棬搴楋級
+ if (takeShopFee > 0 && order.getTakeShopId() != null) {
+ ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
+ if (takeShop != null && takeShop.getRegionMemberId() != null) {
+ revenueMapper.insert(buildRevenue(takeShop.getRegionMemberId(), Constants.TWO,
+ takeShopFee, orderId, order.getCode()));
+ }
+ }
+
+ // 鍙告満鏀剁泭锛堝紓鍦板瘎瀛橈級
+ if (driverFee > 0 && order.getAcceptDriver() != null) {
+ DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver());
+ if (driver != null && driver.getMemberId() != null) {
+ revenueMapper.insert(buildRevenue(driver.getMemberId(), Constants.ONE,
+ driverFee, orderId, order.getCode()));
+ }
+ }
+ }
+
+ /**
+ * 鏋勫缓鏀剁泭璁板綍
+ */
+ private Revenue buildRevenue(Integer memberId, Integer memberType, Long amount, Integer orderId, String orderNo) {
+ Revenue revenue = new Revenue();
+ revenue.setMemberId(memberId);
+ revenue.setMemberType(memberType); // 1=鍙告満, 2=闂ㄥ簵
+ revenue.setType(Constants.ZERO); // 0=瀹屾垚璁㈠崟
+ revenue.setOptType(Constants.ONE); // 1=鏀跺叆
+ revenue.setAmount(amount);
+ revenue.setVaildStatus(Constants.ZERO); // 0=鍏ヨ处涓紙鏈粨绠楋級
+ revenue.setObjId(orderId);
+ revenue.setObjType(Constants.ZERO); // 0=璁㈠崟涓氬姟
+ revenue.setStatus(Constants.ZERO); // 0=鎴愬姛
+ revenue.setOrderNo(orderNo);
+ revenue.setDeleted(Constants.ZERO);
+ revenue.setCreateTime(new Date());
+ return revenue;
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void driverVerifyOrder(String verifyCode, List<String> images, String remark, Integer driverId) {
+ if (StringUtils.isBlank(verifyCode)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏍搁攢鐮佷笉鑳戒负绌�");
+ }
+ // 鏍规嵁鍙告満鏍搁攢鐮佹煡鎵捐鍗�
+ Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+ .eq(Orders::getDriverVerifyCode, verifyCode)
+ .eq(Orders::getDeleted, Constants.ZERO)
+ .last("limit 1"));
+ if (order == null) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鏍搁攢鐮佹棤鏁�");
+ }
+
+ // 浠呭紓鍦板瘎瀛� + 鏈夊彇浠堕棬搴� + 娲鹃�佷腑(4) 鍙牳閿�
+ if (!Constants.equalsInteger(order.getType(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "浠呭紓鍦板瘎瀛樿鍗曟敮鎸佸徃鏈烘牳閿�");
+ }
+ if (order.getTakeShopId() == null) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曟棤鍙栦欢闂ㄥ簵锛屾棤闇�鍙告満鏍搁攢");
+ }
+ if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getStatus())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽鏍搁攢");
+ }
+
+ // 娲鹃�佷腑(4) 鈫� 宸插埌搴�(5)
+ order.setStatus(Constants.OrderStatus.arrived.getStatus());
+ order.setArriveTime(new Date());
+ if (StringUtils.isNotBlank(remark)) {
+ order.setRemark(remark);
+ }
+ ordersMapper.updateById(order);
+
+ // 閲婃斁鍙告満鏍搁攢鐮�
+ releaseVerifyCode(verifyCode);
+
+ // 淇濆瓨闄勪欢锛坥bj_type=3 闂ㄥ簵鍏ュ簱鍥剧墖锛屾渶澶�3寮狅級
+ saveVerifyImages(order.getId(), images, Constants.FileType.ORDER_TAKE.getKey(), driverId);
+ }
+
+ /**
+ * 淇濆瓨鏍搁攢闄勪欢鍒� multifile 琛�
+ *
+ * @param orderId 璁㈠崟涓婚敭
+ * @param images 鍥剧墖鍦板潃鍒楄〃锛堟渶澶�3寮狅級
+ * @param objType 闄勪欢绫诲瀷
+ * @param creator 鍒涘缓浜虹紪鐮�
+ */
+ private void saveVerifyImages(Integer orderId, List<String> images, int objType, Integer creator) {
+ if (images == null || images.isEmpty()) return;
+ List<String> saveImages = images.size() > 3 ? images.subList(0, 3) : images;
+ Date now = new Date();
+ int sortNum = 1;
+ for (String imgUrl : saveImages) {
+ if (StringUtils.isBlank(imgUrl)) continue;
+ Multifile multifile = new Multifile();
+ multifile.setObjId(orderId);
+ multifile.setObjType(objType);
+ multifile.setFileurl(imgUrl);
+ multifile.setType(Constants.ZERO);
+ multifile.setCreator(creator);
+ multifile.setCreateDate(now);
+ multifile.setIsdeleted(Constants.ZERO);
+ multifile.setSortnum(sortNum++);
+ multifileMapper.insert(multifile);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+ public void confirmCustomerArrived(Integer orderId, Integer shopId) {
+ // 1. 鏌ヨ璁㈠崟
+ Orders order = ordersMapper.selectById(orderId);
+ if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+ }
+
+ // 2. 鏍¢獙璁㈠崟鐘舵�侊細寰呭彇浠�(5)
+ if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽璇ユ搷浣�");
+ }
+
+ // 3. 鏍¢獙闂ㄥ簵涓庤鍗曞叧绯�
+ if (Constants.equalsInteger(order.getType(), Constants.ONE) && order.getTakeShopId() != null) {
+ // 寮傚湴瀵勫瓨鏈夊彇浠堕棬搴楋細鏍¢獙鍙栦欢闂ㄥ簵
+ if (!shopId.equals(order.getTakeShopId())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵");
+ }
+ } else if (Constants.equalsInteger(order.getType(), Constants.ZERO)) {
+ // 灏卞湴瀵勫瓨锛氭牎楠屽瓨浠堕棬搴楋紙鍙栦欢闂ㄥ簵鍚屽瓨浠堕棬搴楋級
+ if (!shopId.equals(order.getDepositShopId())) {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵");
+ }
+ } else {
+ throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曟棤鍙栦欢闂ㄥ簵锛屾棤娉曠‘璁ゅ埌搴�");
+ }
+
+ // 4. 鏌ヨ闂ㄥ簵鍚嶇О锛堢敤浜庢棩蹇楋級
+ String shopName = "";
+ ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
+ if (shopInfo != null) {
+ shopName = shopInfo.getName() != null ? shopInfo.getName() : "";
+ }
+
+ // 5. 璁$畻閫炬湡璐圭敤
+ List<OrdersDetail> details = ordersDetailMapper.selectList(
+ new QueryWrapper<OrdersDetail>().lambda()
+ .eq(OrdersDetail::getOrderId, orderId)
+ .eq(OrdersDetail::getDeleted, Constants.ZERO));
+ OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(order, details);
+
+ Date now = new Date();
+
+ if (overdueInfo.getOverdue() && overdueInfo.getOverdueDays() > 0) {
+ // 瀛樺湪閫炬湡锛氭爣璁伴�炬湡鐘舵�侊紝璁㈠崟淇濇寔褰撳墠鐘舵��
+ order.setConfirmArriveTime(now);
+ order.setOverdueStatus(Constants.ONE);
+ order.setOverdueDays(overdueInfo.getOverdueDays());
+ order.setOverdueAmount(overdueInfo.getOverdueFee());
+ order.setUpdateTime(now);
+ ordersMapper.updateById(order);
+
+ // 璁板綍璁㈠崟鏃ュ織
+ saveShopVerifyLog(order, "纭椤惧鍒板簵锛堥�炬湡锛�",
+ "闂ㄥ簵銆�" + shopName + "銆戠‘璁ら【瀹㈠埌搴楋紝閫炬湡" + overdueInfo.getOverdueDays()
+ + "澶╋紝閫炬湡璐圭敤" + Constants.getFormatMoney(overdueInfo.getOverdueFee()) + "鍏�",
+ null, shopId);
+ } else {
+ // 鏈�炬湡锛氭爣璁伴�炬湡鐘舵�佷负0锛岃鍗曚繚鎸佸綋鍓嶇姸鎬�
+ order.setConfirmArriveTime(now);
+ order.setOverdueStatus(Constants.ZERO);
+
+ // 灏卞湴瀵勫瓨锛氳绠楁槸鍚﹂渶瑕侀��娆�
+ if (Constants.equalsInteger(order.getType(), Constants.ZERO) && !CollectionUtils.isEmpty(details)) {
+ int actualDays = calcActualDepositDays(now, order.getDepositTime());
+ order.setDepositDays(actualDays);
+
+ int estimatedDays = order.getEstimatedDepositDays() != null ? order.getEstimatedDepositDays() : 1;
+ int refundDays = estimatedDays - actualDays;
+ if (refundDays > 0) {
+ // 閫�娆鹃噾棰� = 閫�娆惧ぉ鏁� 脳 危(鐗╁搧鍗曚环 脳 鏁伴噺)
+ long dailyBaseFee = 0L;
+ for (OrdersDetail d : details) {
+ dailyBaseFee += (d.getUnitPrice() != null ? d.getUnitPrice() : 0L)
+ * (d.getNum() != null ? d.getNum() : 0);
+ }
+ long refundAmount = (long) refundDays * dailyBaseFee;
+ order.setRefundAmount(refundAmount);
+ }
+ }
+
+ order.setUpdateTime(now);
+ ordersMapper.updateById(order);
+
+ // 閫�娆惧鑷存�婚噾棰濆彉鍖栵紝閲嶇畻涓夋柟鏀剁泭
+ if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
+ long newTotal = (order.getTotalAmount() != null ? order.getTotalAmount() : 0L) - order.getRefundAmount();
+ order.setTotalAmount(newTotal);
+ ordersMapper.update(new UpdateWrapper<Orders>().lambda()
+ .eq(Orders::getId, orderId)
+ .set(Orders::getTotalAmount, newTotal));
+ calculateAndSaveOrderFees(orderId);
+ }
+
+ // 璁板綍璁㈠崟鏃ュ織
+ String logInfo = "闂ㄥ簵銆�" + shopName + "銆戠‘璁ら【瀹㈠埌搴楋紝鏈�炬湡";
+ if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
+ logInfo += "锛岄渶閫�娆�" + Constants.getFormatMoney(order.getRefundAmount()) + "鍏�";
+ }
+ saveShopVerifyLog(order, "纭椤惧鍒板簵", logInfo, null, shopId);
+ }
+ }
+
+ /**
+ * 鏋勫缓璁㈠崟鐘舵�佹弿杩�
+ */
+ private String buildStatusDesc(Orders order) {
+ boolean isLocal = Constants.equalsInteger(order.getType(), Constants.ZERO);
+ Integer status = order.getStatus();
+ if (status == null) return "";
+
+ if (Constants.equalsInteger(status, Constants.OrderStatus.waitPay.getStatus())) {
+ String minutes = "";
+ try {
+ minutes = operationConfigBiz.getConfig().getAutoCancelTime();
+ } catch (Exception ignored) {}
+ return "璇峰湪" + (StringUtils.isNotBlank(minutes) ? minutes : "") + "鍒嗛挓鍐呭畬鎴愭敮浠橈紝瓒呮椂璁㈠崟灏嗚嚜鍔ㄥ彇娑�";
+ }
+ if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
+ return isLocal ? "璁㈠崟宸叉敮浠橈紝璇风瓑寰呴棬搴楃‘璁ゆ帴鍗�" : "璁㈠崟宸叉敮浠橈紝璇风瓑寰呴棬搴楃‘璁ゆ帴鍗�";
+ }
+ if (Constants.equalsInteger(status, Constants.OrderStatus.deposited.getStatus())) {
+ return isLocal ? "琛屾潕宸插瘎瀛橈紝璇峰嚟鍙栦欢鐮佸墠寰�鎸囧畾闂ㄥ簵鍙栦欢" : "闂ㄥ簵宸叉帴鍗曪紝姝e湪涓烘偍瀹夋帓鍙栦欢鍙告満";
+ }
+ if (Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
+ return isLocal ? "琛屾潕宸插瘎瀛橈紝璇峰嚟鍙栦欢鐮佸墠寰�鎸囧畾闂ㄥ簵鍙栦欢" : "宸叉湁鍙告満鎶㈠崟锛屾鍓嶅線鍙栦欢鍦扮偣";
+ }
+ if (Constants.equalsInteger(status, Constants.OrderStatus.delivering.getStatus())) {
+ return "鍙告満宸插彇浠讹紝姝h繍寰�鐩殑鍦�";
+ }
+ if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
+ return "琛屾潕宸查�佽揪鏈嶅姟鐐癸紝璇峰強鏃跺墠寰�鍙栦欢";
+ }
+ if (Constants.equalsInteger(status, Constants.OrderStatus.finished.getStatus())) {
+ if (Constants.equalsInteger(order.getCommentStatus(), Constants.ONE)) {
+ return "鎰熻阿鎮ㄧ殑鐢ㄥ績璇勪环锛岀鎮ㄥ嚭琛岄『鍒╋紝鏃呴�旀剦蹇紒";
+ }
+ return "璁㈠崟宸插畬鎴愶紝鎰熻阿鎮ㄧ殑鏀寔锛岃瀵规湰娆℃湇鍔″仛鍑鸿瘎浠�";
+ }
+ if (Constants.equalsInteger(status, Constants.OrderStatus.cancelled.getStatus())) {
+ return "璁㈠崟宸插彇娑堬紝鎰熻阿鎮ㄧ殑鏀寔锛屾杩庝笅娆″啀浼氾紒";
+ }
+ if (Constants.equalsInteger(status, Constants.OrderStatus.cancelling.getStatus())) {
+ return "閫�娆剧敵璇峰凡鎻愪氦锛屽钩鍙颁細灏藉揩涓烘偍澶勭悊閫�娆�";
+ }
+ if (Constants.equalsInteger(status, Constants.OrderStatus.closed.getStatus())) {
+ return "閫�娆惧凡鎴愬姛鍘熻矾杩斿洖锛岃娉ㄦ剰鏌ユ敹";
+ }
+ return "";
+ }
+
+ /**
+ * 璁$畻鏀粯鍊掕鏃舵绉�
+ */
+ private Long calcPayCountdownMs(Orders order) {
+ try {
+ String minutesStr = operationConfigBiz.getConfig().getAutoCancelTime();
+ if (StringUtils.isBlank(minutesStr)) return -1L;
+ int minutes = Integer.parseInt(minutesStr);
+ long deadline = order.getCreateTime().getTime() + minutes * 60 * 1000L;
+ long remaining = deadline - System.currentTimeMillis();
+ return remaining > 0 ? remaining : -1L;
+ } catch (Exception e) {
+ return -1L;
+ }
+ }
+ public OverdueFeeVO calculateOverdueFee(Integer orderId) {
+ Orders order = ordersMapper.selectById(orderId);
+ if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
+ throw new BusinessException(ResponseStatus.DATA_EMPTY);
+ }
+
+ // 鏌ヨ璁㈠崟鏄庣粏
+ List<OrdersDetail> details = ordersDetailMapper.selectList(
+ new QueryWrapper<OrdersDetail>().lambda()
+ .eq(OrdersDetail::getOrderId, orderId)
+ .eq(OrdersDetail::getDeleted, Constants.ZERO));
+
+ return calculateOverdueFeeInternal(order, details);
+ }
+
+ /**
+ * 閫炬湡璐圭敤鍐呴儴璁$畻锛堜笉鏌ュ簱锛屾帴鍙楅鏌ヨ鐨勬暟鎹級
+ * 渚涘垎椤电瓑宸叉煡璇㈡槑缁嗙殑涓氬姟鍦烘櫙澶嶇敤锛岄伩鍏嶉噸澶嶆煡璇�
+ */
+ private OverdueFeeVO calculateOverdueFeeInternal(Orders order, List<OrdersDetail> details) {
+ if (CollectionUtils.isEmpty(details)) {
+ OverdueFeeVO vo = new OverdueFeeVO();
+ vo.setOverdue(false);
+ vo.setOverdueDays(0);
+ vo.setOverdueFee(0L);
+ vo.setDailyBaseFee(0L);
+ return vo;
+ }
+
+ // 鐗╁搧鍩虹鏃ヨ垂鐢� = 危(鍗曚环 脳 鏁伴噺)
+ long dailyBaseFee = 0L;
+ for (OrdersDetail d : details) {
+ dailyBaseFee += (d.getUnitPrice() != null ? d.getUnitPrice() : 0L)
+ * (d.getNum() != null ? d.getNum() : 0);
+ }
+
+ Date now = new Date();
+ int overdueDays;
+ long overdueFee;
+
+ if (Constants.equalsInteger(order.getType(), Constants.ZERO)) {
+ // ========== 灏卞湴瀵勫瓨 ==========
+ overdueDays = calcLocalOverdueDays(now, order.getExpectedTakeTime());
+ overdueFee = (long) overdueDays * dailyBaseFee;
+
+ OverdueFeeVO vo = new OverdueFeeVO();
+ vo.setOverdue(overdueDays > 0);
+ vo.setOverdueDays(overdueDays);
+ vo.setOverdueFee(overdueFee);
+ vo.setDailyBaseFee(dailyBaseFee);
+ return vo;
+
+ } else {
+ // ========== 寮傚湴瀵勫瓨 ==========
+ // 鏉′欢锛氬瓨鍦ㄥ彇浠堕棬搴� 涓� 璁㈠崟澶勪簬宸插埌搴楃姸鎬�(5)
+ if (order.getTakeShopId() == null
+ || !Constants.equalsInteger(order.getStatus(), Constants.FIVE)) {
+ OverdueFeeVO vo = new OverdueFeeVO();
+ vo.setOverdue(false);
+ vo.setOverdueDays(0);
+ vo.setOverdueFee(0L);
+ vo.setDailyBaseFee(dailyBaseFee);
+ vo.setDiscountRate(null);
+ return vo;
+ }
+
+ overdueDays = calcRemoteOverdueDays(now, order.getArriveTime());
+
+ // 鎶樻墸姣旂巼
+ String discountStr = operationConfigBiz.getConfig().getUnpickedDiscount();
+ BigDecimal discountRate = StringUtils.isNotBlank(discountStr)
+ ? new BigDecimal(discountStr) : BigDecimal.ONE;
+
+ overdueFee = new BigDecimal(overdueDays)
+ .multiply(new BigDecimal(dailyBaseFee))
+ .multiply(discountRate)
+ .setScale(0, RoundingMode.HALF_UP)
+ .longValue();
+
+ OverdueFeeVO vo = new OverdueFeeVO();
+ vo.setOverdue(overdueDays > 0);
+ vo.setOverdueDays(overdueDays);
+ vo.setOverdueFee(overdueFee);
+ vo.setDailyBaseFee(dailyBaseFee);
+ vo.setDiscountRate(discountStr);
+ return vo;
+ }
+ }
+
+ /**
+ * 璁$畻瀹為檯瀵勫瓨澶╂暟锛坉epositTime 鍒� now 鐨勫ぉ鏁板樊锛屾渶灏�1澶╋級
+ */
+ private int calcActualDepositDays(Date now, Date depositTime) {
+ if (depositTime == null || now == null) {
+ return 1;
+ }
+ Calendar depositCal = Calendar.getInstance();
+ depositCal.setTime(depositTime);
+ depositCal.set(Calendar.HOUR_OF_DAY, 0);
+ depositCal.set(Calendar.MINUTE, 0);
+ depositCal.set(Calendar.SECOND, 0);
+ depositCal.set(Calendar.MILLISECOND, 0);
+
+ Calendar nowCal = Calendar.getInstance();
+ nowCal.setTime(now);
+ nowCal.set(Calendar.HOUR_OF_DAY, 0);
+ nowCal.set(Calendar.MINUTE, 0);
+ nowCal.set(Calendar.SECOND, 0);
+ nowCal.set(Calendar.MILLISECOND, 0);
+
+ long diffMs = nowCal.getTimeInMillis() - depositCal.getTimeInMillis();
+ int days = (int) (diffMs / (1000 * 60 * 60 * 24));
+ return Math.max(days, 1);
+ }
+
+ /**
+ * 灏卞湴瀵勫瓨閫炬湡澶╂暟璁$畻
+ * 杩囦簡棰勮鍙栦欢鏃堕棿褰撳ぉ鐨�12鐐瑰悗鎵嶇畻涓�澶�
+ */
+ private int calcLocalOverdueDays(Date now, Date expectedTakeTime) {
+ if (expectedTakeTime == null || !now.after(expectedTakeTime)) {
+ return 0;
+ }
+ // 鍩哄噯鏃堕棿 = 棰勮鍙栦欢鏃ユ湡鐨�12:00
+ Calendar baseCal = Calendar.getInstance();
+ baseCal.setTime(expectedTakeTime);
+ baseCal.set(Calendar.HOUR_OF_DAY, 12);
+ baseCal.set(Calendar.MINUTE, 0);
+ baseCal.set(Calendar.SECOND, 0);
+ baseCal.set(Calendar.MILLISECOND, 0);
+ Date baseTime = baseCal.getTime();
+
+ if (!now.after(baseTime)) {
+ return 0;
+ }
+ // 閫炬湡澶╂暟 = 褰撳墠鏃ユ湡 - 鍩哄噯鏃ユ湡锛堟寜澶╁彇宸級
+ Calendar nowCal = Calendar.getInstance();
+ nowCal.setTime(now);
+ nowCal.set(Calendar.HOUR_OF_DAY, 0);
+ nowCal.set(Calendar.MINUTE, 0);
+ nowCal.set(Calendar.SECOND, 0);
+ nowCal.set(Calendar.MILLISECOND, 0);
+
+ Calendar baseDateCal = Calendar.getInstance();
+ baseDateCal.setTime(baseTime);
+ baseDateCal.set(Calendar.HOUR_OF_DAY, 0);
+ baseDateCal.set(Calendar.MINUTE, 0);
+ baseDateCal.set(Calendar.SECOND, 0);
+ baseDateCal.set(Calendar.MILLISECOND, 0);
+
+ long diffMs = nowCal.getTimeInMillis() - baseDateCal.getTimeInMillis();
+ int days = (int) (diffMs / (1000 * 60 * 60 * 24));
+ return Math.max(days, 0);
+ }
+
+ /**
+ * 寮傚湴瀵勫瓨閫炬湡澶╂暟璁$畻
+ * 杩囦簡杞Щ鍒板簵鏃堕棿褰撳ぉ鐨勬櫄涓�12鐐癸紙24:00锛夊悗鎵嶇畻绗竴澶�
+ */
+ private int calcRemoteOverdueDays(Date now, Date arriveTime) {
+ if (arriveTime == null || !now.after(arriveTime)) {
+ return 0;
+ }
+ // 鍩哄噯鏃堕棿 = 杞Щ鍒板簵鏃ユ湡鐨勬鏃� 00:00锛堝嵆褰撳ぉ24:00锛�
+ Calendar baseCal = Calendar.getInstance();
+ baseCal.setTime(arriveTime);
+ baseCal.set(Calendar.HOUR_OF_DAY, 0);
+ baseCal.set(Calendar.MINUTE, 0);
+ baseCal.set(Calendar.SECOND, 0);
+ baseCal.set(Calendar.MILLISECOND, 0);
+ baseCal.add(Calendar.DAY_OF_MONTH, 1); // 娆℃棩00:00 = 褰撳ぉ24:00
+ Date baseTime = baseCal.getTime();
+
+ if (!now.after(baseTime)) {
+ return 0;
+ }
+ // 閫炬湡澶╂暟 = 褰撳墠鏃ユ湡 - 鍩哄噯鏃ユ湡
+ Calendar nowCal = Calendar.getInstance();
+ nowCal.setTime(now);
+ nowCal.set(Calendar.HOUR_OF_DAY, 0);
+ nowCal.set(Calendar.MINUTE, 0);
+ nowCal.set(Calendar.SECOND, 0);
+ nowCal.set(Calendar.MILLISECOND, 0);
+
+ Calendar baseDateCal = Calendar.getInstance();
+ baseDateCal.setTime(baseTime);
+ baseDateCal.set(Calendar.HOUR_OF_DAY, 0);
+ baseDateCal.set(Calendar.MINUTE, 0);
+ baseDateCal.set(Calendar.SECOND, 0);
+ baseDateCal.set(Calendar.MILLISECOND, 0);
+
+ long diffMs = nowCal.getTimeInMillis() - baseDateCal.getTimeInMillis();
+ int days = (int) (diffMs / (1000 * 60 * 60 * 24));
+ return Math.max(days, 0);
+ }
+
}
--
Gitblit v1.9.3