Browse Source

后台订单合并,产品复制功能,直播商品警告,不可购买提示,直播间库存问题,增加销售名称筛选,商品重置问题,直播数据人数

yuhongqi 3 days ago
parent
commit
d16756fabd

+ 55 - 0
fs-admin/src/main/java/com/fs/live/controller/OrderController.java

@@ -0,0 +1,55 @@
+package com.fs.live.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ParseUtils;
+import com.fs.his.utils.PhoneUtil;
+import com.fs.hisStore.service.IMergedOrderService;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 合并订单Controller
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Api("合并订单管理")
+@RestController
+@RequestMapping("/order")
+public class OrderController extends BaseController
+{
+    @Autowired
+    private IMergedOrderService mergedOrderService;
+
+    /**
+     * 查询合并订单列表
+     */
+    @ApiOperation("查询合并订单列表")
+    @GetMapping("/list")
+    public TableDataInfo list(MergedOrderQueryParam param)
+    {
+        startPage();
+        List<MergedOrderVO> list = mergedOrderService.selectMergedOrderList(param);
+        for (MergedOrderVO vo : list) {
+            vo.setUserPhone(ParseUtils.parsePhone(vo.getUserPhone()));
+            vo.setPhone(ParseUtils.parsePhone(vo.getPhone()));
+            vo.setSalesPhone(ParseUtils.parsePhone(vo.getSalesPhone()));
+            vo.setUserAddress(ParseUtils.parseAddress(vo.getUserAddress()));
+        }
+        return getDataTable(list);
+    }
+}

+ 7 - 1
fs-company/src/main/java/com/fs/company/controller/live/LiveDataController.java

@@ -22,6 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -99,7 +100,12 @@ public class LiveDataController extends BaseController
     @GetMapping("/exportLiveUserDetail")
     public AjaxResult exportLiveUserDetail(@RequestParam Long liveId, HttpServletRequest request) {
         CompanyUser user = tokenService.getLoginUser(request).getUser();
-        List<LiveUserDetailExportVO> list = liveDataService.exportLiveUserDetail(liveId,user.getCompanyId(),user.getUserId());
+        List<LiveUserDetailExportVO> list = new ArrayList<>();
+        if ("00".equals(user.getUserType())) {
+            liveDataService.getLiveUserDetailListBySql(liveId, user.getCompanyId(), null);
+        } else {
+            list = liveDataService.exportLiveUserDetail(liveId,user.getCompanyId(),user.getUserId());
+        }
         if (list == null || list.isEmpty()) {
             return AjaxResult.error("未找到用户详情数据");
         }

+ 10 - 0
fs-service/src/main/java/com/fs/hisStore/mapper/MergedOrderMapper.java

@@ -2,6 +2,8 @@ package com.fs.hisStore.mapper;
 
 import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
 import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
@@ -15,6 +17,14 @@ import java.util.List;
  */
 public interface MergedOrderMapper
 {
+    /**
+     * 查询合并的订单列表(销售订单+商城订单+直播订单)
+     *
+     * @param param 查询参数
+     * @return 合并后的订单列表
+     */
+    List<MergedOrderVO> selectMergedOrderList(@Param("maps") MergedOrderQueryParam param);
+
     /**
      * 查询合并的订单列表(商城订单+直播订单)
      *

+ 4 - 0
fs-service/src/main/java/com/fs/hisStore/service/IMergedOrderService.java

@@ -2,6 +2,8 @@ package com.fs.hisStore.service;
 
 import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
 import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
 
 import java.util.List;
 
@@ -20,5 +22,7 @@ public interface IMergedOrderService
      * @return 合并后的订单列表
      */
     List<FsMergedOrderListQueryVO> selectMergedOrderListVO(FsMyStoreOrderQueryParam param);
+
+    List<MergedOrderVO> selectMergedOrderList(MergedOrderQueryParam param);
 }
 

+ 26 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -1514,12 +1514,38 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
         FsStoreProductScrm fsStoreProductScrm = fsStoreProductMapper.selectFsStoreProductById(productId);
         if (fsStoreProductScrm == null) return R.error("商品不存在");
 
+        // 查询原商品的规格属性与属性值
+        List<FsStoreProductAttrScrm> attrList = fsStoreProductAttrMapper.selectFsStoreProductAttrByProductId(productId);
+        List<FsStoreProductAttrValueScrm> attrValueList = fsStoreProductAttrValueMapper.selectFsStoreProductAttrValueByProductId(productId);
+
         FsStoreProductScrm copy = new FsStoreProductScrm();
         BeanUtils.copyProperties(fsStoreProductScrm, copy);
         copy.setProductId(null);
         copy.setIsAudit("0");
         fsStoreProductMapper.insertFsStoreProduct(copy);
 
+        // 复制规格属性
+        if (attrList != null && !attrList.isEmpty()) {
+            for (FsStoreProductAttrScrm attr : attrList) {
+                FsStoreProductAttrScrm newAttr = new FsStoreProductAttrScrm();
+                BeanUtils.copyProperties(attr, newAttr);
+                newAttr.setId(null);
+                newAttr.setProductId(copy.getProductId());
+                fsStoreProductAttrMapper.insertFsStoreProductAttr(newAttr);
+            }
+        }
+
+        // 复制属性值
+        if (attrValueList != null && !attrValueList.isEmpty()) {
+            for (FsStoreProductAttrValueScrm val : attrValueList) {
+                FsStoreProductAttrValueScrm newVal = new FsStoreProductAttrValueScrm();
+                BeanUtils.copyProperties(val, newVal);
+                newVal.setId(null);
+                newVal.setProductId(copy.getProductId());
+                fsStoreProductAttrValueMapper.insertFsStoreProductAttrValue(newVal);
+            }
+        }
+
         return R.ok();
 
     }

+ 55 - 11
fs-service/src/main/java/com/fs/hisStore/service/impl/MergedOrderServiceImpl.java

@@ -9,15 +9,14 @@ import com.fs.hisStore.param.FsMyStoreOrderQueryParam;
 import com.fs.hisStore.service.IMergedOrderService;
 import com.fs.hisStore.vo.FsMergedOrderListQueryVO;
 import com.fs.hisStore.vo.FsStoreOrderItemVO;
+import com.fs.live.param.MergedOrderQueryParam;
+import com.fs.live.vo.MergedOrderVO;
 import com.fs.store.config.StoreConfig;
 import com.fs.system.service.ISysConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.List;
+import java.util.*;
 
 /**
  * 合并订单Service实现类
@@ -31,27 +30,72 @@ public class MergedOrderServiceImpl implements IMergedOrderService
     @Autowired
     private MergedOrderMapper mergedOrderMapper;
 
+    /*
+     * 后端合并
+     * */
+    @Override
+    public List<MergedOrderVO> selectMergedOrderList(MergedOrderQueryParam param)
+    {
+        List<MergedOrderVO> list = mergedOrderMapper.selectMergedOrderList(param);
+
+        // 处理商品JSON
+        for (MergedOrderVO vo : list)
+        {
+            if (StringUtils.isNotEmpty(vo.getItemJson()))
+            {
+                try
+                {
+                    JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
+                    if (jsonArray != null && jsonArray.size() > 0)
+                    {
+                        vo.setItems(jsonArray);
+                    }
+                }
+                catch (Exception e)
+                {
+                    // JSON解析失败,忽略
+                }
+            }
+        }
+
+        return list;
+    }
+
+
     @Autowired
     private ISysConfigService configService;
 
+    /*
+     * 小程序合并
+     * */
     @Override
     public List<FsMergedOrderListQueryVO> selectMergedOrderListVO(FsMyStoreOrderQueryParam param)
     {
         List<FsMergedOrderListQueryVO> list = mergedOrderMapper.selectMergedOrderListVO(param);
-        
+
         for (FsMergedOrderListQueryVO vo : list)
         {
+
             // 处理商品JSON
             if (StringUtils.isNotEmpty(vo.getItemJson()))
             {
-                JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
-                List<FsStoreOrderItemVO> items = JSONUtil.toList(jsonArray, FsStoreOrderItemVO.class);
-                if (items != null && items.size() > 0)
-                {
+                List<FsStoreOrderItemVO> items = new ArrayList<>();
+                if (2 == vo.getOrderType()) {
+                    FsStoreOrderItemVO bean = JSONUtil.toBean(vo.getItemJson(), FsStoreOrderItemVO.class);
+                    items.add(bean);
                     vo.setItems(items);
+                }else {
+                    JSONArray jsonArray = JSONUtil.parseArray(vo.getItemJson());
+                    items = JSONUtil.toList(jsonArray, FsStoreOrderItemVO.class);
+                    if (items != null && items.size() > 0)
+                    {
+                        vo.setItems(items);
+                    }
                 }
+
+
             }
-            
+
             // 处理是否可以申请售后
             vo.setIsAfterSales(0);
             if (vo.getStatus() != null && vo.getStatus().equals(OrderInfoEnum.STATUS_3.getValue()))
@@ -83,7 +127,7 @@ public class MergedOrderServiceImpl implements IMergedOrderService
                 vo.setIsAfterSales(1);
             }
         }
-        
+
         return list;
     }
 }

+ 37 - 0
fs-service/src/main/java/com/fs/live/enums/LiveGoodsAddErrorEnum.java

@@ -0,0 +1,37 @@
+package com.fs.live.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 直播商品添加失败原因枚举
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Getter
+@AllArgsConstructor
+public enum LiveGoodsAddErrorEnum {
+
+    /**
+     * 未上架
+     */
+    NOT_SHELVED("未上架"),
+
+    /**
+     * 未审核
+     */
+    NOT_AUDITED("未审核"),
+
+    /**
+     * 已删除
+     */
+    DELETED("已删除");
+
+    /**
+     * 错误描述
+     */
+    private String desc;
+
+}
+

+ 85 - 0
fs-service/src/main/java/com/fs/live/param/MergedOrderQueryParam.java

@@ -0,0 +1,85 @@
+package com.fs.live.param;
+
+import com.fs.common.param.BaseQueryParam;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 合并订单查询参数
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedOrderQueryParam extends BaseQueryParam implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 订单状态 */
+    private String status;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** 公司ID */
+    private Long companyId;
+
+    /** 部门ID */
+    private Long deptId;
+
+    /** 订单号 */
+    private String orderCode;
+
+    /** 订单号列表(多个订单号用逗号分隔) */
+    private String orderCodeList;
+
+    /** 运单号 */
+    private String deliveryId;
+
+    /** 银行交易流水号 */
+    private String bankTransactionId;
+
+    /** 手机号 */
+    private String userPhone;
+
+    /** 收件人 */
+    private String realName;
+
+    /** 产品名称 */
+    private String productName;
+
+    /** 物流状态 */
+    private Integer deliveryStatus;
+
+    /** 物流结算状态 */
+    private Integer deliveryPayStatus;
+
+    /** 支付方式 */
+    private String payType;
+
+    /** 下单时间范围 */
+    private String createTimeRange;
+
+    /** 支付时间范围 */
+    private String payTimeRange;
+
+    /** 发货时间范围 */
+    private String deliverySendTimeRange;
+
+    /** 回单时间范围 */
+    private String deliveryImportTimeRange;
+
+    /** 小程序AppId */
+    private String appId;
+
+    /** 订单类型筛选:1-销售订单,2-商城订单,3-直播订单,null-全部 */
+    private Integer orderTypeFilter;
+
+    /** 销售名称(company_user表的user_name) */
+    private String salesName;
+
+    /** 员工姓名(company_user表的nick_name) */
+    private String companyUserNickName;
+}
+

+ 96 - 2
fs-service/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java

@@ -3,6 +3,7 @@ package com.fs.live.service.impl;
 
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysUser;
+import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.DateUtils;
 import com.fs.company.domain.CompanyUser;
 import com.fs.his.domain.FsStoreProduct;
@@ -10,6 +11,7 @@ import com.fs.his.mapper.FsStoreProductMapper;
 import com.fs.hisStore.domain.FsStoreProductScrm;
 import com.fs.hisStore.mapper.FsStoreProductScrmMapper;
 import com.fs.live.domain.LiveGoods;
+import com.fs.live.enums.LiveGoodsAddErrorEnum;
 import com.fs.live.mapper.LiveGoodsMapper;
 import com.fs.live.service.ILiveAutoTaskService;
 import com.fs.live.service.ILiveGoodsService;
@@ -91,8 +93,8 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         liveGoods.setUpdateTime(DateUtils.getNowDate());
         LiveGoods existGoods = baseMapper.selectLiveGoodsByGoodsId(liveGoods.getGoodsId());
         if (liveGoods.getStock() != null) {
-            if(liveGoods.getProductId() == null) return R.error("店铺已停止售卖商品!");
-            FsStoreProductScrm fsStoreProduct = fsStoreProductMapper.selectFsStoreProductById(liveGoods.getProductId());
+            if(existGoods.getProductId() == null) return R.error("店铺已停止售卖商品!");
+            FsStoreProductScrm fsStoreProduct = fsStoreProductMapper.selectFsStoreProductById(existGoods.getProductId());
             if(fsStoreProduct == null) return R.error("商品不存在");
             if(fsStoreProduct.getIsShow() == 0 || existGoods.getStatus() == 0) return R.error("商品已下架");
             if(fsStoreProduct.getStock() < liveGoods.getStock()) return R.error("商品库存不足");
@@ -223,6 +225,52 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         //  查询商品信息列表(假设返回 List<StoreProduct>)
         List<FsStoreProductScrm> productInfoList = fsStoreProductMapper.selectFsStoreProductByProductIds(productIdsLong);
 
+        // 检查商品状态:未上架、未审核、已删除
+        StringBuilder errorMsg = new StringBuilder();
+        List<String> notShelvedProducts = new ArrayList<>();
+        List<String> notAuditedProducts = new ArrayList<>();
+        List<String> deletedProducts = new ArrayList<>();
+
+        for (FsStoreProductScrm product : productInfoList) {
+            // 检查是否未上架
+            if (product.getIsShow() == null || product.getIsShow() == 0) {
+                notShelvedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否未审核
+            if (product.getIsAudit() == null || !"1".equals(product.getIsAudit())) {
+                notAuditedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否已删除
+            if (product.getIsDel() != null && product.getIsDel() == 1) {
+                deletedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+        }
+
+        // 构建错误信息
+        if (!notShelvedProducts.isEmpty()) {
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_SHELVED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notShelvedProducts));
+        }
+        if (!notAuditedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_AUDITED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notAuditedProducts));
+        }
+        if (!deletedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.DELETED.getDesc()).append(":");
+            errorMsg.append(String.join("、", deletedProducts));
+        }
+
+        // 如果有错误,返回错误信息
+        if (errorMsg.length() > 0) {
+            return R.error("添加失败原因:" + errorMsg.toString());
+        }
+
         //  转换为 LiveGoods 并批量插入
         List<LiveGoods> liveGoodsList = productInfoList.stream()
                 .map(product -> {
@@ -265,6 +313,52 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         //  查询商品信息列表(假设返回 List<StoreProduct>)
         List<FsStoreProductScrm> productInfoList = fsStoreProductMapper.selectFsStoreProductByProductIds(productIdsLong);
 
+        // 检查商品状态:未上架、未审核、已删除
+        StringBuilder errorMsg = new StringBuilder();
+        List<String> notShelvedProducts = new ArrayList<>();
+        List<String> notAuditedProducts = new ArrayList<>();
+        List<String> deletedProducts = new ArrayList<>();
+
+        for (FsStoreProductScrm product : productInfoList) {
+            // 检查是否未上架
+            if (product.getIsShow() == null || product.getIsShow() == 0) {
+                notShelvedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否未审核
+            if (product.getIsAudit() == null || !"1".equals(product.getIsAudit())) {
+                notAuditedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+            // 检查是否已删除
+            if (product.getIsDel() != null && product.getIsDel() == 1) {
+                deletedProducts.add(product.getProductId() + "(" + product.getProductName() + ")");
+            }
+        }
+
+        // 构建错误信息
+        if (!notShelvedProducts.isEmpty()) {
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_SHELVED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notShelvedProducts));
+        }
+        if (!notAuditedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.NOT_AUDITED.getDesc()).append(":");
+            errorMsg.append(String.join("、", notAuditedProducts));
+        }
+        if (!deletedProducts.isEmpty()) {
+            if (errorMsg.length() > 0) {
+                errorMsg.append(";");
+            }
+            errorMsg.append(LiveGoodsAddErrorEnum.DELETED.getDesc()).append(":");
+            errorMsg.append(String.join("、", deletedProducts));
+        }
+
+        // 如果有错误,抛出异常(因为返回类型是int,不能返回R.error)
+        if (errorMsg.length() > 0) {
+            throw new ServiceException("添加失败原因:" + errorMsg.toString());
+        }
+
         //  转换为 LiveGoods 并批量插入
         List<LiveGoods> liveGoodsList = productInfoList.stream()
                 .map(product -> {

+ 24 - 1
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -1879,8 +1879,12 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         }
         BigDecimal payPrice = BigDecimal.ZERO;
         BigDecimal payDelivery = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if (param.getCityId() != null) {
             payDelivery = handleDeliveryMoney(param.getCityId(), fsStoreProduct, param.getTotalNum());
+            if (payDelivery.compareTo(badCode) == 0) {
+                throw new ServiceException("偏远地区暂不可购买");
+            }
             totalPrice = totalPrice.add(payDelivery);
         }
         return LiveOrderComputeDTO.builder().payPrice(payPrice)
@@ -1948,6 +1952,9 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 //        payPrice = payPrice.add(serviceFee);
         // 生成
         BigDecimal deliveryMoney = handleDeliveryMoney(liveOrder);
+        if (deliveryMoney.compareTo(BigDecimal.valueOf(-1)) == 0) {
+            return R.error("偏远地区暂不可购买");
+        }
         totalPrice = totalPrice.add(deliveryMoney);
         liveOrder.setDiscountMoney(totalPrice);
 
@@ -2028,8 +2035,12 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         totalPrice = totalPrice.add(payPrice);
         BigDecimal payDelivery = BigDecimal.ZERO;
         BigDecimal deductionPrice = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if (param.getCityId() != null) {
             payDelivery = handleDeliveryMoney(param.getCityId(), fsStoreProduct, param.getTotalNum());
+            if (payDelivery.compareTo(badCode) == 0) {
+                throw new ServiceException("偏远地区暂不可购买");
+            }
             payPrice = payPrice.add(payDelivery);
         }
 
@@ -2472,6 +2483,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
     private BigDecimal handleDeliveryMoney(Long cityId, FsStoreProductScrm fsStoreProduct, String totalNumSize) {
         BigDecimal storePostage = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if (ObjectUtil.isNull(fsStoreProduct.getTempId())) {
             return storePostage;
         }
@@ -2487,6 +2499,11 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         String cityIds = String.join(",", citys.stream()
                 .map(String::valueOf).collect(Collectors.toList()));
         List<FsShippingTemplatesRegionScrm> shippingTemplatesRegionList = shippingTemplatesRegionService.selectFsShippingTemplatesRegionListByTempIdsAndCityIds(ids,cityIds);
+        // 有运费模板,但当前城市没有匹配的区域
+        if (shippingTemplatesList != null && !shippingTemplatesList.isEmpty()
+                && (shippingTemplatesRegionList == null || shippingTemplatesRegionList.isEmpty())) {
+            return badCode;
+        }
         Map<Long, Integer> shippingTemplatesMap = shippingTemplatesList
                 .stream()
                 .collect(Collectors.toMap(FsShippingTemplatesScrm::getId,
@@ -2521,7 +2538,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         FsShippingTemplatesRegionScrm shippingTemplatesRegion = shippingTemplatesRegionMap.get(tempId);
         if (shippingTemplatesRegion == null) {
             log.error("没有找到运费模板");
-            return storePostage;
+            return badCode;
         }
         BigDecimal price = NumberUtil.round(NumberUtil.mul(totalNum, fsStoreProduct.getPrice()), 2);
 
@@ -3621,6 +3638,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
     private BigDecimal handleDeliveryMoney(LiveOrder liveOrder) {
         BigDecimal storePostage = BigDecimal.ZERO;
+        BigDecimal badCode = BigDecimal.valueOf(-1);
         if(liveOrder.getUserAddress() == null || liveOrder.getCityId() == null) return storePostage;
         List<Long> citys = new ArrayList<>();
         citys.add(liveOrder.getCityId());
@@ -3633,6 +3651,11 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
 
         //获取运费模板区域列表按照城市排序
         List<FsShippingTemplatesRegionScrm> shippingTemplatesRegionList = shippingTemplatesRegionService.selectFsShippingTemplatesRegionListByTempIdsAndCityIds(ids, StringUtils.join(citys, ","));
+        // 有运费模板但城市未匹配到区域,返回 badCode
+        if (shippingTemplatesList != null && !shippingTemplatesList.isEmpty()
+                && (shippingTemplatesRegionList == null || shippingTemplatesRegionList.isEmpty())) {
+            return badCode;
+        }
 
         //提取运费模板类型
         Map<Long, Integer> shippingTemplatesMap = shippingTemplatesList

+ 195 - 0
fs-service/src/main/java/com/fs/live/vo/MergedOrderVO.java

@@ -0,0 +1,195 @@
+package com.fs.live.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 合并订单VO(销售订单+商城订单+直播订单)
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+public class MergedOrderVO implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 订单ID */
+    private Long id;
+
+    /** 订单ID(直播订单使用) */
+    private Long orderId;
+
+    /** 直播ID(直播订单使用) */
+    private Long liveId;
+
+    /** 售后ID(直播订单使用) */
+    private Long afterSalesId;
+
+    /** 订单号 */
+    private String orderCode;
+
+    /** 实际支付金额 */
+    private BigDecimal payPrice;
+
+    /** 会员等级 */
+    private Integer userLevel;
+
+    /** 实付金额 */
+    private BigDecimal payMoney;
+
+    /** 优惠金额(直播订单使用) */
+    private Integer discountMoney;
+
+    /** 运费 */
+    private BigDecimal payDelivery;
+
+    /** 成本价 */
+    private BigDecimal cost;
+
+    /** 订单状态 */
+    @Excel(name = "订单状态",dictType="sys_live_order_status")
+    private Integer status;
+
+    /** 订单总价 */
+    private BigDecimal totalPrice;
+
+    /** 是否套餐 */
+    private Integer isPackage;
+
+    /** 套餐JSON */
+    private String packageJson;
+
+    /** 商品JSON */
+    private String itemJson;
+
+    /** 物流单号 */
+    private String deliveryId;
+
+    /** 是否可以申请售后 */
+    private Integer isAfterSales;
+
+    /** 完成时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date finishTime;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 支付时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date payTime;
+
+    /** 发货时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date deliverySendTime;
+
+    /** 总数量(直播订单使用) */
+    private Integer totalNum;
+
+
+
+    /** 订单类型:1-销售订单,2-商城订单,3-直播订单 */
+    private Integer orderType;
+
+    /** 订单类型名称 */
+    private String orderTypeName;
+
+    /** 公司名称 */
+    private String companyName;
+
+    /** 销售名称(company_user表的user_name) */
+    private String salesName;
+
+    /** 销售创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date salesCreateTime;
+
+    /** 销售手机 */
+    private String salesPhone;
+
+    /** 员工昵称(company_user表的nick_name) */
+    private String companyUserNickName;
+
+    /** 用户昵称 */
+    private String nickname;
+
+    /** 用户手机号 */
+    private String phone;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** 用户累计成交避暑 */
+    private Integer userOrderCount;
+
+    /** 用户累计成交避暑 */
+    private BigDecimal userTotalAmount;
+
+    /** 收货人姓名 */
+    private String realName;
+
+    /** 收货人电话 */
+    private String userPhone;
+
+    /** 店铺ID */
+    private Long storeId;
+
+    /** 店铺名字 */
+    private String storeName;
+
+    /** 产品ID */
+    private Long productId;
+
+    /** 产品名字 */
+    private String productName;
+    /** 商品规格 */
+    private String productSpec;
+
+    /** 商品编码 */
+    private String barCode;
+
+    /** 商品分类 */
+    private String cateName;
+
+
+
+    /** 收货地址 */
+    private String userAddress;
+
+    /** 支付方式 */
+    private String payType;
+
+    /** 物流状态 */
+    private Integer deliveryStatus;
+
+    /** 物流结算状态 */
+    private Integer deliveryPayStatus;
+
+
+
+    /** 订单商品列表 */
+    private List<?> items;
+
+    /** 小程序名称 */
+    private String miniProgramName;
+
+    /** ERP账号 */
+    private String erpAccount;
+
+    /** ERP电话 */
+    private String erpPhone;
+
+    /** 银行交易流水号 */
+    private String bankTransactionId;
+
+
+}
+

+ 403 - 0
fs-service/src/main/resources/mapper/hisStore/MergedOrderMapper.xml

@@ -0,0 +1,403 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.hisStore.mapper.MergedOrderMapper">
+
+    <select id="selectMergedOrderList" parameterType="com.fs.live.param.MergedOrderQueryParam" resultType="com.fs.live.vo.MergedOrderVO">
+        SELECT * FROM (
+      SELECT
+      o.id,
+      '销售订单' AS order_type_name,
+      NULL AS order_id,
+      NULL AS live_id,
+      NULL AS after_sales_id,
+      o.order_code,
+      o.pay_price,
+      o.pay_money,
+      o.STATUS,
+      o.is_package,
+      o.package_json,
+      o.item_json,
+      o.delivery_id,
+      o.finish_time,
+      o.create_time,
+      o.pay_time,
+      o.delivery_send_time,
+      NULL AS total_num,
+      NULL AS discount_money,
+      1 AS order_type,
+
+        cu.phonenumber as salesPhone,
+        cu.create_time as salesCreateTime,
+        u.user_id as userId,
+        u.order_count as userOrderCount,
+        u.total_amount as userTotalAmount,
+        u.level as userLevel,
+        fspc.product_id as productId,
+        fspc.product_name as productName,
+        fspc.cost as cost,
+        o.pay_postage as payDelivery,
+        o.coupon_price as discountMoney,
+        fspc.prescribe_spec as productSpec,
+        fss.store_id as storeId,
+        fss.store_name as storeName,
+        fspcs.cate_name as cateName,
+        GROUP_CONCAT(JSON_UNQUOTE(JSON_EXTRACT(o.item_json, '$.barCode')) SEPARATOR ',') AS barCode,
+
+      c.company_name,
+      cu.user_name AS sales_name,
+      cu.nick_name AS company_user_nick_name,
+      u.nickname,
+      u.phone,
+      o.real_name,
+      o.user_phone,
+      o.user_address,
+      o.pay_type,
+      o.delivery_status,
+      o.delivery_pay_status,
+      o.total_price,
+      csc.NAME AS mini_program_name,
+      sp_latest.bank_transaction_id
+      FROM
+      fs_store_order_scrm o
+      left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.item_id  ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id and item_latest.rn = 1
+      LEFT JOIN fs_user u ON o.user_id = u.user_id
+
+      LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = item_latest.product_id
+      LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+      left join fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+
+      LEFT JOIN company c ON c.company_id = o.company_id
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_code ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_code = o.order_code
+      AND sp_latest.rn = 1
+      LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+          WHERE o.is_del = 0 AND o.is_sys_del = 0 AND o.company_user_id IS NOT NULL AND o.company_user_id != 0
+          <if test="maps.status != null and maps.status != ''">
+            AND o.status = #{maps.status}
+          </if>
+          <if test="maps.orderCode != null and maps.orderCode != ''">
+            AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+          </if>
+          <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+            AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+          </if>
+          <if test="maps.deliveryId != null and maps.deliveryId != ''">
+            AND o.delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
+          </if>
+          <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
+            AND o.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+          </if>
+          <if test="maps.userPhone != null and maps.userPhone != ''">
+            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+          </if>
+          <if test="maps.realName != null and maps.realName != ''">
+            AND o.real_name LIKE CONCAT('%', #{maps.realName}, '%')
+          </if>
+          <if test="maps.productName != null and maps.productName != ''">
+            AND fspc.productName LIKE CONCAT('%', #{maps.productName}, '%')
+          </if>
+          <if test="maps.deliveryStatus != null">
+            AND o.delivery_status = #{maps.deliveryStatus}
+          </if>
+          <if test="maps.deliveryPayStatus != null">
+            AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+          </if>
+          <if test="maps.payType != null and maps.payType != ''">
+            AND o.pay_type = #{maps.payType}
+          </if>
+          <if test="maps.companyId != null">
+            AND o.company_id = #{maps.companyId}
+          </if>
+          <if test="maps.deptId != null">
+            AND o.dept_id = #{maps.deptId}
+          </if>
+          <if test="maps.salesName != null and maps.salesName != ''">
+            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+          </if>
+          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+          </if>
+          <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+            AND DATE(o.create_time) BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+          </if>
+          <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+            AND DATE(o.pay_time) BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+            AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
+            AND DATE(o.delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+          </if>
+          <if test="maps.appId != null and maps.appId != ''">
+            AND csc.appid = #{maps.appId}
+          </if>
+          group by o.id
+          UNION ALL
+          -- 商城订单(没有company_user_id的商城订单)
+      SELECT
+      o.id,
+      '商城订单' AS order_type_name,
+      NULL AS order_id,
+      NULL AS live_id,
+      NULL AS after_sales_id,
+      o.order_code,
+      o.pay_price,
+      o.pay_money,
+      o.STATUS,
+      o.is_package,
+      o.package_json,
+      o.item_json,
+      o.delivery_id,
+      o.finish_time,
+      o.create_time,
+      o.pay_time,
+      o.delivery_send_time,
+      NULL AS total_num,
+      NULL AS discount_money,
+      2 AS order_type,
+
+    cu.phonenumber as salesPhone,
+    cu.create_time as salesCreateTime,
+    u.user_id as userId,
+    u.order_count as userOrderCount,
+    u.total_amount as userTotalAmount,
+    u.level as userLevel,
+    fspc.product_id as productId,
+    fspc.product_name as productName,
+    fspc.prescribe_spec as productSpec,
+        fspc.cost as cost,
+        o.pay_postage as payDelivery,
+        o.coupon_price as discountMoney,
+    fss.store_id as storeId,
+    fss.store_name as storeName,
+    fspcs.cate_name as cateName,
+    GROUP_CONCAT(JSON_UNQUOTE(JSON_EXTRACT(o.item_json, '$.barCode')) SEPARATOR ',') AS barCode,
+
+      c.company_name,
+      cu.user_name AS sales_name,
+      cu.nick_name AS company_user_nick_name,
+      u.nickname,
+      u.phone,
+      o.real_name,
+      o.user_phone,
+      o.user_address,
+      o.pay_type,
+      o.delivery_status,
+      o.delivery_pay_status,
+      o.total_price,
+      csc.NAME AS mini_program_name,
+      sp_latest.bank_transaction_id
+      FROM
+      fs_store_order_scrm o
+        left join ( SELECT fsois.*, ROW_NUMBER() OVER ( PARTITION BY fsois.item_id  ) AS rn FROM fs_store_order_item_scrm fsois ) item_latest ON item_latest.order_id = o.id
+      LEFT JOIN fs_user u ON o.user_id = u.user_id
+
+        LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = item_latest.product_id
+        LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+        left join fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+
+      LEFT JOIN company c ON c.company_id = o.company_id
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_code ORDER BY sp.create_time DESC ) AS rn FROM fs_store_payment_scrm sp ) sp_latest ON sp_latest.business_code = o.order_code
+      AND sp_latest.rn = 1
+      LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+          WHERE o.is_del = 0 AND o.is_sys_del = 0 AND (o.company_user_id IS NULL OR o.company_user_id = 0)
+          <if test="maps.status != null and maps.status != ''">
+            AND o.status = #{maps.status}
+          </if>
+          <if test="maps.orderCode != null and maps.orderCode != ''">
+            AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+          </if>
+          <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+            AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+          </if>
+          <if test="maps.deliveryId != null and maps.deliveryId != ''">
+            AND o.delivery_id LIKE CONCAT('%', #{maps.deliveryId}, '%')
+          </if>
+          <if test="maps.bankTransactionId != null and maps.bankTransactionId != ''">
+            AND o.bank_transaction_id LIKE CONCAT('%', #{maps.bankTransactionId}, '%')
+          </if>
+          <if test="maps.userPhone != null and maps.userPhone != ''">
+            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+          </if>
+          <if test="maps.realName != null and maps.realName != ''">
+            AND o.real_name LIKE CONCAT('%', #{maps.realName}, '%')
+          </if>
+          <if test="maps.productName != null and maps.productName != ''">
+            AND o.item_json LIKE CONCAT('%', #{maps.productName}, '%')
+          </if>
+          <if test="maps.deliveryStatus != null">
+            AND o.delivery_status = #{maps.deliveryStatus}
+          </if>
+          <if test="maps.deliveryPayStatus != null">
+            AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+          </if>
+          <if test="maps.payType != null and maps.payType != ''">
+            AND o.pay_type = #{maps.payType}
+          </if>
+          <if test="maps.companyId != null">
+            AND o.company_id = #{maps.companyId}
+          </if>
+          <if test="maps.deptId != null">
+            AND o.dept_id = #{maps.deptId}
+          </if>
+          <if test="maps.salesName != null and maps.salesName != ''">
+            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+          </if>
+          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+          </if>
+          <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+            AND DATE(o.create_time) BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+          </if>
+          <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+            AND DATE(o.pay_time) BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+            AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliveryImportTimeRange != null and maps.deliveryImportTimeRange != ''">
+            AND DATE(o.delivery_import_time) BETWEEN SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliveryImportTimeRange}, '--', -1)
+          </if>
+          <if test="maps.appId != null and maps.appId != ''">
+            AND csc.appid = #{maps.appId}
+          </if>
+        group by o.id
+          UNION ALL
+          -- 直播订单
+      SELECT NULL AS
+      id,
+      '直播订单' AS order_type_name,
+      o.order_id,
+      o.live_id,
+      a.id AS after_sales_id,
+      o.order_code,
+      o.pay_price,
+      o.pay_money,
+      o.STATUS,
+      NULL AS is_package,
+      NULL AS package_json,
+      o.item_json,
+      o.delivery_sn AS delivery_id,
+      o.finish_time,
+      o.create_time,
+      o.pay_time,
+      o.delivery_send_time,
+      o.total_num,
+      o.discount_money,
+      3 AS order_type,
+
+        cu.phonenumber as salesPhone,
+        cu.create_time as salesCreateTime,
+        u.user_id as userId,
+        u.order_count as userOrderCount,
+        u.total_amount as userTotalAmount,
+        u.level as userLevel,
+        fspc.product_id as productId,
+        fspc.product_name as productName,
+        fspc.prescribe_spec as productSpec,
+        fspc.cost as cost,
+        o.pay_delivery as payDelivery,
+        o.discount_money as discountMoney,
+        fss.store_id as storeId,
+        fss.store_name as storeName,
+        fspcs.cate_name as cateName,
+        GROUP_CONCAT(JSON_UNQUOTE(JSON_EXTRACT(loi.json_info, '$.barCode')) SEPARATOR ',') AS barCode,
+
+      c.company_name,
+      cu.user_name AS sales_name,
+      cu.nick_name AS company_user_nick_name,
+      u.nickname,
+      u.phone,
+      o.user_name AS real_name,
+      o.user_phone,
+      o.user_address,
+      o.pay_type,
+      o.delivery_status,
+      o.delivery_pay_status,
+      o.total_price,
+      csc.NAME AS mini_program_name,
+      sp_latest.bank_transaction_id
+      FROM
+      live_order o
+      left join live_order_item loi on loi.order_id = o.order_id
+      LEFT JOIN fs_user u ON o.user_id = u.user_id
+
+        LEFT JOIN fs_store_product_scrm  fspc ON fspc.product_id = o.product_id
+        LEFT JOIN fs_store_scrm  fss ON fspc.store_id = fss.store_id
+        left join fs_store_product_category_scrm fspcs on fspc.cate_id = fspcs.cate_id
+
+
+      LEFT JOIN company c ON c.company_id = o.company_id
+      LEFT JOIN company_user cu ON cu.user_id = o.company_user_id
+      LEFT JOIN ( SELECT t.*, ROW_NUMBER() OVER ( PARTITION BY t.order_id ORDER BY t.create_time DESC ) AS rn FROM live_after_sales t ) a ON o.order_id = a.order_id
+      AND a.rn = 1
+      LEFT JOIN ( SELECT sp.*, ROW_NUMBER() OVER ( PARTITION BY sp.business_code ORDER BY sp.create_time DESC ) AS rn FROM live_order_payment sp ) sp_latest ON sp_latest.business_code = o.order_code
+      AND sp_latest.rn = 1
+      LEFT JOIN fs_course_play_source_config csc ON csc.appid = sp_latest.app_id
+          WHERE o.is_del = 0
+          <if test="maps.status != null and maps.status != ''">
+            AND o.status = #{maps.status}
+          </if>
+          <if test="maps.orderCode != null and maps.orderCode != ''">
+            AND o.order_code LIKE CONCAT('%', #{maps.orderCode}, '%')
+          </if>
+          <if test="maps.orderCodeList != null and maps.orderCodeList != ''">
+            AND FIND_IN_SET(o.order_code, #{maps.orderCodeList})
+          </if>
+          <if test="maps.deliveryId != null and maps.deliveryId != ''">
+            AND o.delivery_sn LIKE CONCAT('%', #{maps.deliveryId}, '%')
+          </if>
+          <if test="maps.userPhone != null and maps.userPhone != ''">
+            AND o.user_phone LIKE CONCAT('%', #{maps.userPhone}, '%')
+          </if>
+          <if test="maps.realName != null and maps.realName != ''">
+            AND o.user_name LIKE CONCAT('%', #{maps.realName}, '%')
+          </if>
+          <if test="maps.productName != null and maps.productName != ''">
+            AND o.item_json LIKE CONCAT('%', #{maps.productName}, '%')
+          </if>
+          <if test="maps.deliveryStatus != null">
+            AND o.delivery_status = #{maps.deliveryStatus}
+          </if>
+          <if test="maps.deliveryPayStatus != null">
+            AND o.delivery_pay_status = #{maps.deliveryPayStatus}
+          </if>
+          <if test="maps.payType != null and maps.payType != ''">
+            AND o.pay_type = #{maps.payType}
+          </if>
+          <if test="maps.companyId != null">
+            AND o.company_id = #{maps.companyId}
+          </if>
+          <if test="maps.deptId != null">
+            AND o.dept_id = #{maps.deptId}
+          </if>
+          <if test="maps.salesName != null and maps.salesName != ''">
+            AND cu.user_name LIKE CONCAT('%', #{maps.salesName}, '%')
+          </if>
+          <if test="maps.companyUserNickName != null and maps.companyUserNickName != ''">
+            AND cu.nick_name LIKE CONCAT('%', #{maps.companyUserNickName}, '%')
+          </if>
+          <if test="maps.createTimeRange != null and maps.createTimeRange != ''">
+            AND DATE(o.create_time) BETWEEN SUBSTRING_INDEX(#{maps.createTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.createTimeRange}, '--', -1)
+          </if>
+          <if test="maps.payTimeRange != null and maps.payTimeRange != ''">
+            AND DATE(o.pay_time) BETWEEN SUBSTRING_INDEX(#{maps.payTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.payTimeRange}, '--', -1)
+          </if>
+          <if test="maps.deliverySendTimeRange != null and maps.deliverySendTimeRange != ''">
+            AND DATE(o.delivery_send_time) BETWEEN SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', 1) AND SUBSTRING_INDEX(#{maps.deliverySendTimeRange}, '--', -1)
+          </if>
+        group by o.order_id
+        ) AS merged_orders
+        WHERE 1=1
+        <if test="maps.orderTypeFilter != null">
+          AND order_type = #{maps.orderTypeFilter}
+        </if>
+        ORDER BY create_time DESC
+    </select>
+
+</mapper>
+

+ 3 - 3
fs-service/src/main/resources/mapper/live/LiveDataMapper.xml

@@ -283,7 +283,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <!-- 查询直播间统计数据 -->
     <select id="selectLiveDataStatistics" resultType="com.fs.live.vo.LiveDataStatisticsVo">
         SELECT
-            COUNT( lwu.user_id) AS totalViewers,
+            COUNT( distinct lwu.user_id) AS totalViewers,
             COUNT( CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.user_id END) AS liveViewers,
             COUNT( CASE WHEN lwu.live_flag = 0 and lwu.replay_flag = 1 THEN lwu.user_id END) AS playbackViewers,
             COALESCE(AVG(CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.online_seconds END), 0) AS liveAvgDuration,
@@ -360,7 +360,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             l.status AS status,
             l.start_time AS startTime,
             l.finish_time AS finishTime,
-            COUNT(1) AS totalViewers,
+            COUNT(distinct lwu.user_id) AS totalViewers,
             COUNT(CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.user_id END) AS liveViewers,
             COUNT(CASE WHEN lwu.live_flag = 0 and lwu.replay_flag = 1 THEN lwu.user_id END) AS playbackViewers,
             COALESCE(AVG(CASE WHEN lwu.live_flag = 1 and lwu.replay_flag = 0 THEN lwu.online_seconds END), 0) AS liveAvgDuration,
@@ -420,7 +420,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             CASE
                 WHEN COUNT(DISTINCT lwu.user_id) > 0 THEN
                     ROUND(COUNT(DISTINCT CASE
-                        WHEN COALESCE(user_duration.total_duration, 0) >= 1800
+                        WHEN COALESCE(user_duration.total_duration, 0) >= 1200
                         THEN lwu.user_id
                     END) * 100.0 / COUNT(DISTINCT lwu.user_id), 2)
                 ELSE 0