Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/main'

caotao 2 mēneši atpakaļ
vecāks
revīzija
0cc2df144c
12 mainītis faili ar 427 papildinājumiem un 194 dzēšanām
  1. 1 0
      ship-module-trade/ship-module-trade-api/src/main/java/com/yc/ship/module/trade/enums/TradeOrderOperateTypeEnum.java
  2. 4 4
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/report/vo/VisitorSourceDashboardReqVO.java
  3. 2 2
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/mysql/report/VoyageStockBoardMapper.java
  4. 3 3
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/otc/impl/OtcTradeOrderServiceImpl.java
  5. 7 4
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/pay/impl/TradeOrderPayServiceImpl.java
  6. 95 62
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/refund/impl/TradeRefundServiceImpl.java
  7. 26 10
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/report/impl/VoyageStockBoardServiceImpl.java
  8. 239 87
      ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/utils/IdCardProvinceUtil.java
  9. 5 5
      ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/orderjzdetail/OrderGiftVisitorMapper.xml
  10. 18 12
      ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/orderjzdetail/OrderJzDetailMapper.xml
  11. 6 0
      ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/report/VoyageStockBoardMapper.xml
  12. 21 5
      ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/report/YangtzePassengerSummaryMapper.xml

+ 1 - 0
ship-module-trade/ship-module-trade-api/src/main/java/com/yc/ship/module/trade/enums/TradeOrderOperateTypeEnum.java

@@ -48,6 +48,7 @@ public enum TradeOrderOperateTypeEnum {
     ORDER_BC_PAY(33, "订单补差价支付,支付方式:{payType},金额:{payAmount}"),
     ORDER_AUDIT_LOG(34, "订单进入审核"),
     ORDER_REMARK_UPDATE(35, "修改订单备注,修改记录:{content},结果:{result}"),
+    ORDER_CANCEL_REFUND(36, "取消订单退款,修改记录:{content},结果:{result}"),
     ;
 
     /**

+ 4 - 4
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/report/vo/VisitorSourceDashboardReqVO.java

@@ -3,7 +3,7 @@ package com.yc.ship.module.trade.controller.admin.report.vo;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
-import javax.validation.constraints.NotNull;
+import javax.validation.constraints.NotEmpty;
 import java.util.List;
 
 /**
@@ -13,9 +13,9 @@ import java.util.List;
 @Data
 public class VisitorSourceDashboardReqVO {
 
-    @Schema(description = "游轮ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
-    @NotNull(message = "游轮ID不能为空")
-    private Long shipId;
+    @Schema(description = "游轮ID列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
+    @NotEmpty(message = "游轮ID列表不能为空")
+    private List<Long> shipIds;
 
     @Schema(description = "航次ID列表", example = "[1,2,3]")
     private List<Long> voyageIds;

+ 2 - 2
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/mysql/report/VoyageStockBoardMapper.java

@@ -48,7 +48,7 @@ public interface VoyageStockBoardMapper {
      * @param voyageIds 航次ID列表
      * @return 游客列表(含证件类型、证件号、国籍及国籍名称)
      */
-    List<TradeVisitorDO> selectVisitorListForSourceDashboard(@Param("shipId") Long shipId, @Param("voyageIds") List<Long> voyageIds);
+    List<TradeVisitorDO> selectVisitorListForSourceDashboard(@Param("shipId") Long shipId, @Param("voyageIds") List<Long> voyageIds, @Param("distributorId") Long distributorId);
 
     /**
      * 查询游客年龄段分析看板所需的游客年龄列表
@@ -57,7 +57,7 @@ public interface VoyageStockBoardMapper {
      * @param voyageIds 航次ID列表
      * @return 游客年龄列表
      */
-    List<Integer> selectVisitorAgeListForAgeDashboard(@Param("shipId") Long shipId, @Param("voyageIds") List<Long> voyageIds);
+    List<Integer> selectVisitorAgeListForAgeDashboard(@Param("shipId") Long shipId, @Param("voyageIds") List<Long> voyageIds, @Param("distributorId") Long distributorId);
 
     /**
      * 查询所有航次库存分页

+ 3 - 3
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/otc/impl/OtcTradeOrderServiceImpl.java

@@ -1308,7 +1308,7 @@ public class OtcTradeOrderServiceImpl implements OtcTradeOrderService {
                 extMap.put("result", result);
                 tradeRefundService.refundByOrderId(orderId, supplementAmount);
 
-                TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), TradeOrderStatusEnum.UNPAID.getStatus(), extMap);
+                TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), TradeOrderStatusEnum.CANCELED.getStatus(), extMap);
             } else {
                 AuditUserDO auditUserDO = auditUserMapper.selectOne(new QueryWrapper<AuditUserDO>().eq("type", tradeOrderDO.getAuditType()).eq("audit_status", tradeOrderDO.getAuditStatus() + 1).last("limit 1"));
                 tradeOrderMapper.update(Wrappers.<TradeOrderDO>lambdaUpdate()
@@ -1344,10 +1344,10 @@ public class OtcTradeOrderServiceImpl implements OtcTradeOrderService {
 
             int oldorderStatus =  TradeOrderStatusEnum.YD.getStatus();
             if(tradeOrderDO.getConfirmDate()!=null){
-                oldorderStatus = TradeOrderStatusEnum.UNPAID.getStatus();
+                oldorderStatus = TradeOrderStatusEnum.CANCELED.getStatus();
             }
             if(tradeOrderDO.getPayStatus()!=null && tradeOrderDO.getPayStatus()==1){
-                oldorderStatus = TradeOrderStatusEnum.UNUSED.getStatus();
+                oldorderStatus = TradeOrderStatusEnum.CANCELED.getStatus();
             }
             tradeOrderMapper.update(new UpdateWrapper<TradeOrderDO>().set("order_status",oldorderStatus).set("audit_status", 5).set("damaged_status",3).eq("id", orderId));
 

+ 7 - 4
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/pay/impl/TradeOrderPayServiceImpl.java

@@ -33,10 +33,7 @@ import com.yc.ship.module.trade.controller.admin.order.vo.order.PayOrderReqVO;
 import com.yc.ship.module.trade.controller.admin.order.vo.order.PayOrderRespVO;
 import com.yc.ship.module.trade.dal.dataobject.order.*;
 import com.yc.ship.module.trade.dal.dataobject.supplier.TradeSupplierOrderDO;
-import com.yc.ship.module.trade.dal.mysql.order.TradeOrderLogMapper;
-import com.yc.ship.module.trade.dal.mysql.order.TradeOrderMapper;
-import com.yc.ship.module.trade.dal.mysql.order.TradeOrderTotalMapper;
-import com.yc.ship.module.trade.dal.mysql.order.TradeVisitorMapper;
+import com.yc.ship.module.trade.dal.mysql.order.*;
 import com.yc.ship.module.trade.enums.*;
 import com.yc.ship.module.trade.framework.annotation.TradeOrderLog;
 import com.yc.ship.module.trade.framework.aop.TradeOrderLogAspect;
@@ -125,6 +122,8 @@ public class TradeOrderPayServiceImpl implements TradeOrderPayService {
 
     @Resource
     private TradeOrderMapper tradeOrderMapper;
+    @Resource
+    private TradeOrderPayMapper tradeOrderPayMapper;
 
     @Resource
     private TradeOrderLogMapper tradeOrderLogMapper;
@@ -845,6 +844,10 @@ public class TradeOrderPayServiceImpl implements TradeOrderPayService {
                 .set(TradeOrderDO::getOrderStatus, orderStatus)
                 .set(TradeOrderDO::getDeposiStatus, 2)
                 .set(TradeOrderDO::getDeposiPayTime, LocalDateTime.now()));
+
+        tradeOrderPayMapper.update(new LambdaUpdateWrapper<TradeOrderPayDO>()
+                .eq(TradeOrderPayDO::getId, tradeOrderPayDO.getId())
+                .set(TradeOrderPayDO::getPayStatus, PaymentStatusEnum.PAID.getStatus()));
         //保存订单日志
         TradeOrderLogUtils.setOrderInfo(tradeOrderDO.getId(), orderStatus, tradeOrderDO.getOrderStatus(), MapUtil.<String, Object>builder().put("payType", PayTypeEnum.valueOf(tradeOrderPayDO.getPaymentType()).getName()).put("payAmount", tradeOrderPayDO.getPayAmount()).build());
     }

+ 95 - 62
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/refund/impl/TradeRefundServiceImpl.java

@@ -35,6 +35,7 @@ import com.yc.ship.module.trade.dal.dataobject.bill.BillStoreDO;
 import com.yc.ship.module.trade.dal.dataobject.order.*;
 import com.yc.ship.module.trade.dal.dataobject.refund.*;
 import com.yc.ship.module.trade.dal.dataobject.supplier.SupplierRefundDO;
+import com.yc.ship.module.trade.dal.mysql.order.TradeOrderLogMapper;
 import com.yc.ship.module.trade.dal.mysql.order.TradeOrderMapper;
 import com.yc.ship.module.trade.dal.mysql.order.TradeOrderTotalMapper;
 import com.yc.ship.module.trade.dal.mysql.order.TradeVisitorMapper;
@@ -102,6 +103,8 @@ public class TradeRefundServiceImpl implements TradeRefundService {
     @Resource
     private TradeRefundRepositoryService tradeRefundRepositoryService;
     @Resource
+    private TradeOrderLogMapper tradeOrderLogMapper;
+    @Resource
     private Map<String, PayTypeService> payTypeServiceMap;
     @Resource
     private PayPlatService payPlatService;
@@ -380,71 +383,97 @@ public class TradeRefundServiceImpl implements TradeRefundService {
         try {
             //如果失败更新退款记录状态为退款失败,成功状态由通知变更
             List<TradeOrderPayDO> tradeOrderPayList = tradeOrderRepositoryService.getPayOrderByOrderId(tradeOrderDO.getId());
-            for (TradeOrderPayDO payOrderDO : tradeOrderPayList) {
-                if(totalAmount.compareTo(BigDecimal.ZERO) <= 0){
-                    break;
-                }
-                RefundDO refundDO = new RefundDO();
-                if(payOrderDO.getPayAmount().compareTo(totalAmount) >= 0){
-                    refundDO.setRefundAmount(amount);
-                    refundDO.setOriginAmount(amount);
-                }else{
-                    refundDO.setRefundAmount(payOrderDO.getPayAmount());
-                    refundDO.setOriginAmount(payOrderDO.getPayAmount());
-                    //还剩多少
-                    totalAmount = totalAmount.subtract(payOrderDO.getPayAmount());
-                }
-                refundDO.setOrderId(orderId);
-                refundDO.setRefundChannel(6);
-                refundDO.setId(IdWorker.getId());
-                refundDO.setSellMethod(tradeOrderDO.getSellMethod());
-                refundDO.setRefundStatus(RefundStatusEnum.REFUNDING.getStatus());
-                refundDO.setRefundMethod(3);
-                refundDO.setRefundBackType(0);
-                refundDO.setRefundReason("取消订单退押金");
-                refundDO.setRefundUser(tradeOrderDO.getSellerId());
-                refundDO.setFee(BigDecimal.ZERO);
-                refundDO.setOrderStatus(tradeOrderDO.getOrderStatus());
-                refundDO.setTenantId(1L);
-                refundDO.setCreator(getLoginUserId()+"");
-                refundDO.setCreateTime(LocalDateTime.now());
-                refundDO.setDeleted(false);
-                refundMapper.insert(refundDO);
-
-                RefundRecordDO refundRecordDO = new  RefundRecordDO();
-                refundRecordDO.setOrderPayId(payOrderDO.getId());
-                refundRecordDO.setRefundStatus(RefundStatusEnum.REFUNDING.getStatus());
-                refundRecordDO.setMerchantNo(payOrderDO.getMerchantNo());
-                refundRecordDO.setChannelId(payOrderDO.getChannelId());
-                refundRecordDO.setPaymentType(payOrderDO.getPaymentType());
-                refundRecordDO.setRefundId(refundDO.getId());
-                refundRecordDO.setRefundAmount(refundDO.getRefundAmount());
-                refundRecordDO.setTenantId(payOrderDO.getTenantId());
-                refundRecordDO.setPaymentNo(payOrderDO.getPaymentNo());
-                refundRecordDO.setRealPayOrderId(payOrderDO.getId());
-                refundRecordDO.setRefundBackType(0);
-                tradeRefundRepositoryService.saveRefundRecord(refundRecordDO);
-                if(payOrderDO.getPaymentType() == 1 || payOrderDO.getPaymentType() == 2) {
-                    TradeMchInfoDO tradeMchInfoDO = tradeOrderRepositoryService.getByMchId(refundRecordDO.getMerchantNo());
-                    Map result = payPlatService.createRefundOrder(refundRecordDO, refundDO, payOrderDO, tradeMchInfoDO);
-                    Asserts.isTrue(StrUtil.equals(SUCCESS, MapUtils.getString(result, "retCode")), "调用支付平台退款失败");
-                    String refundOrderId = MapUtil.getStr(result, "refundOrderId");
-                    if (StrUtil.isNotBlank(refundOrderId)) {
-                        refundRecordDO.setRefundNo(refundOrderId);
+            log.error("refundByOrderId开始退款"+orderId+";"+(tradeOrderPayList!=null?tradeOrderPayList.size():0));
+            if (tradeOrderPayList != null) {
+                for (TradeOrderPayDO payOrderDO : tradeOrderPayList) {
+                    if(totalAmount.compareTo(BigDecimal.ZERO) <= 0){
+                        break;
+                    }
+                    RefundDO refundDO = new RefundDO();
+                    if(payOrderDO.getPayAmount().compareTo(totalAmount) >= 0){
+                        refundDO.setRefundAmount(amount);
+                        refundDO.setOriginAmount(amount);
+                    }else{
+                        refundDO.setRefundAmount(payOrderDO.getPayAmount());
+                        refundDO.setOriginAmount(payOrderDO.getPayAmount());
+                        //还剩多少
+                        totalAmount = totalAmount.subtract(payOrderDO.getPayAmount());
+                    }
+                    refundDO.setOrderId(orderId);
+                    refundDO.setRefundChannel(6);
+                    refundDO.setId(IdWorker.getId(refundDO));
+                    refundDO.setSellMethod(tradeOrderDO.getSellMethod());
+                    refundDO.setRefundStatus(RefundStatusEnum.REFUNDING.getStatus());
+                    refundDO.setRefundMethod(3);
+                    refundDO.setRefundBackType(0);
+                    refundDO.setRefundReason("取消订单退押金");
+                    refundDO.setRefundUser(tradeOrderDO.getSellerId());
+                    refundDO.setFee(BigDecimal.ZERO);
+                    refundDO.setOrderStatus(tradeOrderDO.getOrderStatus());
+                    refundDO.setTenantId(1L);
+                    refundDO.setCreator(getLoginUserId()+"");
+                    refundDO.setCreateTime(LocalDateTime.now());
+                    refundDO.setDeleted(false);
+                    refundMapper.insert(refundDO);
+
+                    RefundRecordDO refundRecordDO = new  RefundRecordDO();
+                    refundRecordDO.setId(IdWorker.getId(refundRecordDO));
+                    refundRecordDO.setOrderPayId(payOrderDO.getId());
+                    refundRecordDO.setRefundStatus(RefundStatusEnum.REFUNDING.getStatus());
+                    refundRecordDO.setMerchantNo(payOrderDO.getMerchantNo());
+                    refundRecordDO.setChannelId(payOrderDO.getChannelId());
+                    refundRecordDO.setPaymentType(payOrderDO.getPaymentType());
+                    refundRecordDO.setRefundId(refundDO.getId());
+                    refundRecordDO.setRefundAmount(refundDO.getRefundAmount());
+                    refundRecordDO.setTenantId(payOrderDO.getTenantId());
+                    refundRecordDO.setPaymentNo(payOrderDO.getPaymentNo());
+                    refundRecordDO.setRealPayOrderId(payOrderDO.getId());
+                    refundRecordDO.setRefundBackType(0);
+                    tradeRefundRepositoryService.saveRefundRecord(refundRecordDO);
+                    if(payOrderDO.getPaymentType() == 1 || payOrderDO.getPaymentType() == 2) {
+                        log.error(tradeOrderDO.getOrderNo()+ "定金退款开始调用支付平台退款"+refundDO.getRefundAmount());
+                        TradeMchInfoDO tradeMchInfoDO = tradeOrderRepositoryService.getByMchId(refundRecordDO.getMerchantNo());
+                        Map result = payPlatService.createRefundOrder(refundRecordDO, refundDO, payOrderDO, tradeMchInfoDO);
+                        Asserts.isTrue(StrUtil.equals(SUCCESS, MapUtils.getString(result, "retCode")), "调用支付平台退款失败");
+                        String refundOrderId = MapUtil.getStr(result, "refundOrderId");
+                        if (StrUtil.isNotBlank(refundOrderId)) {
+                            refundRecordDO.setRefundNo(refundOrderId);
+                            refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
+                            tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
+                        }
+                        TradeOrderLogDO tradeOrderLogDO = new TradeOrderLogDO();
+                        tradeOrderLogDO.setId(IdWorker.getId());
+                        tradeOrderLogDO.setOrderId(tradeOrderDO.getId());
+                        tradeOrderLogDO.setUserId(getLoginUserId());
+                        tradeOrderLogDO.setUserType(2);
+                        tradeOrderLogDO.setOperateType(TradeOrderOperateTypeEnum.ORDER_CANCEL_REFUND.getType());
+                        tradeOrderLogDO.setBeforeStatus(tradeOrderDO.getOrderStatus());
+                        tradeOrderLogDO.setAfterStatus(TradeOrderStatusEnum.CANCELED.getStatus());
+                        tradeOrderLogDO.setContent("取消订单退款,金额"+refundDO.getRefundAmount());
+                        tradeOrderLogMapper.insert(tradeOrderLogDO);
+                    }else{
+                        log.error(tradeOrderDO.getOrderNo()+ "定金退款开始调用余额授信退款"+refundDO.getRefundAmount());
+                        PayTypeEnum payType = PayTypeEnum.valueOf(refundRecordDO.getPaymentType());
+                        if (StrUtil.isNotBlank(payType.getServiceName()) && refundRecordDO.getRefundBackType() == REFUND_BACK_TYPE_ORIGINAL && (refundRecordDO.getRefundAmount().compareTo(BigDecimal.ZERO) > 0)) {
+                            PayTypeService payTypeService = payTypeServiceMap.get(payType.getServiceName());
+                            Long tradeId = payTypeService.refund(refundDO, refundRecordDO);
+                            //更新退款记录(退款成功)
+                            refundRecordDO.setRefundNo(tradeId.toString());
+                        }
+                        refundRecordDO.setRefundTime(LocalDateTime.now());
                         refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
                         tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
+                        TradeOrderLogDO tradeOrderLogDO = new TradeOrderLogDO();
+                        tradeOrderLogDO.setId(IdWorker.getId());
+                        tradeOrderLogDO.setOrderId(tradeOrderDO.getId());
+                        tradeOrderLogDO.setUserId(getLoginUserId());
+                        tradeOrderLogDO.setUserType(2);
+                        tradeOrderLogDO.setOperateType(TradeOrderOperateTypeEnum.ORDER_CANCEL_REFUND.getType());
+                        tradeOrderLogDO.setBeforeStatus(tradeOrderDO.getOrderStatus());
+                        tradeOrderLogDO.setAfterStatus(TradeOrderStatusEnum.CANCELED.getStatus());
+                        tradeOrderLogDO.setContent("取消订单退款,金额"+refundDO.getRefundAmount());
+                        tradeOrderLogMapper.insert(tradeOrderLogDO);
                     }
-                }else{
-                    PayTypeEnum payType = PayTypeEnum.valueOf(refundRecordDO.getPaymentType());
-                    if (StrUtil.isNotBlank(payType.getServiceName()) && refundRecordDO.getRefundBackType() == REFUND_BACK_TYPE_ORIGINAL && (refundRecordDO.getRefundAmount().compareTo(BigDecimal.ZERO) > 0)) {
-                        PayTypeService payTypeService = payTypeServiceMap.get(payType.getServiceName());
-                        Long tradeId = payTypeService.refund(refundDO, refundRecordDO);
-                        //更新退款记录(退款成功)
-                        refundRecordDO.setRefundNo(tradeId.toString());
-                    }
-                    refundRecordDO.setRefundTime(LocalDateTime.now());
-                    refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
-                    tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
                 }
             }
         } catch (ServiceException e) {
@@ -1580,6 +1609,10 @@ public class TradeRefundServiceImpl implements TradeRefundService {
      * @param refundDO
      */
     private void afterRefundFromPayPlatSuccess(RefundDO refundDO) {
+        if(refundDO.getSellMethod()==3){
+            //取消订单退款不处理订单状态
+            return;
+        }
         //已退金额与退款金额一致则全部退款
         String lockKey = String.format(UPDATE_REFUND_LOCK, refundDO.getId());
         RLock lock = redissonClient.getLock(lockKey);

+ 26 - 10
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/report/impl/VoyageStockBoardServiceImpl.java

@@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.yc.ship.framework.common.pojo.PageResult;
 import com.yc.ship.framework.excel.core.util.ExcelUtils;
+import com.yc.ship.framework.security.core.util.SecurityFrameworkUtils;
 import com.yc.ship.module.resource.api.ship.dto.RoomModelFloorNumDTO;
 import com.yc.ship.module.resource.dal.mysql.room.ResourceRoomMapper;
 import com.yc.ship.module.trade.controller.admin.report.vo.*;
@@ -676,15 +677,22 @@ public class VoyageStockBoardServiceImpl implements VoyageStockBoardService {
 
     @Override
     public VisitorSourceDashboardRespVO getVisitorSourceDashboard(VisitorSourceDashboardReqVO reqVO) {
-        // 1. 查询游客列表
-        List<TradeVisitorDO> visitorList = voyageStockBoardMapper.selectVisitorListForSourceDashboard(
-                reqVO.getShipId(), reqVO.getVoyageIds());
-        if (CollUtil.isEmpty(visitorList)) {
+        // 1. 查询游客列表(多游轮汇总)
+        List<TradeVisitorDO> allVisitorList = new ArrayList<>();
+        Long distributorId = SecurityFrameworkUtils.getLoginDistributorId();
+        for (Long shipId : reqVO.getShipIds()) {
+            List<TradeVisitorDO> visitorList = voyageStockBoardMapper.selectVisitorListForSourceDashboard(
+                    shipId, reqVO.getVoyageIds(), distributorId);
+            if (CollUtil.isNotEmpty(visitorList)) {
+                allVisitorList.addAll(visitorList);
+            }
+        }
+        if (CollUtil.isEmpty(allVisitorList)) {
             return buildEmptyResp();
         }
 
         // 2. 分类统计
-        return analyzeVisitorSource(visitorList);
+        return analyzeVisitorSource(allVisitorList);
     }
 
     @Override
@@ -949,17 +957,25 @@ public class VoyageStockBoardServiceImpl implements VoyageStockBoardService {
 
     @Override
     public VisitorAgeDashboardRespVO getVisitorAgeDashboard(VisitorSourceDashboardReqVO reqVO) {
-        // 1. 查询游客年龄列表
-        List<Integer> ageList = voyageStockBoardMapper.selectVisitorAgeListForAgeDashboard(
-                reqVO.getShipId(), reqVO.getVoyageIds());
-        if (CollUtil.isEmpty(ageList)) {
+        // 1. 查询游客年龄列表(多游轮汇总)
+        List<Integer> allAgeList = new ArrayList<>();
+        Long distributorId = SecurityFrameworkUtils.getLoginDistributorId();
+        for (Long shipId : reqVO.getShipIds()) {
+            List<Integer> ageList = voyageStockBoardMapper.selectVisitorAgeListForAgeDashboard(
+                    shipId, reqVO.getVoyageIds(), distributorId);
+            if (CollUtil.isNotEmpty(ageList)) {
+                allAgeList.addAll(ageList);
+            }
+        }
+        if (CollUtil.isEmpty(allAgeList)) {
             return buildEmptyAgeResp();
         }
 
         // 2. 年龄段分组统计
-        return analyzeVisitorAge(ageList);
+        return analyzeVisitorAge(allAgeList);
     }
 
+
     @Override
     public void exportVisitorAgeDashboardExcel(VisitorSourceDashboardReqVO reqVO, HttpServletResponse response) throws IOException {
         VisitorAgeDashboardRespVO data = getVisitorAgeDashboard(reqVO);

+ 239 - 87
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/utils/IdCardProvinceUtil.java

@@ -114,112 +114,264 @@ public class IdCardProvinceUtil {
         // 91开头的为国外身份证件,无对应省份
         PROVINCE_MAP = Collections.unmodifiableMap(map);
 
-        Map<String, String> cityMap = new HashMap<>(80);
-        // 直辖市
-        cityMap.put("1100", "北京"); cityMap.put("1101", "北京");
-        cityMap.put("1200", "天津"); cityMap.put("1201", "天津");
-        cityMap.put("3100", "上海"); cityMap.put("3101", "上海");
-        cityMap.put("5000", "重庆"); cityMap.put("5001", "重庆");
-        // 河北
+        Map<String, String> cityMap = new HashMap<>(350);
+        // ==================== 直辖市 ====================
+        cityMap.put("1100", "北京"); cityMap.put("1101", "北京"); cityMap.put("1102", "北京"); // 1102: 北京旧县区码
+        cityMap.put("1200", "天津"); cityMap.put("1201", "天津"); cityMap.put("1202", "天津"); // 1202: 天津旧县区码
+        cityMap.put("3100", "上海"); cityMap.put("3101", "上海"); cityMap.put("3102", "上海"); // 3102: 上海旧县区码
+        cityMap.put("5000", "重庆"); cityMap.put("5001", "重庆"); cityMap.put("5002", "重庆"); // 5002: 重庆旧县区码
+
+        // ==================== 河北(13) ====================
         cityMap.put("1301", "石家庄"); cityMap.put("1302", "唐山"); cityMap.put("1303", "秦皇岛"); cityMap.put("1304", "邯郸");
-        // 山西
-        cityMap.put("1401", "太原"); cityMap.put("1402", "大同");
-        // 内蒙古
-        cityMap.put("1501", "呼和浩特"); cityMap.put("1502", "包头");
-        // 辽宁
+        cityMap.put("1305", "邢台");   cityMap.put("1306", "保定");   cityMap.put("1307", "张家口");
+        cityMap.put("1308", "承德");   cityMap.put("1309", "沧州");   cityMap.put("1310", "廊坊"); cityMap.put("1311", "衡水");
+        // 河北旧地区码(已撤并入地级市)
+        cityMap.put("1321", "邯郸"); cityMap.put("1322", "邢台"); cityMap.put("1323", "石家庄");
+        cityMap.put("1324", "保定"); cityMap.put("1325", "张家口"); cityMap.put("1326", "承德");
+        cityMap.put("1327", "唐山"); cityMap.put("1328", "衡水"); cityMap.put("1329", "沧州"); cityMap.put("1330", "廊坊");
+
+        // ==================== 山西(14) ====================
+        cityMap.put("1401", "太原"); cityMap.put("1402", "大同");   cityMap.put("1403", "阳泉");
+        cityMap.put("1404", "长治"); cityMap.put("1405", "晋城");   cityMap.put("1406", "朔州");
+        cityMap.put("1407", "晋中"); cityMap.put("1408", "运城");   cityMap.put("1409", "忻州");
+        cityMap.put("1410", "临汾"); cityMap.put("1411", "吕梁");
+        // 山西旧地区码
+        cityMap.put("1422", "忻州"); cityMap.put("1423", "吕梁"); cityMap.put("1424", "晋中");
+        cityMap.put("1426", "临汾"); cityMap.put("1427", "运城"); cityMap.put("1421", "雁北");
+
+        // ==================== 内蒙古(15) ====================
+        cityMap.put("1501", "呼和浩特"); cityMap.put("1502", "包头");   cityMap.put("1503", "乌海");
+        cityMap.put("1504", "赤峰");     cityMap.put("1505", "通辽");   cityMap.put("1506", "鄂尔多斯");
+        cityMap.put("1507", "呼伦贝尔"); cityMap.put("1508", "巴彦淖尔"); cityMap.put("1509", "乌兰察布");
+        cityMap.put("1522", "兴安盟");   cityMap.put("1525", "锡林郭勒盟"); cityMap.put("1529", "阿拉善盟");
+        // 内蒙古旧盟码
+        cityMap.put("1521", "呼伦贝尔"); cityMap.put("1523", "通辽");   cityMap.put("1524", "兴安盟");
+        cityMap.put("1526", "乌兰察布"); cityMap.put("1527", "鄂尔多斯"); cityMap.put("1528", "巴彦淖尔");
+
+        // ==================== 辽宁(21) ====================
         cityMap.put("2101", "沈阳"); cityMap.put("2102", "大连"); cityMap.put("2103", "鞍山");
-        // 吉林
-        cityMap.put("2201", "长春"); cityMap.put("2202", "吉林");
-        // 黑龙江
-        cityMap.put("2301", "哈尔滨"); cityMap.put("2302", "齐齐哈尔");
-        // 江苏
-        cityMap.put("3201", "南京"); cityMap.put("3202", "无锡"); cityMap.put("3203", "徐州");
-        cityMap.put("3204", "常州"); cityMap.put("3205", "苏州"); cityMap.put("3206", "南通");
+        cityMap.put("2104", "抚顺"); cityMap.put("2105", "本溪"); cityMap.put("2106", "丹东");
+        cityMap.put("2107", "锦州"); cityMap.put("2108", "营口"); cityMap.put("2109", "阜新");
+        cityMap.put("2110", "辽阳"); cityMap.put("2111", "盘锦"); cityMap.put("2112", "铁岭");
+        cityMap.put("2113", "朝阳"); cityMap.put("2114", "葫芦岛");
+        // 辽宁旧地区码(铁岭地区/朝阳地区)
+        cityMap.put("2121", "铁岭"); cityMap.put("2122", "朝阳");
+
+        // ==================== 吉林(22) ====================
+        cityMap.put("2201", "长春"); cityMap.put("2202", "吉林"); cityMap.put("2203", "四平");
+        cityMap.put("2204", "辽源"); cityMap.put("2205", "通化"); cityMap.put("2206", "白山");
+        cityMap.put("2207", "松原"); cityMap.put("2208", "白城"); cityMap.put("2224", "延边");
+        // 吉林旧地区码
+        cityMap.put("2223", "延边"); cityMap.put("2224", "延边");
+
+        // ==================== 黑龙江(23) ====================
+        cityMap.put("2301", "哈尔滨"); cityMap.put("2302", "齐齐哈尔"); cityMap.put("2303", "鸡西");
+        cityMap.put("2304", "鹤岗");   cityMap.put("2305", "双鸭山");   cityMap.put("2306", "大庆");
+        cityMap.put("2307", "伊春");   cityMap.put("2308", "佳木斯");   cityMap.put("2309", "七台河");
+        cityMap.put("2310", "牡丹江"); cityMap.put("2311", "黑河");     cityMap.put("2312", "绥化");
+        cityMap.put("2327", "大兴安岭");
+        // 黑龙江旧地区码
+        cityMap.put("2321", "松花江"); cityMap.put("2323", "绥化"); cityMap.put("2326", "黑河");
+
+        // ==================== 江苏(32) ====================
+        cityMap.put("3200", "南京"); // 直辖/省级直辖旧码
+        cityMap.put("3201", "南京"); cityMap.put("3202", "无锡");   cityMap.put("3203", "徐州");
+        cityMap.put("3204", "常州"); cityMap.put("3205", "苏州");   cityMap.put("3206", "南通");
         cityMap.put("3207", "连云港"); cityMap.put("3208", "淮安"); cityMap.put("3209", "盐城");
-        cityMap.put("3210", "扬州"); cityMap.put("3211", "镇江"); cityMap.put("3212", "泰州"); cityMap.put("3213", "宿迁");
-        // 浙江
+        cityMap.put("3210", "扬州"); cityMap.put("3211", "镇江");   cityMap.put("3212", "泰州"); cityMap.put("3213", "宿迁");
+
+        // ==================== 浙江(33) ====================
+        cityMap.put("3300", "杭州"); // 直辖旧码
         cityMap.put("3301", "杭州"); cityMap.put("3302", "宁波"); cityMap.put("3303", "温州");
         cityMap.put("3304", "嘉兴"); cityMap.put("3305", "湖州"); cityMap.put("3306", "绍兴");
         cityMap.put("3307", "金华"); cityMap.put("3308", "衢州"); cityMap.put("3309", "舟山");
         cityMap.put("3310", "台州"); cityMap.put("3311", "丽水");
-        // 安徽
-        cityMap.put("3401", "合肥"); cityMap.put("3402", "芜湖"); cityMap.put("3403", "蚌埠"); cityMap.put("3404", "淮南");
-        cityMap.put("3405", "马鞍山"); cityMap.put("3406", "淮北"); cityMap.put("3407", "铜陵"); cityMap.put("3408", "安庆");
-        // 福建
+
+        // ==================== 安徽(34) ====================
+        cityMap.put("3401", "合肥");   cityMap.put("3402", "芜湖");   cityMap.put("3403", "蚌埠"); cityMap.put("3404", "淮南");
+        cityMap.put("3405", "马鞍山"); cityMap.put("3406", "淮北");   cityMap.put("3407", "铜陵"); cityMap.put("3408", "安庆");
+        cityMap.put("3410", "黄山");   cityMap.put("3411", "滁州");   cityMap.put("3412", "阜阳"); cityMap.put("3413", "宿州");
+        cityMap.put("3415", "六安");   cityMap.put("3416", "亳州");   cityMap.put("3417", "池州"); cityMap.put("3418", "宣城");
+        // 安徽旧地区码(巢湖已撤销并入合肥/芜湖/马鞍山)
+        cityMap.put("3414", "巢湖"); cityMap.put("3421", "阜阳"); cityMap.put("3422", "宿州");
+        cityMap.put("3423", "滁州"); cityMap.put("3424", "六安"); cityMap.put("3425", "宣城");
+        cityMap.put("3426", "巢湖"); cityMap.put("3427", "徽州"); cityMap.put("3428", "安庆");
+        cityMap.put("3429", "池州");
+
+        // ==================== 福建(35) ====================
+        cityMap.put("3500", "福州"); // 直辖旧码
         cityMap.put("3501", "福州"); cityMap.put("3502", "厦门"); cityMap.put("3503", "莆田"); cityMap.put("3504", "三明");
         cityMap.put("3505", "泉州"); cityMap.put("3506", "漳州"); cityMap.put("3507", "南平"); cityMap.put("3508", "龙岩"); cityMap.put("3509", "宁德");
-        // 江西
+        // 福建旧地区码(建阳→南平)
+        cityMap.put("3521", "南平"); cityMap.put("3522", "宁德");
+
+        // ==================== 江西(36) ====================
+        cityMap.put("3600", "南昌"); // 直辖旧码
         cityMap.put("3601", "南昌"); cityMap.put("3602", "景德镇"); cityMap.put("3603", "萍乡"); cityMap.put("3604", "九江");
-        cityMap.put("3605", "新余"); cityMap.put("3606", "鹰潭"); cityMap.put("3607", "赣州"); cityMap.put("3608", "吉安");
-        cityMap.put("3609", "宜春"); cityMap.put("3610", "抚州"); cityMap.put("3611", "上饶");
-        // 山东
-        cityMap.put("3701", "济南"); cityMap.put("3702", "青岛"); cityMap.put("3703", "淄博"); cityMap.put("3704", "枣庄");
-        cityMap.put("3705", "东营"); cityMap.put("3706", "烟台"); cityMap.put("3707", "潍坊"); cityMap.put("3708", "济宁");
-        cityMap.put("3709", "泰安"); cityMap.put("3710", "威海"); cityMap.put("3711", "日照"); cityMap.put("3713", "临沂");
-        // 河南
-        cityMap.put("4101", "郑州"); cityMap.put("4102", "开封"); cityMap.put("4103", "洛阳"); cityMap.put("4104", "平顶山");
-        cityMap.put("4105", "安阳"); cityMap.put("4106", "鹤壁"); cityMap.put("4107", "新乡"); cityMap.put("4108", "焦作");
-        cityMap.put("4109", "濮阳"); cityMap.put("4110", "许昌"); cityMap.put("4111", "漯河"); cityMap.put("4112", "三门峡");
-        cityMap.put("4113", "南阳"); cityMap.put("4114", "商丘"); cityMap.put("4115", "信阳"); cityMap.put("4116", "周口"); cityMap.put("4117", "驻马店");
-        // 湖北
-        cityMap.put("4201", "武汉"); cityMap.put("4202", "黄石"); cityMap.put("4203", "十堰"); cityMap.put("4205", "宜昌");
-        cityMap.put("4206", "襄阳"); cityMap.put("4207", "鄂州"); cityMap.put("4208", "荆门"); cityMap.put("4209", "孝感");
-        cityMap.put("4210", "荆州"); cityMap.put("4211", "黄冈"); cityMap.put("4212", "咸宁"); cityMap.put("4213", "随州");
+        cityMap.put("3605", "新余"); cityMap.put("3606", "鹰潭");   cityMap.put("3607", "赣州"); cityMap.put("3608", "吉安");
+        cityMap.put("3609", "宜春"); cityMap.put("3610", "抚州");   cityMap.put("3611", "上饶");
+        // 江西旧地区码
+        cityMap.put("3621", "赣州"); cityMap.put("3622", "宜春"); cityMap.put("3623", "上饶");
+        cityMap.put("3624", "吉安"); cityMap.put("3625", "抚州"); cityMap.put("3626", "九江");
+
+        // ==================== 山东(37) ====================
+        cityMap.put("3700", "济南"); // 直辖旧码
+        cityMap.put("3701", "济南"); cityMap.put("3702", "青岛");   cityMap.put("3703", "淄博"); cityMap.put("3704", "枣庄");
+        cityMap.put("3705", "东营"); cityMap.put("3706", "烟台");   cityMap.put("3707", "潍坊"); cityMap.put("3708", "济宁");
+        cityMap.put("3709", "泰安"); cityMap.put("3710", "威海");   cityMap.put("3711", "日照"); cityMap.put("3712", "莱芜");
+        cityMap.put("3713", "临沂"); cityMap.put("3714", "德州");   cityMap.put("3715", "聊城"); cityMap.put("3716", "滨州"); cityMap.put("3717", "菏泽");
+        // 山东旧地区码
+        cityMap.put("3723", "滨州"); cityMap.put("3724", "德州");   cityMap.put("3725", "聊城");
+        cityMap.put("3728", "临沂"); cityMap.put("3729", "菏泽");
+
+        // ==================== 河南(41) ====================
+        cityMap.put("4101", "郑州"); cityMap.put("4102", "开封");   cityMap.put("4103", "洛阳"); cityMap.put("4104", "平顶山");
+        cityMap.put("4105", "安阳"); cityMap.put("4106", "鹤壁");   cityMap.put("4107", "新乡"); cityMap.put("4108", "焦作");
+        cityMap.put("4109", "濮阳"); cityMap.put("4110", "许昌");   cityMap.put("4111", "漯河"); cityMap.put("4112", "三门峡");
+        cityMap.put("4113", "南阳"); cityMap.put("4114", "商丘");   cityMap.put("4115", "信阳"); cityMap.put("4116", "周口"); cityMap.put("4117", "驻马店");
+        // 河南旧地区码
+        cityMap.put("4123", "新乡"); cityMap.put("4125", "商丘");   cityMap.put("4127", "周口");
+        cityMap.put("4128", "驻马店"); cityMap.put("4129", "南阳"); cityMap.put("4130", "信阳");
+
+        // ==================== 湖北(42) ====================
+        cityMap.put("4200", "武汉"); // 直辖旧码
+        cityMap.put("4201", "武汉"); cityMap.put("4202", "黄石");   cityMap.put("4203", "十堰"); cityMap.put("4205", "宜昌");
+        cityMap.put("4206", "襄阳"); cityMap.put("4207", "鄂州");   cityMap.put("4208", "荆门"); cityMap.put("4209", "孝感");
+        cityMap.put("4210", "荆州"); cityMap.put("4211", "黄冈");   cityMap.put("4212", "咸宁"); cityMap.put("4213", "随州");
         cityMap.put("4228", "恩施");
-        // 湖南
-        cityMap.put("4301", "长沙"); cityMap.put("4302", "株洲"); cityMap.put("4303", "湘潭"); cityMap.put("4304", "衡阳");
-        cityMap.put("4305", "邵阳"); cityMap.put("4306", "岳阳"); cityMap.put("4307", "常德"); cityMap.put("4308", "张家界");
-        cityMap.put("4309", "益阳"); cityMap.put("4310", "郴州"); cityMap.put("4311", "永州"); cityMap.put("4312", "怀化");
+        // 湖北旧地区码
+        cityMap.put("4221", "黄冈"); cityMap.put("4222", "孝感");   cityMap.put("4223", "咸宁");
+        cityMap.put("4224", "荆州"); cityMap.put("4225", "襄阳");   cityMap.put("4226", "郧阳");
+        cityMap.put("4227", "宜昌"); cityMap.put("4229", "鄂西");
+        // 湖北省直辖县级
+        cityMap.put("4290", "湖北直辖"); cityMap.put("4294", "仙桃"); cityMap.put("4295", "潜江");
+        cityMap.put("4296", "天门");   cityMap.put("4292", "神农架");
+
+        // ==================== 湖南(43) ====================
+        cityMap.put("4300", "长沙"); // 直辖旧码
+        cityMap.put("4301", "长沙"); cityMap.put("4302", "株洲");   cityMap.put("4303", "湘潭"); cityMap.put("4304", "衡阳");
+        cityMap.put("4305", "邵阳"); cityMap.put("4306", "岳阳");   cityMap.put("4307", "常德"); cityMap.put("4308", "张家界");
+        cityMap.put("4309", "益阳"); cityMap.put("4310", "郴州");   cityMap.put("4311", "永州"); cityMap.put("4312", "怀化");
         cityMap.put("4313", "娄底"); cityMap.put("4331", "湘西");
-        // 广东
-        cityMap.put("4401", "广州"); cityMap.put("4402", "韶关"); cityMap.put("4403", "深圳"); cityMap.put("4404", "珠海");
-        cityMap.put("4405", "汕头"); cityMap.put("4406", "佛山"); cityMap.put("4407", "江门"); cityMap.put("4408", "湛江");
-        cityMap.put("4409", "茂名"); cityMap.put("4412", "肇庆"); cityMap.put("4413", "惠州"); cityMap.put("4414", "梅州");
-        cityMap.put("4415", "汕尾"); cityMap.put("4416", "河源"); cityMap.put("4417", "阳江"); cityMap.put("4418", "清远");
-        cityMap.put("4419", "东莞"); cityMap.put("4420", "中山"); cityMap.put("4451", "潮州"); cityMap.put("4452", "揭阳"); cityMap.put("4453", "云浮");
-        // 广西
-        cityMap.put("4501", "南宁"); cityMap.put("4502", "柳州"); cityMap.put("4503", "桂林"); cityMap.put("4504", "梧州");
+        // 湖南旧地区码
+        cityMap.put("4321", "湘潭"); cityMap.put("4322", "衡阳");   cityMap.put("4323", "邵阳");
+        cityMap.put("4324", "岳阳"); cityMap.put("4325", "常德");   cityMap.put("4326", "益阳");
+        cityMap.put("4327", "郴州"); cityMap.put("4328", "永州");   cityMap.put("4329", "娄底");
+        cityMap.put("4330", "怀化");
+
+        // ==================== 广东(44) ====================
+        cityMap.put("4400", "广州"); // 直辖旧码
+        cityMap.put("4401", "广州"); cityMap.put("4402", "韶关");   cityMap.put("4403", "深圳"); cityMap.put("4404", "珠海");
+        cityMap.put("4405", "汕头"); cityMap.put("4406", "佛山");   cityMap.put("4407", "江门"); cityMap.put("4408", "湛江");
+        cityMap.put("4409", "茂名"); cityMap.put("4412", "肇庆");   cityMap.put("4413", "惠州"); cityMap.put("4414", "梅州");
+        cityMap.put("4415", "汕尾"); cityMap.put("4416", "河源");   cityMap.put("4417", "阳江"); cityMap.put("4418", "清远");
+        cityMap.put("4419", "东莞"); cityMap.put("4420", "中山");   cityMap.put("4451", "潮州"); cityMap.put("4452", "揭阳"); cityMap.put("4453", "云浮");
+        // 广东旧地区码(海南行政区已独立)
+        cityMap.put("4421", "海南行政区"); cityMap.put("4425", "梅州");   cityMap.put("4428", "肇庆");
+        cityMap.put("4429", "湛江");       cityMap.put("4490", "广东直辖");
+
+        // ==================== 广西(45) ====================
+        cityMap.put("4500", "南宁"); // 直辖旧码
+        cityMap.put("4501", "南宁"); cityMap.put("4502", "柳州");   cityMap.put("4503", "桂林"); cityMap.put("4504", "梧州");
         cityMap.put("4505", "北海"); cityMap.put("4506", "防城港"); cityMap.put("4507", "钦州"); cityMap.put("4508", "贵港");
-        cityMap.put("4509", "玉林"); cityMap.put("4510", "百色"); cityMap.put("4511", "贺州"); cityMap.put("4512", "河池");
+        cityMap.put("4509", "玉林"); cityMap.put("4510", "百色");   cityMap.put("4511", "贺州"); cityMap.put("4512", "河池");
         cityMap.put("4513", "来宾"); cityMap.put("4514", "崇左");
-        // 海南
+        // 广西旧地区码
+        cityMap.put("4521", "南宁"); cityMap.put("4522", "柳州");   cityMap.put("4523", "桂林");
+        cityMap.put("4524", "梧州"); cityMap.put("4525", "玉林");   cityMap.put("4526", "百色");
+        cityMap.put("4527", "河池"); cityMap.put("4528", "钦州");
+
+        // ==================== 海南(46) ====================
+        cityMap.put("4600", "海口"); // 直辖旧码
         cityMap.put("4601", "海口"); cityMap.put("4602", "三亚"); cityMap.put("4604", "儋州");
-        // 四川
-        cityMap.put("5101", "成都"); cityMap.put("5103", "自贡"); cityMap.put("5104", "攀枝花"); cityMap.put("5105", "泸州");
-        cityMap.put("5106", "德阳"); cityMap.put("5107", "绵阳"); cityMap.put("5108", "广元"); cityMap.put("5109", "遂宁");
-        cityMap.put("5110", "内江"); cityMap.put("5111", "乐山"); cityMap.put("5113", "南充"); cityMap.put("5114", "眉山");
-        cityMap.put("5115", "宜宾"); cityMap.put("5116", "广安"); cityMap.put("5117", "达州"); cityMap.put("5118", "雅安");
-        cityMap.put("5119", "巴中"); cityMap.put("5120", "资阳"); cityMap.put("5132", "阿坝"); cityMap.put("5133", "甘孜"); cityMap.put("5134", "凉山");
-        // 贵州
+        // 海南旧码(原属广东)
+        cityMap.put("4603", "三沙");
+
+        // ==================== 四川(51) ====================
+        cityMap.put("5100", "成都"); // 直辖旧码
+        cityMap.put("5101", "成都"); cityMap.put("5103", "自贡");   cityMap.put("5104", "攀枝花"); cityMap.put("5105", "泸州");
+        cityMap.put("5106", "德阳"); cityMap.put("5107", "绵阳");   cityMap.put("5108", "广元"); cityMap.put("5109", "遂宁");
+        cityMap.put("5110", "内江"); cityMap.put("5111", "乐山");   cityMap.put("5113", "南充"); cityMap.put("5114", "眉山");
+        cityMap.put("5115", "宜宾"); cityMap.put("5116", "广安");   cityMap.put("5117", "达州"); cityMap.put("5118", "雅安");
+        cityMap.put("5119", "巴中"); cityMap.put("5120", "资阳");   cityMap.put("5132", "阿坝"); cityMap.put("5133", "甘孜"); cityMap.put("5134", "凉山");
+        // 四川旧地区码
+        cityMap.put("5121", "内江"); cityMap.put("5122", "涪陵");   cityMap.put("5123", "万县");
+        cityMap.put("5125", "宜宾"); cityMap.put("5128", "南充");   cityMap.put("5129", "达州");
+        cityMap.put("5130", "雅安"); cityMap.put("5131", "黔江");
+
+        // ==================== 贵州(52) ====================
+        cityMap.put("5200", "贵阳"); // 直辖旧码
         cityMap.put("5201", "贵阳"); cityMap.put("5202", "六盘水"); cityMap.put("5203", "遵义"); cityMap.put("5204", "安顺");
-        cityMap.put("5205", "毕节"); cityMap.put("5206", "铜仁"); cityMap.put("5223", "黔西南"); cityMap.put("5226", "黔东南"); cityMap.put("5227", "黔南");
-        // 云南
-        cityMap.put("5301", "昆明"); cityMap.put("5303", "曲靖"); cityMap.put("5304", "玉溪"); cityMap.put("5305", "保山");
-        cityMap.put("5306", "昭通"); cityMap.put("5307", "丽江"); cityMap.put("5308", "普洱"); cityMap.put("5309", "临沧");
-        cityMap.put("5323", "楚雄"); cityMap.put("5325", "红河"); cityMap.put("5326", "文山"); cityMap.put("5328", "西双版纳");
-        cityMap.put("5329", "大理"); cityMap.put("5331", "德宏"); cityMap.put("5333", "怒江"); cityMap.put("5334", "迪庆");
-        // 西藏
-        cityMap.put("5401", "拉萨"); cityMap.put("5402", "日喀则"); cityMap.put("5403", "昌都"); cityMap.put("5404", "林芝");
-        cityMap.put("5405", "山南"); cityMap.put("5406", "那曲"); cityMap.put("5425", "阿里");
-        // 陕西
-        cityMap.put("6101", "西安"); cityMap.put("6102", "铜川"); cityMap.put("6103", "宝鸡"); cityMap.put("6104", "咸阳");
-        cityMap.put("6105", "渭南"); cityMap.put("6106", "延安"); cityMap.put("6107", "汉中"); cityMap.put("6108", "榆林");
+        cityMap.put("5205", "毕节"); cityMap.put("5206", "铜仁");   cityMap.put("5223", "黔西南");
+        cityMap.put("5226", "黔东南"); cityMap.put("5227", "黔南");
+        // 贵州旧地区码
+        cityMap.put("5221", "遵义"); cityMap.put("5222", "铜仁");   cityMap.put("5224", "毕节");
+        cityMap.put("5225", "安顺");
+
+        // ==================== 云南(53) ====================
+        cityMap.put("5300", "昆明"); // 直辖旧码
+        cityMap.put("5301", "昆明"); cityMap.put("5303", "曲靖");   cityMap.put("5304", "玉溪"); cityMap.put("5305", "保山");
+        cityMap.put("5306", "昭通"); cityMap.put("5307", "丽江");   cityMap.put("5308", "普洱"); cityMap.put("5309", "临沧");
+        cityMap.put("5323", "楚雄"); cityMap.put("5325", "红河");   cityMap.put("5326", "文山");
+        cityMap.put("5328", "西双版纳"); cityMap.put("5329", "大理"); cityMap.put("5331", "德宏");
+        cityMap.put("5333", "怒江"); cityMap.put("5334", "迪庆");
+        // 云南旧地区码
+        cityMap.put("5321", "昭通"); cityMap.put("5322", "曲靖");   cityMap.put("5324", "玉溪");
+        cityMap.put("5327", "普洱"); cityMap.put("5330", "保山");   cityMap.put("5332", "临沧");
+
+        // ==================== 西藏(54) ====================
+        cityMap.put("5400", "拉萨"); // 直辖旧码
+        cityMap.put("5401", "拉萨"); cityMap.put("5402", "日喀则"); cityMap.put("5403", "昌都");
+        cityMap.put("5404", "林芝"); cityMap.put("5405", "山南");   cityMap.put("5406", "那曲"); cityMap.put("5425", "阿里");
+        // 西藏旧地区码
+        cityMap.put("5421", "昌都"); cityMap.put("5422", "山南");   cityMap.put("5423", "日喀则");
+        cityMap.put("5424", "那曲"); cityMap.put("5426", "林芝");
+
+        // ==================== 陕西(61) ====================
+        cityMap.put("6100", "西安"); // 直辖旧码
+        cityMap.put("6101", "西安"); cityMap.put("6102", "铜川");   cityMap.put("6103", "宝鸡"); cityMap.put("6104", "咸阳");
+        cityMap.put("6105", "渭南"); cityMap.put("6106", "延安");   cityMap.put("6107", "汉中"); cityMap.put("6108", "榆林");
         cityMap.put("6109", "安康"); cityMap.put("6110", "商洛");
-        // 甘肃
+        // 陕西旧地区码
+        cityMap.put("6121", "渭南"); cityMap.put("6123", "汉中");   cityMap.put("6124", "安康");
+        cityMap.put("6125", "商洛"); cityMap.put("6126", "延安");   cityMap.put("6127", "榆林");
+
+        // ==================== 甘肃(62) ====================
+        cityMap.put("6200", "兰州"); // 直辖旧码
         cityMap.put("6201", "兰州"); cityMap.put("6202", "嘉峪关"); cityMap.put("6203", "金昌"); cityMap.put("6204", "白银");
-        cityMap.put("6205", "天水"); cityMap.put("6206", "武威"); cityMap.put("6207", "张掖"); cityMap.put("6208", "平凉");
-        cityMap.put("6209", "酒泉"); cityMap.put("6210", "庆阳"); cityMap.put("6211", "定西"); cityMap.put("6212", "陇南");
-        // 青海
-        cityMap.put("6301", "西宁"); cityMap.put("6302", "海东"); cityMap.put("6322", "海北"); cityMap.put("6323", "黄南");
-        cityMap.put("6325", "海南"); cityMap.put("6326", "果洛"); cityMap.put("6327", "玉树"); cityMap.put("6328", "海西");
-        // 宁夏
-        cityMap.put("6401", "银川"); cityMap.put("6402", "石嘴山"); cityMap.put("6403", "吴忠"); cityMap.put("6404", "固原"); cityMap.put("6405", "中卫");
-        // 新疆
+        cityMap.put("6205", "天水"); cityMap.put("6206", "武威");   cityMap.put("6207", "张掖"); cityMap.put("6208", "平凉");
+        cityMap.put("6209", "酒泉"); cityMap.put("6210", "庆阳");   cityMap.put("6211", "定西"); cityMap.put("6212", "陇南");
+        // 甘肃旧地区码
+        cityMap.put("6221", "酒泉"); cityMap.put("6222", "张掖");   cityMap.put("6223", "武威");
+        cityMap.put("6224", "定西"); cityMap.put("6225", "天水");   cityMap.put("6226", "陇南");
+        cityMap.put("6227", "平凉"); cityMap.put("6228", "庆阳");   cityMap.put("6229", "临夏");
+
+        // ==================== 青海(63) ====================
+        cityMap.put("6300", "西宁"); // 直辖旧码
+        cityMap.put("6301", "西宁"); cityMap.put("6302", "海东");   cityMap.put("6322", "海北");
+        cityMap.put("6323", "黄南"); cityMap.put("6325", "海南");   cityMap.put("6326", "果洛");
+        cityMap.put("6327", "玉树"); cityMap.put("6328", "海西");
+        // 青海旧地区码
+        cityMap.put("6321", "海东");
+
+        // ==================== 宁夏(64) ====================
+        cityMap.put("6400", "银川"); // 直辖旧码
+        cityMap.put("6401", "银川"); cityMap.put("6402", "石嘴山"); cityMap.put("6403", "吴忠");
+        cityMap.put("6404", "固原"); cityMap.put("6405", "中卫");
+        // 宁夏旧地区码
+        cityMap.put("6421", "吴忠"); cityMap.put("6422", "固原");
+
+        // ==================== 新疆(65) ====================
+        cityMap.put("6500", "乌鲁木齐"); // 直辖旧码
         cityMap.put("6501", "乌鲁木齐"); cityMap.put("6502", "克拉玛依"); cityMap.put("6504", "吐鲁番"); cityMap.put("6505", "哈密");
-        cityMap.put("6523", "昌吉"); cityMap.put("6527", "博尔塔拉"); cityMap.put("6528", "巴音郭楞"); cityMap.put("6529", "阿克苏");
-        cityMap.put("6530", "克孜勒苏"); cityMap.put("6531", "喀什"); cityMap.put("6532", "和田"); cityMap.put("6540", "伊犁");
-        cityMap.put("6542", "塔城"); cityMap.put("6543", "阿勒泰"); cityMap.put("6590", "自治区直辖县级");
-        // 港澳台
+        cityMap.put("6523", "昌吉");     cityMap.put("6527", "博尔塔拉"); cityMap.put("6528", "巴音郭楞");
+        cityMap.put("6529", "阿克苏");   cityMap.put("6530", "克孜勒苏"); cityMap.put("6531", "喀什");
+        cityMap.put("6532", "和田");     cityMap.put("6540", "伊犁");     cityMap.put("6542", "塔城");
+        cityMap.put("6543", "阿勒泰");   cityMap.put("6590", "自治区直辖县级");
+        // 新疆旧地区码
+        cityMap.put("6521", "吐鲁番"); cityMap.put("6522", "哈密");
+
+        // 港澳台(暂不启用)
         // cityMap.put("7100", "台湾"); cityMap.put("8100", "香港"); cityMap.put("8200", "澳门");
         CITY_MAP = Collections.unmodifiableMap(cityMap);
     }

+ 5 - 5
ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/orderjzdetail/OrderGiftVisitorMapper.xml

@@ -29,13 +29,13 @@
               dj.remark as dispatchRemark,
              gfv.sign_remark as remark
         FROM trade_visitor v
-        LEFT JOIN  trade_order_jz_gift_visitor gfv ON gfv.visitor_id = v.id
+        LEFT JOIN  trade_order_jz_gift_visitor gfv ON gfv.visitor_id = v.id and gfv.deleted = 0
         INNER JOIN trade_order o ON v.order_id = o.id
         INNER JOIN product_voyage pv ON o.voyage_id = pv.id
         inner join trade_detail td on td.order_id = o.id and td.visitor_id = v.id  and td.product_id = 2034458675435925505 and td.deleted = 0
         INNER JOIN resource_route r ON pv.route_id = r.id
-        LEFT JOIN resource_room_model rm ON v.room_model_id = rm.id
-        LEFT JOIN trade_order_jz_dispatch dj ON gfv.dispatch_id = dj.id AND dj.source_type = 2
+        LEFT JOIN resource_room_model rm ON v.room_model_id = rm.id and rm.deleted = 0
+        LEFT JOIN trade_order_jz_dispatch dj ON gfv.dispatch_id = dj.id AND dj.source_type = 2 and dj.deleted = 0
         WHERE v.deleted = 0 AND o.deleted = 0 AND pv.deleted = 0 AND o.order_status IN (15, 14, 13, 10, 12, 9, 8, 7, 6, 5, 4, 3, 1, 0)
         AND o.voyage_id = #{vo.voyageId}
         <if test="vo.idCard != null and vo.idCard != ''">
@@ -92,13 +92,13 @@
             dj.remark as dispatchRemark,
             gfv.sign_remark as remark
         FROM trade_visitor v
-        LEFT JOIN  trade_order_jz_gift_visitor gfv ON gfv.visitor_id = v.id AND v.deleted = 0
+        LEFT JOIN  trade_order_jz_gift_visitor gfv ON gfv.visitor_id = v.id AND v.deleted = 0 and gfv.deleted = 0
         INNER JOIN trade_order o ON v.order_id = o.id AND o.deleted = 0
         inner join trade_detail td on td.order_id = o.id and td.visitor_id = v.id and td.product_id = 2034458675435925505 and td.deleted = 0
         INNER JOIN product_voyage pv ON o.voyage_id = pv.id AND pv.deleted = 0
         INNER JOIN resource_route r ON pv.route_id = r.id
         LEFT JOIN resource_room_model rm ON v.room_model_id = rm.id
-        LEFT JOIN trade_order_jz_dispatch dj ON gfv.dispatch_id = dj.id AND dj.source_type = 2
+        LEFT JOIN trade_order_jz_dispatch dj ON gfv.dispatch_id = dj.id AND dj.source_type = 2 and dj.deleted = 0
         WHERE  o.voyage_id = #{vo.voyageId} AND o.order_status IN (15, 14, 13, 10, 12, 9, 8, 7, 6, 5, 4, 3, 1, 0)
         <if test="vo.idCard != null and vo.idCard != ''">
             AND v.credential_no LIKE CONCAT('%', #{vo.idCard}, '%')

+ 18 - 12
ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/orderjzdetail/OrderJzDetailMapper.xml

@@ -16,13 +16,13 @@
         dj.bus_number as busNumber, dj.driver_name as driverName, dj.driver_phone as driverPhone,
         dj.receiver_name as receiverName, dj.receiver_phone as receiverPhone, d.sign_remark as dispatchRemark
         from trade_order_jz_detail d
-        inner join trade_order_jz oz on d.jz_id = oz.id
+        inner join trade_order_jz oz on d.jz_id = oz.id and oz.deleted = 0
         inner join trade_order o on o.deleted = 0 and o.id = oz.order_id
-        inner join product_voyage v on o.voyage_id = v.id
-        inner join resource_route r on v.route_id = r.id
-        inner join trade_visitor u on u.credential_no = d.id_card and u.order_id = oz.order_id
-        left join resource_room_model rm on u.room_model_id = rm.id
-        left join trade_order_jz_dispatch dj on d.dispatch_id = dj.id
+        inner join product_voyage v on o.voyage_id = v.id and v.deleted = 0
+        inner join resource_route r on v.route_id = r.id and r.deleted = 0
+        inner join trade_visitor u on u.credential_no = d.id_card and u.order_id = oz.order_id and u.deleted = 0
+        left join resource_room_model rm on u.room_model_id = rm.id and rm.deleted = 0
+        left join trade_order_jz_dispatch dj on d.dispatch_id = dj.id and dj.deleted = 0
         inner join trade_detail td on td.order_id = o.id and td.product_id != 2034458675435925505
         where d.deleted = 0 and o.voyage_id = #{vo.voyageId} and o.order_status IN (15, 14, 13, 10, 12, 9, 8, 7, 6, 5, 4, 3, 1, 0)
         <if test="vo.idCard != null">
@@ -40,6 +40,9 @@
         <if test="vo.dispatchNo != null and vo.dispatchNo != ''">
             and dj.dispatch_no like concat('%', #{vo.dispatchNo}, '%')
         </if>
+        <if test="vo.orderNo != null and vo.orderNo != ''">
+            and o.order_no = #{vo.orderNo}
+        </if>
         <if test="vo.signStatus != null and vo.signStatus == 1">
             and d.sign_time is not null
         </if>
@@ -55,13 +58,13 @@
         select distinct d.* , r.direction , rm.name as room_model_name,
         o.order_no as orderNo,o.source_name as sourceName, dj.dispatch_no as dispatchNo, dj.group_no as groupNo, dj.bus_number as busNumber,dj.driver_name as driverName,dj.driver_phone as driverPhone,dj.receiver_name as receiverName,dj.receiver_phone as receiverPhone,d.sign_remark as dispatchRemark
         from trade_order_jz_detail d
-        inner join trade_order_jz oz on d.jz_id = oz.id
+        inner join trade_order_jz oz on d.jz_id = oz.id and oz.deleted = 0
         inner join trade_order o on o.deleted = 0 and o.id = oz.order_id
-        inner join product_voyage v on o.voyage_id = v.id
-        inner join resource_route r on v.route_id = r.id
-        inner join trade_visitor u on u.credential_no = d.id_card and u.order_id = oz.order_id
-        left join resource_room_model rm on u.room_model_id = rm.id
-        left join trade_order_jz_dispatch dj on d.dispatch_id = dj.id
+        inner join product_voyage v on o.voyage_id = v.id and v.deleted = 0
+        inner join resource_route r on v.route_id = r.id and r.deleted = 0
+        inner join trade_visitor u on u.credential_no = d.id_card and u.order_id = oz.order_id and u.deleted = 0
+        left join resource_room_model rm on u.room_model_id = rm.id and rm.deleted = 0
+        left join trade_order_jz_dispatch dj on d.dispatch_id = dj.id and dj.deleted = 0
         inner join trade_detail td on td.order_id = o.id and td.product_id != 2034458675435925505
         where d.deleted = 0 and o.voyage_id = #{vo.voyageId} and o.order_status IN (15, 14, 13, 10, 12, 9, 8, 7, 6, 5, 4, 3, 1, 0)
         <if test="vo.idCard != null and vo.idCard != ''">
@@ -76,6 +79,9 @@
         <if test="vo.arriveTime != null and vo.arriveTime != ''">
             and d.arrive_time = #{vo.arriveTime}
         </if>
+        <if test="vo.orderNo != null and vo.orderNo != ''">
+            and o.order_no = #{vo.orderNo}
+        </if>
         <if test="vo.dispatchNo != null and vo.dispatchNo != ''">
             and dj.dispatch_no like concat('%', #{vo.dispatchNo}, '%')
         </if>

+ 6 - 0
ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/report/VoyageStockBoardMapper.xml

@@ -122,6 +122,9 @@
                 #{item}
             </foreach>
         </if>
+        <if test="distributorId != null">
+            AND tor.source_id = #{distributorId}
+        </if>
     </select>
 
     <select id="selectVisitorAgeListForAgeDashboard" resultType="java.lang.Integer">
@@ -140,6 +143,9 @@
                 #{item}
             </foreach>
         </if>
+        <if test="distributorId != null">
+            AND tor.source_id = #{distributorId}
+        </if>
     </select>
 
     <select id="selectAllVoyageStockPage"

+ 21 - 5
ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/report/YangtzePassengerSummaryMapper.xml

@@ -25,7 +25,7 @@
             COALESCE(visitor_stats.totalPassengers, 0) AS totalPassengers,
             COALESCE(free_stats.ticketedPassengers, 0) AS ticketedPassengers,
             COALESCE(free_stats.freePassengers, 0) AS freePassengers,
-            COALESCE(room_stats.reservedRooms * 2 + visitor_stats.totalPassengers, 0) AS estimatedPassengers,
+            COALESCE(room_stats.reservedRooms, 0) * 2 + COALESCE(visitor_stats.totalPassengers, 0) AS estimatedPassengers,
             COALESCE(finance_stats.receivableAmount, 0) AS receivableAmount,
             COALESCE(finance_stats.receivedAmount, 0) AS receivedAmount,
             COALESCE(finance_stats.receivableAmount, 0) - COALESCE(finance_stats.receivedAmount, 0) AS unreceivedAmount,
@@ -39,7 +39,9 @@
             COALESCE(team_stats.groupCount, 0) AS groupCount,
             COALESCE(team_stats.individualCount, 0) AS individualCount,
             COALESCE(nationality_stats.domesticCount, 0) AS domesticCount,
-            COALESCE(nationality_stats.overseasCount, 0) AS overseasCount
+            COALESCE(nationality_stats.overseasCount, 0) AS overseasCount,
+            COALESCE(product_stats.upgradeFloorCount, 0) AS upgradeFloorCount,
+            COALESCE(product_stats.suiteDiscountCount, 0) AS suiteDiscountCount
         FROM product_voyage v
         INNER JOIN resource_ship s ON v.ship_id = s.id AND s.deleted = 0
         INNER JOIN resource_route r ON v.route_id = r.id AND r.deleted = 0
@@ -47,9 +49,9 @@
         LEFT JOIN (
         SELECT
         o.voyage_id,
-        COUNT(DISTINCT tot.room_index_id) AS totalRooms,
-        COUNT(DISTINCT CASE WHEN o.order_status != 14 THEN tot.room_index_id END) AS paidRooms,
-        COUNT(DISTINCT CASE WHEN o.order_status = 14 THEN tot.room_index_id END) AS reservedRooms
+        SUM(COALESCE(tot.use_room_num, 0)) AS totalRooms,
+        SUM( CASE WHEN o.order_status != 14 THEN COALESCE(tot.use_room_num, 0)  END) AS paidRooms,
+        SUM( CASE WHEN o.order_status = 14 THEN COALESCE(tot.use_room_num, 0)  END) AS reservedRooms
         FROM trade_order_room_model tot
         INNER JOIN trade_order o ON o.id = tot.order_id
         WHERE o.deleted = 0
@@ -141,6 +143,20 @@
             WHERE o.deleted = 0 AND o.order_status IN (15, 14, 13, 10, 12, 9, 8, 7, 6, 5, 4, 3, 1, 0)
             GROUP BY o.voyage_id
         ) nationality_stats ON v.id = nationality_stats.voyage_id
+
+        LEFT JOIN (
+            SELECT
+                o.voyage_id,
+                COUNT(DISTINCT CASE WHEN td.product_id IN (2007974975358070785, 2012077983546568706, 2012078139826335745) THEN td.id END) AS upgradeFloorCount,
+                COUNT(DISTINCT CASE WHEN td.product_id = 2013782764375310338 THEN td.id END) AS suiteDiscountCount
+            FROM trade_detail td
+            INNER JOIN trade_order o ON o.id = td.order_id AND o.deleted = 0
+            WHERE td.deleted = 0
+              AND o.order_status IN (14, 13, 6, 1)
+              AND td.product_id IN (2007974975358070785, 2012077983546568706, 2012078139826335745, 2013782764375310338)
+            GROUP BY o.voyage_id
+        ) product_stats ON v.id = product_stats.voyage_id
+
         WHERE v.deleted = 0
         <if test="vo.voyageIds != null and vo.voyageIds.size() > 0">
             AND v.id IN