chenxiaofei 3 viikkoa sitten
vanhempi
commit
47d472a7e1

+ 10 - 0
sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/dubbo/TransportRemoteService.java

@@ -2,6 +2,7 @@ package com.sckw.transport.api.dubbo;
 
 
 import com.sckw.core.web.response.HttpResult;
 import com.sckw.core.web.response.HttpResult;
 import com.sckw.transport.api.model.dto.AcceptCarriageLogisticsOrderDto;
 import com.sckw.transport.api.model.dto.AcceptCarriageLogisticsOrderDto;
+import com.sckw.transport.api.model.dto.RawOreOrderExecutionDto;
 import com.sckw.transport.api.model.dto.TradeOrderWaybillAggDto;
 import com.sckw.transport.api.model.dto.TradeOrderWaybillAggDto;
 import com.sckw.transport.api.model.dto.AccountCheckingBindDTO;
 import com.sckw.transport.api.model.dto.AccountCheckingBindDTO;
 import com.sckw.transport.api.model.dto.RWaybillOrderDto;
 import com.sckw.transport.api.model.dto.RWaybillOrderDto;
@@ -222,4 +223,13 @@ public interface TransportRemoteService {
      * @param tOrderIds 贸易订单 id 列表(建议按展示顺序传入,返回值顺序与入参一致)
      * @param tOrderIds 贸易订单 id 列表(建议按展示顺序传入,返回值顺序与入参一致)
      */
      */
     List<TradeOrderWaybillAggDto> aggregateCompletedWaybillStatsByTradeOrderIds(List<Long> tOrderIds);
     List<TradeOrderWaybillAggDto> aggregateCompletedWaybillStatsByTradeOrderIds(List<Long> tOrderIds);
+
+    /**
+     * Query raw ore logistics order execution display data.
+     *
+     * @param entIds enterprise ids visible to the public dashboard
+     * @param limit maximum row count
+     * @return raw ore logistics order execution data
+     */
+    List<RawOreOrderExecutionDto> listRawOreOrderExecutionDisplay(List<Long> entIds, int limit);
 }
 }

+ 37 - 0
sckw-modules-api/sckw-transport-api/src/main/java/com/sckw/transport/api/model/dto/RawOreOrderExecutionDto.java

@@ -0,0 +1,37 @@
+package com.sckw.transport.api.model.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * Raw ore logistics order execution display data.
+ */
+@Data
+public class RawOreOrderExecutionDto implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "Logistics order ID")
+    private Long logisticsOrderId;
+
+    @Schema(description = "Logistics order number")
+    private String logisticsOrderNo;
+
+    @Schema(description = "Planned amount")
+    private BigDecimal plannedAmount;
+
+    @Schema(description = "Completed waybill trip count")
+    private Integer tripCount;
+
+    @Schema(description = "Completed waybill net weight sum")
+    private BigDecimal netWeightSum;
+
+    @Schema(description = "Logistics order update time")
+    private Date updateTime;
+}

+ 12 - 0
sckw-modules/sckw-order/src/main/java/com/sckw/order/repository/KwoTradeOrderUnitRepository.java

@@ -37,6 +37,18 @@ public class KwoTradeOrderUnitRepository extends ServiceImpl<KwoTradeOrderUnitMa
                 .in(KwoTradeOrderUnit::getEntId, entIds));
                 .in(KwoTradeOrderUnit::getEntId, entIds));
     }
     }
 
 
+    public List<KwoTradeOrderUnit> queryByReportEntIds(List<Long> entIds) {
+        if (entIds == null || entIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return list(Wrappers.<KwoTradeOrderUnit>lambdaQuery()
+                .select(KwoTradeOrderUnit::getTOrderId)
+                .eq(BaseModel::getDelFlag, 0)
+                .and(wrapper -> wrapper.in(KwoTradeOrderUnit::getEntId, entIds)
+                        .or()
+                        .in(KwoTradeOrderUnit::getTopEntId, entIds)));
+    }
+
     public List<KwoTradeOrderUnit> queryByEntIdAndUnitType(Long entId, String unitType) {
     public List<KwoTradeOrderUnit> queryByEntIdAndUnitType(Long entId, String unitType) {
         if (entId == null) {
         if (entId == null) {
             return Collections.emptyList();
             return Collections.emptyList();

+ 190 - 39
sckw-modules/sckw-order/src/main/java/com/sckw/order/serivce/KwoTradeOrderService.java

@@ -86,6 +86,7 @@ import com.sckw.system.api.model.dto.req.DataPermissionFilterReqDto;
 import com.sckw.system.api.model.dto.res.*;
 import com.sckw.system.api.model.dto.res.*;
 import com.sckw.transport.api.dubbo.TransportRemoteStatisticsService;
 import com.sckw.transport.api.dubbo.TransportRemoteStatisticsService;
 import com.sckw.transport.api.dubbo.TransportRemoteService;
 import com.sckw.transport.api.dubbo.TransportRemoteService;
+import com.sckw.transport.api.model.dto.RawOreOrderExecutionDto;
 import com.sckw.transport.api.model.dto.TradeOrderWaybillAggDto;
 import com.sckw.transport.api.model.dto.TradeOrderWaybillAggDto;
 import com.sckw.transport.api.model.param.AddLogisticOrderParam;
 import com.sckw.transport.api.model.param.AddLogisticOrderParam;
 import com.sckw.transport.api.model.param.LogisticInfo;
 import com.sckw.transport.api.model.param.LogisticInfo;
@@ -124,6 +125,17 @@ import java.util.stream.Collectors;
 @RequiredArgsConstructor
 @RequiredArgsConstructor
 public class KwoTradeOrderService {
 public class KwoTradeOrderService {
 
 
+    /**
+     * Fixed enterprise scope for the public BI dashboard.
+     */
+    private static final List<Long> REPORT_ENT_IDS = List.of(
+            538038314096136193L,
+            538039617157337089L,
+            538040297439891457L
+    );
+
+    private static final int ORDER_EXECUTION_DISPLAY_LIMIT = 500;
+
     @DubboReference(version = "1.0.0", group = "design", check = false)
     @DubboReference(version = "1.0.0", group = "design", check = false)
     private RemoteSystemService remoteSystemService;
     private RemoteSystemService remoteSystemService;
     @DubboReference(version = "1.0.0", group = "design", check = false)
     @DubboReference(version = "1.0.0", group = "design", check = false)
@@ -2070,7 +2082,7 @@ public class KwoTradeOrderService {
         }
         }
         List<OrderListResDTO> orders = kwoTradeOrderMapper.tradeOrderExport(dto, dto.getGoodIds(), LoginUserHolder.getAuthUserIdList(), ids);
         List<OrderListResDTO> orders = kwoTradeOrderMapper.tradeOrderExport(dto, dto.getGoodIds(), LoginUserHolder.getAuthUserIdList(), ids);
         if (CollUtil.isEmpty(orders)) {
         if (CollUtil.isEmpty(orders)) {
-            return Collections.emptyList();
+            return new ArrayList<>();
         }
         }
         Map<String, Map<String, String>> dict = remoteSystemService.queryDictByType(List.of(DictTypeEnum.TORDER_STATUS.getType(), DictTypeEnum.PICKUP_TYPE.getType(), DictTypeEnum.DELIVERY_TYPE.getType(), DictTypeEnum.TORDER_SOURCE.getType(), DictTypeEnum.TRADE_TYPE.getType()));
         Map<String, Map<String, String>> dict = remoteSystemService.queryDictByType(List.of(DictTypeEnum.TORDER_STATUS.getType(), DictTypeEnum.PICKUP_TYPE.getType(), DictTypeEnum.DELIVERY_TYPE.getType(), DictTypeEnum.TORDER_SOURCE.getType(), DictTypeEnum.TRADE_TYPE.getType()));
         Map<String, String> statusMap, pickupMap, deliveryMap, sourceMap, tradeMap;
         Map<String, String> statusMap, pickupMap, deliveryMap, sourceMap, tradeMap;
@@ -3058,92 +3070,231 @@ public class KwoTradeOrderService {
 
 
 
 
     /**
     /**
-     * 订单执行情况:已下单未结算({@link TradeOrderStatusEnum}:待审核、进行中、结算中;不含已完成、审核驳回、已取消),最多 500 条,按更新时间倒序
+     * 获取贸易订单及原矿物流订单的执行展示列表。
+     * 主要用于公共BI大屏展示,包含特定企业的贸易订单执行进度和原矿物流订单执行情况。
      *
      *
-     * @return 订单执行展示列表
+     * @return 订单执行展示数据列表
      */
      */
     public List<OrderExecutionDisplayVo> listOrderExecutionDisplay() {
     public List<OrderExecutionDisplayVo> listOrderExecutionDisplay() {
-        log.info("开始查询订单执行情况展示列表");
+        log.info("开始查询订单执行展示数据");
 
 
-        // 1. 查询符合条件的贸易订单列表(待审核、进行中、结算中),限制最多500条,按更新时间倒序
+        // 1. 查询原矿物流订单执行数据
+        List<OrderExecutionDisplayVo> rawOreRows = buildRawOreOrderExecutionDisplayRows(ORDER_EXECUTION_DISPLAY_LIMIT);
+        log.debug("查询到原矿物流订单执行数据条数: {}", rawOreRows.size());
+
+        // 2. 查询指定报表企业范围内的贸易订单ID
+        List<Long> reportTradeOrderIds = queryReportTradeOrderIds();
+        if (CollectionUtils.isEmpty(reportTradeOrderIds)) {
+            log.info("未找到指定报表企业的贸易订单ID,仅返回原矿物流订单数据");
+            return rawOreRows;
+        }
+        log.info("找到指定报表企业的贸易订单ID数量: {}", reportTradeOrderIds.size());
+
+        // 3. 查询符合条件的贸易订单列表
+        // 条件:未删除、在指定ID范围内、状态为待审核/进行中/结算中、按更新时间倒序、限制数量
         List<KwoTradeOrder> orders = kwoTradeOrderMapper.selectList(
         List<KwoTradeOrder> orders = kwoTradeOrderMapper.selectList(
                 Wrappers.<KwoTradeOrder>lambdaQuery()
                 Wrappers.<KwoTradeOrder>lambdaQuery()
                         .eq(KwoTradeOrder::getDelFlag, Global.NO)
                         .eq(KwoTradeOrder::getDelFlag, Global.NO)
+                        .in(CollectionUtils.isNotEmpty(reportTradeOrderIds), KwoTradeOrder::getId, reportTradeOrderIds)
                         .in(KwoTradeOrder::getStatus, Arrays.asList(
                         .in(KwoTradeOrder::getStatus, Arrays.asList(
                                 TradeOrderStatusEnum.AUDIT.getCode(),
                                 TradeOrderStatusEnum.AUDIT.getCode(),
                                 TradeOrderStatusEnum.ING.getCode(),
                                 TradeOrderStatusEnum.ING.getCode(),
                                 TradeOrderStatusEnum.DEAL.getCode()))
                                 TradeOrderStatusEnum.DEAL.getCode()))
                         .orderByDesc(KwoTradeOrder::getUpdateTime)
                         .orderByDesc(KwoTradeOrder::getUpdateTime)
-                        .last("LIMIT 500"));
+                        .last("LIMIT " + ORDER_EXECUTION_DISPLAY_LIMIT));
 
 
         if (CollectionUtils.isEmpty(orders)) {
         if (CollectionUtils.isEmpty(orders)) {
-            log.info("未查询到符合条件的订单数据");
-            return Collections.emptyList();
+            log.info("未查询到符合条件的贸易订单,仅返回原矿物流订单数据");
+            return rawOreRows;
         }
         }
-        log.info("查询到 {} 条待处理订单", orders.size());
+        log.info("查询到符合条件的贸易订单数量: {}", orders.size());
 
 
-        // 2. 提取订单ID列表,批量查询物流运单聚合统计数据
-        List<Long> ids = orders.stream().map(KwoTradeOrder::getId).toList();
-        log.debug("开始批量查询物流运单统计信息,订单ID数量: {}", ids.size());
-        List<TradeOrderWaybillAggDto> aggs = transportRemoteService.aggregateCompletedWaybillStatsByTradeOrderIds(ids);
+        // 4. 批量查询贸易订单关联的运单统计信息(已完成运单的趟次、净重等)
+        List<Long> orderIds = orders.stream().map(KwoTradeOrder::getId).toList();
+        log.debug("开始批量查询贸易订单运单统计信息,订单ID数量: {}", orderIds.size());
+        List<TradeOrderWaybillAggDto> aggs = transportRemoteService.aggregateCompletedWaybillStatsByTradeOrderIds(orderIds);
+        log.debug("查询到贸易订单运单统计信息数量: {}", aggs != null ? aggs.size() : 0);
 
 
-        // 3. 将物流统计数据转换为Map,方便后续通过订单ID快速查找
-        Map<Long, TradeOrderWaybillAggDto> aggMap = aggs.stream()
-                .collect(Collectors.toMap(TradeOrderWaybillAggDto::getTOrderId, Function.identity(), (a, b) -> a));
-        log.debug("获取到 {} 条物流运单统计信息", aggMap.size());
+        // 5. 将运单统计信息转换为Map,以便通过订单ID快速查找
+        Map<Long, TradeOrderWaybillAggDto> aggMap = aggs != null ? aggs.stream()
+                .collect(Collectors.toMap(TradeOrderWaybillAggDto::getTOrderId, Function.identity(), (a, b) -> a))
+                : Collections.emptyMap();
 
 
-        // 4. 组装返回结果
-        List<OrderExecutionDisplayVo> rows = new ArrayList<>(orders.size());
-        for (KwoTradeOrder o : orders) {
-            TradeOrderWaybillAggDto a = aggMap.get(o.getId());
+        // 6. 组装贸易订单执行展示数据
+        List<OrderExecutionDisplayVo> rows = new ArrayList<>(orders.size() + rawOreRows.size());
+        for (KwoTradeOrder order : orders) {
+            TradeOrderWaybillAggDto agg = aggMap.get(order.getId());
 
 
             // 获取运输趟次,若无数据则默认为0
             // 获取运输趟次,若无数据则默认为0
-            int trips = a != null && a.getTripCount() != null ? a.getTripCount() : 0;
+            int trips = (agg != null && agg.getTripCount() != null) ? agg.getTripCount() : 0;
 
 
             // 获取已装载净重总和,若无数据则默认为0
             // 获取已装载净重总和,若无数据则默认为0
-            BigDecimal loadedRaw = a != null && a.getNetWeightSum() != null ? a.getNetWeightSum() : BigDecimal.ZERO;
+            BigDecimal loadedWeight = (agg != null && agg.getNetWeightSum() != null) ? agg.getNetWeightSum() : BigDecimal.ZERO;
 
 
             // 获取订单计划总量,若无数据则默认为0
             // 获取订单计划总量,若无数据则默认为0
-            BigDecimal plannedRaw = o.getAmount() != null ? o.getAmount() : BigDecimal.ZERO;
+            BigDecimal plannedAmount = (order.getAmount() != null) ? order.getAmount() : BigDecimal.ZERO;
 
 
-            // 计算计划吨数(取整)
-            int plannedTons = Math.max(0, plannedRaw.setScale(0, RoundingMode.HALF_UP).intValue());
+            // 计算计划吨数(取整,至少为0
+            int plannedTons = Math.max(0, plannedAmount.setScale(0, RoundingMode.HALF_UP).intValue());
 
 
             // 计算已装载吨数(保留两位小数)
             // 计算已装载吨数(保留两位小数)
-            BigDecimal loadedTons = loadedRaw.setScale(2, RoundingMode.HALF_UP);
+            BigDecimal loadedTons = loadedWeight.setScale(2, RoundingMode.HALF_UP);
 
 
             // 计算完成率百分比
             // 计算完成率百分比
-            BigDecimal rate;
-            if (plannedRaw.compareTo(BigDecimal.ZERO) <= 0) {
-                // 如果计划量为0或负数,完成率为0
-                rate = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+            BigDecimal completionRate;
+            if (plannedAmount.compareTo(BigDecimal.ZERO) <= 0) {
+                // 如果计划量为0或负数,防止除以零错误,完成率为0
+                completionRate = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
             } else {
             } else {
-                // 完成率 = (已装载量 / 计划量) * 100,保留两位小数
-                rate = loadedRaw.divide(plannedRaw, 10, RoundingMode.HALF_UP)
+                // 完成率 = (已装载量 / 计划量) * 100,中间计算保留10位精度,最终结果保留2位小数
+                completionRate = loadedWeight.divide(plannedAmount, 10, RoundingMode.HALF_UP)
                         .multiply(BigDecimal.valueOf(100))
                         .multiply(BigDecimal.valueOf(100))
                         .setScale(2, RoundingMode.HALF_UP);
                         .setScale(2, RoundingMode.HALF_UP);
             }
             }
 
 
             // 构建返回对象
             // 构建返回对象
             OrderExecutionDisplayVo vo = new OrderExecutionDisplayVo();
             OrderExecutionDisplayVo vo = new OrderExecutionDisplayVo();
-            vo.setOrderNo(o.getTOrderNo());
+            vo.setOrderNo(order.getTOrderNo());
             vo.setPlannedTons(plannedTons);
             vo.setPlannedTons(plannedTons);
             vo.setLoadedTons(loadedTons);
             vo.setLoadedTons(loadedTons);
             vo.setTripCount(trips);
             vo.setTripCount(trips);
-            vo.setCompletionRatePercent(rate);
+            vo.setCompletionRatePercent(completionRate);
             rows.add(vo);
             rows.add(vo);
         }
         }
 
 
-        log.info("订单执行情况展示列表组装完成,共 {} 条数据", rows.size());
+        // 7. 将原矿物流订单数据追加到列表末尾
+        rows.addAll(rawOreRows);
+        log.info("订单执行展示数据组装完成,贸易订单数: {}, 原矿物流订单数: {}, 总数: {}",
+                orders.size(), rawOreRows.size(), rows.size());
         return rows;
         return rows;
     }
     }
+    /**
+     * 查询属于公共BI大屏固定企业范围的贸易订单ID列表。
+     * <p>
+     * 该方法通过查询 {@link KwoTradeOrderUnitRepository} 获取指定企业ID列表({@link #REPORT_ENT_IDS})
+     * 关联的贸易订单单位信息,并提取出唯一的贸易订单ID集合。
+     * </p>
+     *
+     * @return 贸易订单ID列表,若无匹配数据则返回空列表
+     */
+    private List<Long> queryReportTradeOrderIds() {
+        log.debug("开始查询公共BI大屏固定企业范围的贸易订单ID,目标企业IDs: {}", REPORT_ENT_IDS);
+        
+        // 1. 根据固定企业ID列表查询关联的贸易订单单位信息
+        List<KwoTradeOrderUnit> units = kwoTradeOrderUnitRepository.queryByReportEntIds(REPORT_ENT_IDS);
+        
+        // 2. 若未查询到任何单位信息,直接返回空列表
+        if (CollectionUtils.isEmpty(units)) {
+            log.debug("未查询到固定企业范围内的贸易订单单位信息");
+            return Collections.emptyList();
+        }
+        
+        log.debug("查询到贸易订单单位信息数量: {}", units.size());
+
+        // 3. 流式处理:过滤空对象 -> 提取订单ID -> 过滤空ID -> 去重 -> 转换为List
+        List<Long> orderIds = units.stream()
+                .filter(Objects::nonNull)
+                .map(KwoTradeOrderUnit::getTOrderId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .toList();
+        
+        log.debug("最终获取到的唯一贸易订单ID数量: {}", orderIds.size());
+        return orderIds;
+    }
 
 
     /**
     /**
-     * 查询增补运力物流合同(手动派车物流合同)
+     * 构建原矿物流订单执行展示数据列表。
+     * <p>
+     * 该方法调用远程运输服务,获取指定企业范围({@link #REPORT_ENT_IDS})内的原矿物流订单执行数据,
+     * 并将其转换为前端展示所需的 {@link OrderExecutionDisplayVo} 对象列表。
+     * </p>
      *
      *
-     * @param param
-     * @return
+     * @param limit 查询数量限制,用于控制返回的数据条数
+     * @return 原矿物流订单执行展示数据列表;若查询失败或无数据,则返回空列表
      */
      */
+    private List<OrderExecutionDisplayVo> buildRawOreOrderExecutionDisplayRows(int limit) {
+        log.info("开始构建原矿物流订单执行展示数据,限制数量: {}", limit);
+        try {
+            // 1. 调用远程服务查询原矿物流订单执行数据
+            log.debug("调用远程运输服务查询原矿物流订单,企业IDs: {}, 限制数量: {}", REPORT_ENT_IDS, limit);
+            List<RawOreOrderExecutionDto> rawOreRows = transportRemoteService.listRawOreOrderExecutionDisplay(REPORT_ENT_IDS, limit);
+
+            // 2. 判空处理:若无数据,直接返回空列表
+            if (CollectionUtils.isEmpty(rawOreRows)) {
+                log.info("未查询到原矿物流订单执行数据");
+                return Collections.emptyList();
+            }
+            log.debug("查询到原矿物流订单原始数据条数: {}", rawOreRows.size());
+
+            // 3. 数据转换:将 DTO 转换为 VO
+            List<OrderExecutionDisplayVo> result = rawOreRows.stream()
+                    .map(this::buildRawOreOrderExecutionDisplayVo)
+                    .toList();
+            
+            log.info("原矿物流订单执行展示数据构建完成,最终返回条数: {}", result.size());
+            return result;
+        } catch (Exception e) {
+            // 4. 异常处理:记录错误日志并返回空列表,避免影响主流程(如贸易订单数据的展示)
+            log.error("构建原矿物流订单执行展示数据失败", e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * 构建原矿物流订单执行展示视图对象。
+     * <p>
+     * 该方法将远程服务返回的原矿物流订单执行数据 DTO 转换为前端展示所需的 VO 对象。
+     * 主要包含以下逻辑:
+     * 1. 处理空值:将计划量和已装载量中的 null 值转换为 BigDecimal.ZERO。
+     * 2. 计算完成率:避免除以零错误,保留两位小数。
+     * 3. 数据格式化:计划吨数取整,已装载吨数保留两位小数,趟次处理 null 值。
+     * </p>
+     *
+     * @param row 原矿物流订单执行数据 DTO
+     * @return 原矿物流订单执行展示视图对象
+     */
+    private OrderExecutionDisplayVo buildRawOreOrderExecutionDisplayVo(RawOreOrderExecutionDto row) {
+        log.debug("开始构建原矿物流订单执行展示VO,物流单号: {}", row != null ? row.getLogisticsOrderNo() : "null");
+
+        // 1. 获取并处理计划量,防止空指针
+        BigDecimal plannedRaw = row.getPlannedAmount() != null ? row.getPlannedAmount() : BigDecimal.ZERO;
+        // 2. 获取并处理已装载净重总和,防止空指针
+        BigDecimal loadedRaw = row.getNetWeightSum() != null ? row.getNetWeightSum() : BigDecimal.ZERO;
+
+        // 3. 计算完成率百分比
+        BigDecimal rate;
+        if (plannedRaw.compareTo(BigDecimal.ZERO) <= 0) {
+            // 如果计划量为0或负数,防止除以零,完成率设为0
+            rate = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+            log.debug("物流单号: {} 计划量为0或负数,完成率设为0", row.getLogisticsOrderNo());
+        } else {
+            // 正常计算:(已装载量 / 计划量) * 100
+            // 中间计算保留10位精度以确保准确性,最终结果保留2位小数
+            rate = loadedRaw.divide(plannedRaw, 10, RoundingMode.HALF_UP)
+                    .multiply(BigDecimal.valueOf(100))
+                    .setScale(2, RoundingMode.HALF_UP);
+        }
+
+        // 4. 组装返回对象
+        OrderExecutionDisplayVo vo = new OrderExecutionDisplayVo();
+        // 设置物流订单号
+        vo.setOrderNo(row.getLogisticsOrderNo());
+        // 设置计划吨数:四舍五入取整,且最小为0
+        vo.setPlannedTons(Math.max(0, plannedRaw.setScale(0, RoundingMode.HALF_UP).intValue()));
+        // 设置已装载吨数:保留两位小数
+        vo.setLoadedTons(loadedRaw.setScale(2, RoundingMode.HALF_UP));
+        // 设置运输趟次:若为null则默认为0
+        vo.setTripCount(row.getTripCount() == null ? 0 : row.getTripCount());
+        // 设置完成率百分比
+        vo.setCompletionRatePercent(rate);
+
+        log.debug("原矿物流订单执行展示VO构建完成,单号: {}, 计划吨: {}, 已装吨: {}, 趟次: {}, 完成率: {}%",
+                vo.getOrderNo(), vo.getPlannedTons(), vo.getLoadedTons(), vo.getTripCount(), vo.getCompletionRatePercent());
+
+        return vo;
+    }
+
     public List<ContractLogisticsVO> queryAddContractList(ContractLogisticsQueryParam param) {
     public List<ContractLogisticsVO> queryAddContractList(ContractLogisticsQueryParam param) {
         KwoTradeOrder kwoTradeOrder = kwoTradeOrderMapper.selectOne(new LambdaQueryWrapper<KwoTradeOrder>().eq(KwoTradeOrder::getId, param.getTradeOrderId()).eq(KwoTradeOrder::getDelFlag, 0));
         KwoTradeOrder kwoTradeOrder = kwoTradeOrderMapper.selectOne(new LambdaQueryWrapper<KwoTradeOrder>().eq(KwoTradeOrder::getId, param.getTradeOrderId()).eq(KwoTradeOrder::getDelFlag, 0));
         if (Objects.isNull(kwoTradeOrder)) {
         if (Objects.isNull(kwoTradeOrder)) {

+ 208 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/dubbo/TransportServiceImpl.java

@@ -43,6 +43,7 @@ import com.sckw.system.api.RemoteSystemService;
 import com.sckw.system.api.model.dto.res.SysDictResDto;
 import com.sckw.system.api.model.dto.res.SysDictResDto;
 import com.sckw.transport.api.dubbo.TransportRemoteService;
 import com.sckw.transport.api.dubbo.TransportRemoteService;
 import com.sckw.transport.api.model.dto.AcceptCarriageLogisticsOrderDto;
 import com.sckw.transport.api.model.dto.AcceptCarriageLogisticsOrderDto;
+import com.sckw.transport.api.model.dto.RawOreOrderExecutionDto;
 import com.sckw.transport.api.model.dto.TradeOrderWaybillAggDto;
 import com.sckw.transport.api.model.dto.TradeOrderWaybillAggDto;
 import com.sckw.transport.api.model.dto.AccountCheckingBindDTO;
 import com.sckw.transport.api.model.dto.AccountCheckingBindDTO;
 import com.sckw.transport.api.model.dto.RWaybillOrderDto;
 import com.sckw.transport.api.model.dto.RWaybillOrderDto;
@@ -1886,6 +1887,213 @@ public class TransportServiceImpl implements TransportRemoteService {
         return result;
         return result;
     }
     }
 
 
+    /**
+     * 查询原矿物流订单执行展示列表
+     * <p>
+     * 根据上报企业ID集合,查询关联的物流订单及其已完成的运单执行情况(趟次、净重等)。
+     * 主要用于监控原矿运输任务的实时执行进度。
+     * </p>
+     *
+     * @param entIds 上报企业ID集合
+     * @param limit  查询数量限制
+     * @return 原矿订单执行展示DTO列表
+     */
+    @Override
+    public List<RawOreOrderExecutionDto> listRawOreOrderExecutionDisplay(List<Long> entIds, int limit) {
+        log.info("开始查询原矿物流订单执行展示数据, entIds={}, limit={}", entIds, limit);
+
+        // 1. 参数校验
+        if (CollectionUtils.isEmpty(entIds) || limit <= 0) {
+            log.warn("查询原矿订单执行展示参数无效, entIds为空或limit<=0");
+            return Collections.emptyList();
+        }
+
+        // 2. 根据上报企业ID查询关联的物流订单单元信息
+        List<KwtLogisticsOrderUnit> units = logisticsOrderUnitRepository.queryByReportEntIds(entIds);
+        if (CollectionUtils.isEmpty(units)) {
+            log.debug("未找到上报企业关联的物流订单单元, entIds={}", entIds);
+            return Collections.emptyList();
+        }
+
+        // 3. 提取并去重物流订单ID
+        List<Long> logisticsOrderIds = units.stream()
+                .filter(Objects::nonNull)
+                .map(KwtLogisticsOrderUnit::getLOrderId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .toList();
+
+        if (CollectionUtils.isEmpty(logisticsOrderIds)) {
+            log.debug("从物流订单单元中未提取到有效的物流订单ID");
+            return Collections.emptyList();
+        }
+        log.debug("提取到待查询的物流订单ID数量: {}", logisticsOrderIds.size());
+
+        // 4. 查询物流订单主表信息
+        // 筛选条件:未删除、订单类型为原矿(1)、排除已结算/退回/拒绝/取消的状态、按更新时间倒序、限制条数
+        List<KwtLogisticsOrder> logisticsOrders = logisticsOrderRepository.list(
+                Wrappers.<KwtLogisticsOrder>lambdaQuery()
+                        .select(KwtLogisticsOrder::getId, KwtLogisticsOrder::getLOrderNo,
+                                KwtLogisticsOrder::getAmount, KwtLogisticsOrder::getUpdateTime)
+                        .eq(KwtLogisticsOrder::getDelFlag, Global.NO)
+                        .eq(KwtLogisticsOrder::getOrderType, 1) // 1代表原矿订单
+                        .in(KwtLogisticsOrder::getId, logisticsOrderIds)
+                        .notIn(KwtLogisticsOrder::getStatus, Arrays.asList(
+                                LogisticsOrderEnum.HAVE_ALREADY_SETTLED.getCode(), // 已结算
+                                LogisticsOrderEnum.SEND_BACK.getCode(),            // 退回
+                                LogisticsOrderEnum.REJECT_ORDER.getCode(),         // 拒绝
+                                LogisticsOrderEnum.CANCEL_ORDER.getCode()))        // 取消
+                        .orderByDesc(KwtLogisticsOrder::getUpdateTime)
+                        .last("LIMIT " + limit));
+
+        if (CollectionUtils.isEmpty(logisticsOrders)) {
+            log.info("未查询到符合条件的原矿物流订单, logisticsOrderIds={}", logisticsOrderIds);
+            return Collections.emptyList();
+        }
+        log.info("查询到符合条件的原矿物流订单数量: {}", logisticsOrders.size());
+
+        // 5. 提取物流订单ID,用于查询关联的已完成运单
+        List<Long> rawOreLogisticsOrderIds = logisticsOrders.stream()
+                .map(KwtLogisticsOrder::getId)
+                .filter(Objects::nonNull)
+                .toList();
+
+        // 6. 查询这些物流订单下状态为“已完成”的运单
+        List<KwtWaybillOrder> waybills = waybillOrderRepository.queryCompletedWaybillOrdersByLogisticsOrderIds(
+                CarWaybillV1Enum.COMPLETED.getCode(), rawOreLogisticsOrderIds);
+        
+        // 7. 将运单按物流订单ID分组,方便后续统计每个订单下的运单列表
+        Map<Long, List<KwtWaybillOrder>> waybillsByLogisticsOrder = waybills.stream()
+                .filter(wo -> wo.getLOrderId() != null && wo.getId() != null)
+                .collect(Collectors.groupingBy(KwtWaybillOrder::getLOrderId));
+        log.debug("查询到已完成运单数量: {}, 涉及物流订单数: {}", waybills.size(), waybillsByLogisticsOrder.size());
+
+        // 8. 提取所有相关运单ID,批量查询子任务以计算净重
+        Set<Long> waybillIds = waybills.stream()
+                .map(KwtWaybillOrder::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        
+        // 构建运单ID到净重总和的映射关系
+        Map<Long, BigDecimal> netByWaybill = buildNetWeightByWaybill(waybillIds);
+
+        // 9. 组装返回结果DTO
+        List<RawOreOrderExecutionDto> result = logisticsOrders.stream()
+                .map(order -> buildRawOreOrderExecutionDto(order, waybillsByLogisticsOrder, netByWaybill))
+                .toList();
+        
+        log.info("原矿物流订单执行展示数据组装完成, 返回数量: {}", result.size());
+        return result;
+    }
+
+    /**
+     * 构建运单ID到净重总和的映射关系
+     * <p>
+     * 根据运单ID集合,查询对应的运单子任务,计算每个运单的总净重。
+     * 净重计算逻辑优先取卸货量,若无卸货量则取装货量。
+     * </p>
+     *
+     * @param waybillIds 运单ID集合
+     * @return Map<运单ID, 净重总和(吨)>
+     */
+    private Map<Long, BigDecimal> buildNetWeightByWaybill(Set<Long> waybillIds) {
+        // 1. 参数校验:如果运单ID集合为空,直接返回空Map,避免不必要的数据库查询
+        if (CollectionUtils.isEmpty(waybillIds)) {
+            log.debug("构建运单净重映射:运单ID集合为空");
+            return Collections.emptyMap();
+        }
+        
+        log.debug("开始构建运单净重映射,运单数量: {}", waybillIds.size());
+
+        // 2. 批量查询运单子任务
+        // 查询条件:运单ID在指定集合中,且未删除
+        List<KwtWaybillOrderSubtask> subtasks = waybillOrderSubtaskRepository.list(
+                Wrappers.<KwtWaybillOrderSubtask>lambdaQuery()
+                        .in(KwtWaybillOrderSubtask::getWOrderId, waybillIds)
+                        .eq(KwtWaybillOrderSubtask::getDelFlag, Global.NO));
+        
+        if (CollectionUtils.isEmpty(subtasks)) {
+            log.debug("未查询到任何运单子任务,运单IDs: {}", waybillIds);
+        } else {
+            log.debug("查询到运单子任务数量: {}", subtasks.size());
+        }
+
+        // 3. 将子任务按运单ID分组
+        // 过滤掉运单ID为空的异常数据,防止分组Key为null导致错误
+        Map<Long, List<KwtWaybillOrderSubtask>> subByWaybill = subtasks.stream()
+                .filter(st -> st.getWOrderId() != null)
+                .collect(Collectors.groupingBy(KwtWaybillOrderSubtask::getWOrderId));
+
+        // 4. 计算每个运单的总净重并构建结果Map
+        // 遍历原始运单ID集合,确保所有输入的ID都在结果Map中有对应项(即使净重为0)
+        Map<Long, BigDecimal> result = waybillIds.stream()
+                .collect(Collectors.toMap(
+                        Function.identity(), // Key: 运单ID
+                        wid -> {
+                            // 获取该运单下的所有子任务列表,若不存在则为空列表
+                            List<KwtWaybillOrderSubtask> waybillSubtasks = subByWaybill.getOrDefault(wid, Collections.emptyList());
+                            
+                            // 累加子任务的净重
+                            BigDecimal totalNetWeight = waybillSubtasks.stream()
+                                    .map(TransportServiceImpl::waybillSubtaskNetWeightTon) // 计算单个子任务净重
+                                    .reduce(BigDecimal.ZERO, BigDecimal::add); // 求和
+                            
+                            return totalNetWeight;
+                        }
+                ));
+        
+        log.debug("运单净重映射构建完成,结果大小: {}", result.size());
+        return result;
+    }
+
+    /**
+     * 构建原矿订单执行展示DTO对象
+     * <p>
+     * 根据物流订单主表信息、关联的已完成运单列表以及运单净重映射,
+     * 组装原矿订单的执行进度数据,包括计划量、实际趟次、累计净重等。
+     * </p>
+     *
+     * @param order                 物流订单主表对象
+     * @param waybillsByLogisticsOrder 物流订单ID到已完成运单列表的映射
+     * @param netByWaybill          运单ID到净重总和的映射
+     * @return 原矿订单执行展示DTO
+     */
+    private RawOreOrderExecutionDto buildRawOreOrderExecutionDto(KwtLogisticsOrder order,
+                                                                 Map<Long, List<KwtWaybillOrder>> waybillsByLogisticsOrder,
+                                                                 Map<Long, BigDecimal> netByWaybill) {
+        // 1. 获取当前物流订单关联的所有已完成运单
+        Long logisticsOrderId = order.getId();
+        List<KwtWaybillOrder> orderWaybills = waybillsByLogisticsOrder.getOrDefault(logisticsOrderId, Collections.emptyList());
+        
+        // 2. 计算该物流订单下所有已完成运单的累计净重
+        // 遍历运单ID,从预计算的净重映射中获取每个运单的净重并求和
+        BigDecimal netWeightSum = orderWaybills.stream()
+                .map(KwtWaybillOrder::getId)
+                .map(waybillId -> {
+                    BigDecimal weight = netByWaybill.getOrDefault(waybillId, BigDecimal.ZERO);
+                    log.debug("运单ID: {}, 净重: {}", waybillId, weight);
+                    return weight;
+                })
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+        log.debug("物流订单ID: {}, 关联运单数: {}, 累计净重: {}", logisticsOrderId, orderWaybills.size(), netWeightSum);
+
+        // 3. 组装返回DTO
+        RawOreOrderExecutionDto dto = new RawOreOrderExecutionDto();
+        dto.setLogisticsOrderId(logisticsOrderId);
+        dto.setLogisticsOrderNo(order.getLOrderNo());
+        // 计划运输量
+        dto.setPlannedAmount(order.getAmount());
+        // 实际完成趟次(即已完成运单的数量)
+        dto.setTripCount(orderWaybills.size());
+        // 累计完成净重
+        dto.setNetWeightSum(netWeightSum);
+        // 订单最后更新时间
+        dto.setUpdateTime(order.getUpdateTime());
+        
+        return dto;
+    }
+
     private static BigDecimal waybillSubtaskNetWeightTon(KwtWaybillOrderSubtask st) {
     private static BigDecimal waybillSubtaskNetWeightTon(KwtWaybillOrderSubtask st) {
         if (st == null) {
         if (st == null) {
             return BigDecimal.ZERO;
             return BigDecimal.ZERO;

+ 24 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/repository/KwtLogisticsOrderUnitRepository.java

@@ -4,9 +4,11 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.sckw.transport.dao.KwtLogisticsOrderUnitMapper;
 import com.sckw.transport.dao.KwtLogisticsOrderUnitMapper;
 import com.sckw.transport.model.KwtLogisticsOrderUnit;
 import com.sckw.transport.model.KwtLogisticsOrderUnit;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.stereotype.Repository;
 import org.springframework.stereotype.Repository;
 
 
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Set;
 import java.util.Set;
 
 
@@ -65,6 +67,28 @@ public class KwtLogisticsOrderUnitRepository extends ServiceImpl<KwtLogisticsOrd
                 .in(KwtLogisticsOrderUnit::getLOrderId, lOrderIds));
                 .in(KwtLogisticsOrderUnit::getLOrderId, lOrderIds));
     }
     }
 
 
+    /**
+     * 根据报表企业范围查询对应物流订单单位。
+     * <p>
+     * 该报表为免登录公开展示,企业范围由业务固定指定:
+     * 查询单位企业在指定企业集合内,或顶级企业在指定企业集合内的物流订单。
+     * </p>
+     *
+     * @param entIds 报表企业ID集合
+     * @return 报表企业范围内的物流订单单位集合
+     */
+    public List<KwtLogisticsOrderUnit> queryByReportEntIds(List<Long> entIds) {
+        if (CollectionUtils.isEmpty(entIds)) {
+            return Collections.emptyList();
+        }
+        return list(Wrappers.<KwtLogisticsOrderUnit>lambdaQuery()
+                .select(KwtLogisticsOrderUnit::getId, KwtLogisticsOrderUnit::getLOrderId)
+                .eq(KwtLogisticsOrderUnit::getDelFlag, 0)
+                .and(wrapper -> wrapper.in(KwtLogisticsOrderUnit::getEntId, entIds)
+                        .or()
+                        .in(KwtLogisticsOrderUnit::getTopEntId, entIds)));
+    }
+
     public List<KwtLogisticsOrderUnit> queryListByLOrderId(Long lOrderId) {
     public List<KwtLogisticsOrderUnit> queryListByLOrderId(Long lOrderId) {
         return list(Wrappers.<KwtLogisticsOrderUnit>lambdaQuery()
         return list(Wrappers.<KwtLogisticsOrderUnit>lambdaQuery()
                         .eq(KwtLogisticsOrderUnit::getDelFlag, 0)
                         .eq(KwtLogisticsOrderUnit::getDelFlag, 0)

+ 19 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/repository/KwtWaybillOrderRepository.java

@@ -256,6 +256,25 @@ public class KwtWaybillOrderRepository extends ServiceImpl<KwtWaybillOrderMapper
                 .orderByDesc(KwtWaybillOrder::getUpdateTime));
                 .orderByDesc(KwtWaybillOrder::getUpdateTime));
     }
     }
 
 
+    /**
+     * 根据物流订单ID查询已完成运单基础统计数据。
+     * <p>
+     * 仅返回顶部统计需要的字段,避免加载无关列。
+     * </p>
+     *
+     * @param status 已完成状态编码
+     * @param logisticsOrderIds 物流订单ID集合
+     * @return 已完成运单列表
+     */
+    public List<KwtWaybillOrder> queryCompletedWaybillOrdersByLogisticsOrderIds(Integer status, List<Long> logisticsOrderIds) {
+        return list(Wrappers.<KwtWaybillOrder>lambdaQuery()
+                .select(KwtWaybillOrder::getId, KwtWaybillOrder::getLOrderId, KwtWaybillOrder::getUpdateTime)
+                .eq(KwtWaybillOrder::getDelFlag, 0)
+                .eq(KwtWaybillOrder::getStatus, status)
+                .in(CollectionUtils.isNotEmpty(logisticsOrderIds), KwtWaybillOrder::getLOrderId, logisticsOrderIds)
+                .orderByDesc(KwtWaybillOrder::getUpdateTime));
+    }
+
     /**
     /**
      * 分页查询地图车辆列表(进行中任务)
      * 分页查询地图车辆列表(进行中任务)
      * @param startDate 开始日期
      * @param startDate 开始日期

+ 13 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/repository/KwtWaybillOrderSubtaskRepository.java

@@ -5,12 +5,14 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.sckw.core.model.base.BaseModel;
 import com.sckw.core.model.base.BaseModel;
+import com.sckw.core.model.enums.CarWaybillV1Enum;
 import com.sckw.transport.dao.KwtWaybillOrderSubtaskMapper;
 import com.sckw.transport.dao.KwtWaybillOrderSubtaskMapper;
 import com.sckw.transport.model.KwtWaybillOrderSubtask;
 import com.sckw.transport.model.KwtWaybillOrderSubtask;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.stereotype.Repository;
 import org.springframework.stereotype.Repository;
 
 
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
+import java.util.Collections;
 import java.util.List;
 import java.util.List;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.Set;
 import java.util.Set;
@@ -59,6 +61,17 @@ public class KwtWaybillOrderSubtaskRepository extends ServiceImpl<KwtWaybillOrde
                 .in(KwtWaybillOrderSubtask::getLOrderId,logIds));
                 .in(KwtWaybillOrderSubtask::getLOrderId,logIds));
     }
     }
 
 
+    public List<KwtWaybillOrderSubtask> queryValidByLogisticsOrderIds(List<Long> logIds) {
+        if (CollectionUtils.isEmpty(logIds)) {
+            return Collections.emptyList();
+        }
+        return list(Wrappers.<KwtWaybillOrderSubtask>lambdaQuery()
+                .select(KwtWaybillOrderSubtask::getWOrderId, KwtWaybillOrderSubtask::getStatus, KwtWaybillOrderSubtask::getUpdateTime)
+                .eq(BaseModel::getDelFlag, 0)
+                .in(KwtWaybillOrderSubtask::getLOrderId, logIds)
+                .ne(KwtWaybillOrderSubtask::getStatus, CarWaybillV1Enum.CANCELLED.getCode()));
+    }
+
     public List<KwtWaybillOrderSubtask> queryByLogId(Long logOrderId) {
     public List<KwtWaybillOrderSubtask> queryByLogId(Long logOrderId) {
         return list(Wrappers.<KwtWaybillOrderSubtask>lambdaQuery()
         return list(Wrappers.<KwtWaybillOrderSubtask>lambdaQuery()
                 .eq(BaseModel::getDelFlag,0)
                 .eq(BaseModel::getDelFlag,0)

+ 46 - 11
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/TransportStatisticsService.java

@@ -3,11 +3,13 @@ package com.sckw.transport.service;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.date.DateUtil;
 import com.sckw.core.common.enums.enums.DictEnum;
 import com.sckw.core.common.enums.enums.DictEnum;
 import com.sckw.core.model.enums.CarWaybillV1Enum;
 import com.sckw.core.model.enums.CarWaybillV1Enum;
-import com.sckw.transport.model.KwtLogisticsOrder;
 import com.sckw.transport.api.model.vo.TransportTopStatisticsVo;
 import com.sckw.transport.api.model.vo.TransportTopStatisticsVo;
+import com.sckw.transport.model.KwtLogisticsOrder;
+import com.sckw.transport.model.KwtLogisticsOrderUnit;
 import com.sckw.transport.model.KwtWaybillOrder;
 import com.sckw.transport.model.KwtWaybillOrder;
 import com.sckw.transport.model.KwtWaybillOrderSubtask;
 import com.sckw.transport.model.KwtWaybillOrderSubtask;
 import com.sckw.transport.repository.KwtLogisticsOrderRepository;
 import com.sckw.transport.repository.KwtLogisticsOrderRepository;
+import com.sckw.transport.repository.KwtLogisticsOrderUnitRepository;
 import com.sckw.transport.repository.KwtWaybillOrderRepository;
 import com.sckw.transport.repository.KwtWaybillOrderRepository;
 import com.sckw.transport.repository.KwtWaybillOrderSubtaskRepository;
 import com.sckw.transport.repository.KwtWaybillOrderSubtaskRepository;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -38,7 +40,20 @@ import java.util.stream.Collectors;
 @RequiredArgsConstructor
 @RequiredArgsConstructor
 public class TransportStatisticsService {
 public class TransportStatisticsService {
 
 
+    /**
+     * 公开报表固定统计企业范围。
+     * <p>
+     * 当前接口免登录访问,企业范围按需求固定为图片中的父企业及两个子企业。
+     * </p>
+     */
+    private static final List<Long> REPORT_ENT_IDS = List.of(
+            538038314096136193L,
+            538039617157337089L,
+            538040297439891457L
+    );
+
     private final KwtLogisticsOrderRepository logisticsOrderRepository;
     private final KwtLogisticsOrderRepository logisticsOrderRepository;
+    private final KwtLogisticsOrderUnitRepository logisticsOrderUnitRepository;
     private final KwtWaybillOrderRepository waybillOrderRepository;
     private final KwtWaybillOrderRepository waybillOrderRepository;
     private final KwtWaybillOrderSubtaskRepository waybillOrderSubtaskRepository;
     private final KwtWaybillOrderSubtaskRepository waybillOrderSubtaskRepository;
 
 
@@ -60,10 +75,18 @@ public class TransportStatisticsService {
 
 
         log.info("查询顶部运输统计数据,todayStart={}, tomorrowStart={}", todayStart, tomorrowStart);
         log.info("查询顶部运输统计数据,todayStart={}, tomorrowStart={}", todayStart, tomorrowStart);
 
 
+        List<Long> logisticsOrderIds = queryReportLogisticsOrderIds();
+        if (logisticsOrderIds.isEmpty()) {
+            log.info("公开报表企业范围未查询到物流订单,entIds={}", REPORT_ENT_IDS);
+            return buildEmptyStatistics();
+        }
+        log.debug("公开报表企业范围匹配到 {} 个物流订单", logisticsOrderIds.size());
+
         List<KwtWaybillOrder> completedWaybillOrders = defaultList(
         List<KwtWaybillOrder> completedWaybillOrders = defaultList(
-                waybillOrderRepository.queryCompletedWaybillOrders(CarWaybillV1Enum.COMPLETED.getCode()));
+                waybillOrderRepository.queryCompletedWaybillOrdersByLogisticsOrderIds(
+                        CarWaybillV1Enum.COMPLETED.getCode(), logisticsOrderIds));
         if (completedWaybillOrders.isEmpty()) {
         if (completedWaybillOrders.isEmpty()) {
-            log.info("未查询到已完成运单,返回默认零值");
+            log.info("公开报表企业范围未查询到已完成运单,entIds={}", REPORT_ENT_IDS);
             return buildEmptyStatistics();
             return buildEmptyStatistics();
         }
         }
 
 
@@ -73,14 +96,7 @@ public class TransportStatisticsService {
                 .map(KwtWaybillOrder::getId)
                 .map(KwtWaybillOrder::getId)
                 .filter(Objects::nonNull)
                 .filter(Objects::nonNull)
                 .toList();
                 .toList();
-        // 提取已完成运单关联的物流订单ID,用于查询计费方式
-        List<Long> logisticsOrderIds = completedWaybillOrders.stream()
-                .filter(Objects::nonNull)
-                .map(KwtWaybillOrder::getLOrderId)
-                .filter(Objects::nonNull)
-                .distinct()
-                .toList();
-        log.debug("提取到 {} 个唯一的物流订单ID", logisticsOrderIds.size());
+        log.debug("公开报表企业范围匹配到 {} 个已完成运单", waybillOrderIds.size());
 
 
         // 批量查询物流订单信息
         // 批量查询物流订单信息
         List<KwtLogisticsOrder> logisticsOrders = logisticsOrderIds.isEmpty()
         List<KwtLogisticsOrder> logisticsOrders = logisticsOrderIds.isEmpty()
@@ -162,6 +178,25 @@ public class TransportStatisticsService {
         return amount == null ? BigDecimal.ZERO : amount;
         return amount == null ? BigDecimal.ZERO : amount;
     }
     }
 
 
+    /**
+     * 查询公开报表固定企业范围对应的物流订单ID。
+     * <p>
+     * 先从 kwt_logistics_order_unit 表按固定企业范围获取物流订单,
+     * 再进入后续运单统计逻辑。
+     * </p>
+     *
+     * @return 公开报表企业范围内的物流订单ID
+     */
+    private List<Long> queryReportLogisticsOrderIds() {
+        return defaultList(logisticsOrderUnitRepository.queryByReportEntIds(REPORT_ENT_IDS))
+                .stream()
+                .filter(Objects::nonNull)
+                .map(KwtLogisticsOrderUnit::getLOrderId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .toList();
+    }
+
     /**
     /**
      * 根据物流订单计费方式解析运单净重。
      * 根据物流订单计费方式解析运单净重。
      *
      *

+ 37 - 0
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/dashboard/RealtimeSalesVolumeService.java

@@ -9,10 +9,12 @@ import com.sckw.transport.api.model.vo.RealtimeSalesGoodsSeriesVo;
 import com.sckw.transport.api.model.vo.RealtimeSalesVolumeVo;
 import com.sckw.transport.api.model.vo.RealtimeSalesVolumeVo;
 import com.sckw.transport.model.KwtLogisticsOrder;
 import com.sckw.transport.model.KwtLogisticsOrder;
 import com.sckw.transport.model.KwtLogisticsOrderGoods;
 import com.sckw.transport.model.KwtLogisticsOrderGoods;
+import com.sckw.transport.model.KwtLogisticsOrderUnit;
 import com.sckw.transport.model.KwtWaybillOrder;
 import com.sckw.transport.model.KwtWaybillOrder;
 import com.sckw.transport.model.KwtWaybillOrderSubtask;
 import com.sckw.transport.model.KwtWaybillOrderSubtask;
 import com.sckw.transport.repository.KwtLogisticsOrderGoodsRepository;
 import com.sckw.transport.repository.KwtLogisticsOrderGoodsRepository;
 import com.sckw.transport.repository.KwtLogisticsOrderRepository;
 import com.sckw.transport.repository.KwtLogisticsOrderRepository;
+import com.sckw.transport.repository.KwtLogisticsOrderUnitRepository;
 import com.sckw.transport.repository.KwtWaybillOrderRepository;
 import com.sckw.transport.repository.KwtWaybillOrderRepository;
 import com.sckw.transport.repository.KwtWaybillOrderSubtaskRepository;
 import com.sckw.transport.repository.KwtWaybillOrderSubtaskRepository;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -68,10 +70,20 @@ public class RealtimeSalesVolumeService {
      */
      */
     private static final long TASK_END_MIN_EPOCH_MS = 946684800000L;
     private static final long TASK_END_MIN_EPOCH_MS = 946684800000L;
 
 
+    /**
+     * Fixed enterprise scope for the public BI dashboard.
+     */
+    private static final List<Long> REPORT_ENT_IDS = List.of(
+            538038314096136193L,
+            538039617157337089L,
+            538040297439891457L
+    );
+
     private final KwtWaybillOrderRepository waybillOrderRepository;
     private final KwtWaybillOrderRepository waybillOrderRepository;
     private final KwtWaybillOrderSubtaskRepository waybillOrderSubtaskRepository;
     private final KwtWaybillOrderSubtaskRepository waybillOrderSubtaskRepository;
     private final KwtLogisticsOrderGoodsRepository logisticsOrderGoodsRepository;
     private final KwtLogisticsOrderGoodsRepository logisticsOrderGoodsRepository;
     private final KwtLogisticsOrderRepository logisticsOrderRepository;
     private final KwtLogisticsOrderRepository logisticsOrderRepository;
+    private final KwtLogisticsOrderUnitRepository logisticsOrderUnitRepository;
 
 
     /**
     /**
      * 构建实时销量视图对象
      * 构建实时销量视图对象
@@ -104,10 +116,18 @@ public class RealtimeSalesVolumeService {
         log.debug("生成时间标签: {}", vo.getTimeLabels());
         log.debug("生成时间标签: {}", vo.getTimeLabels());
 
 
         // 4. 查询时间窗口内状态为“已完成”且未删除的运单
         // 4. 查询时间窗口内状态为“已完成”且未删除的运单
+        Set<Long> reportLogisticsOrderIds = queryReportLogisticsOrderIds();
+        if (CollectionUtils.isEmpty(reportLogisticsOrderIds)) {
+            log.info("未找到匹配固定看板企业范围的物流订单,企业ID列表={}", REPORT_ENT_IDS);
+            vo.setSeries(List.of());
+            return vo;
+        }
+
         List<KwtWaybillOrder> waybills = waybillOrderRepository.list(
         List<KwtWaybillOrder> waybills = waybillOrderRepository.list(
                 Wrappers.<KwtWaybillOrder>lambdaQuery()
                 Wrappers.<KwtWaybillOrder>lambdaQuery()
                         .eq(KwtWaybillOrder::getStatus, CarWaybillV1Enum.COMPLETED.getCode())
                         .eq(KwtWaybillOrder::getStatus, CarWaybillV1Enum.COMPLETED.getCode())
                         .eq(KwtWaybillOrder::getDelFlag, Global.NO)
                         .eq(KwtWaybillOrder::getDelFlag, Global.NO)
+                        .in(KwtWaybillOrder::getLOrderId, reportLogisticsOrderIds)
                         .ge(KwtWaybillOrder::getUpdateTime, windowStart)
                         .ge(KwtWaybillOrder::getUpdateTime, windowStart)
                         .lt(KwtWaybillOrder::getUpdateTime, windowEnd));
                         .lt(KwtWaybillOrder::getUpdateTime, windowEnd));
 
 
@@ -166,6 +186,23 @@ public class RealtimeSalesVolumeService {
      * @param goodsByLogOrder  物流订单ID到主要商品的映射
      * @param goodsByLogOrder  物流订单ID到主要商品的映射
      * @return 包含贡献信息的流,如果无效则返回空流
      * @return 包含贡献信息的流,如果无效则返回空流
      */
      */
+    /**
+     * Query logistics order ids that belong to the fixed public dashboard enterprise scope.
+     *
+     * @return logistics order id set
+     */
+    private Set<Long> queryReportLogisticsOrderIds() {
+        List<KwtLogisticsOrderUnit> units = logisticsOrderUnitRepository.queryByReportEntIds(REPORT_ENT_IDS);
+        if (CollectionUtils.isEmpty(units)) {
+            return Set.of();
+        }
+        return units.stream()
+                .filter(Objects::nonNull)
+                .map(KwtLogisticsOrderUnit::getLOrderId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+    }
+
     private static Stream<WaybillContribution> contributionStream(
     private static Stream<WaybillContribution> contributionStream(
             KwtWaybillOrder wo,
             KwtWaybillOrder wo,
             Date windowStart,
             Date windowStart,

+ 55 - 8
sckw-modules/sckw-transport/src/main/java/com/sckw/transport/service/dashboard/SubtaskCapacityAnalysisService.java

@@ -1,17 +1,17 @@
 package com.sckw.transport.service.dashboard;
 package com.sckw.transport.service.dashboard;
 
 
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.sckw.core.model.constant.Global;
 import com.sckw.core.model.constant.NumberConstant;
 import com.sckw.core.model.constant.NumberConstant;
 import com.sckw.core.model.enums.CarWaybillV1Enum;
 import com.sckw.core.model.enums.CarWaybillV1Enum;
 import com.sckw.core.model.enums.GatekeeperStatusEnum;
 import com.sckw.core.model.enums.GatekeeperStatusEnum;
 import com.sckw.core.utils.CollectionUtils;
 import com.sckw.core.utils.CollectionUtils;
 import com.sckw.transport.api.model.vo.CurrentCapacityAnalysisVo;
 import com.sckw.transport.api.model.vo.CurrentCapacityAnalysisVo;
 import com.sckw.transport.model.KwtGatekeeperWaybillOrder;
 import com.sckw.transport.model.KwtGatekeeperWaybillOrder;
+import com.sckw.transport.model.KwtLogisticsOrderUnit;
 import com.sckw.transport.model.KwtWaybillOrder;
 import com.sckw.transport.model.KwtWaybillOrder;
 import com.sckw.transport.model.KwtWaybillOrderAddress;
 import com.sckw.transport.model.KwtWaybillOrderAddress;
 import com.sckw.transport.model.KwtWaybillOrderSubtask;
 import com.sckw.transport.model.KwtWaybillOrderSubtask;
 import com.sckw.transport.repository.KwtGatekeeperWaybillOrderRepository;
 import com.sckw.transport.repository.KwtGatekeeperWaybillOrderRepository;
+import com.sckw.transport.repository.KwtLogisticsOrderUnitRepository;
 import com.sckw.transport.repository.KwtWaybillOrderAddressRepository;
 import com.sckw.transport.repository.KwtWaybillOrderAddressRepository;
 import com.sckw.transport.repository.KwtWaybillOrderRepository;
 import com.sckw.transport.repository.KwtWaybillOrderRepository;
 import com.sckw.transport.repository.KwtWaybillOrderSubtaskRepository;
 import com.sckw.transport.repository.KwtWaybillOrderSubtaskRepository;
@@ -38,10 +38,23 @@ import java.util.stream.Stream;
 @RequiredArgsConstructor
 @RequiredArgsConstructor
 public class SubtaskCapacityAnalysisService {
 public class SubtaskCapacityAnalysisService {
 
 
+    /**
+     * 公开报表固定统计企业范围。
+     * <p>
+     * 当前接口免登录访问,企业范围按需求固定为图片中的父企业及两个子企业。
+     * </p>
+     */
+    private static final List<Long> REPORT_ENT_IDS = List.of(
+            538038314096136193L,
+            538039617157337089L,
+            538040297439891457L
+    );
+
     private final KwtWaybillOrderSubtaskRepository waybillOrderSubtaskRepository;
     private final KwtWaybillOrderSubtaskRepository waybillOrderSubtaskRepository;
     private final KwtWaybillOrderAddressRepository waybillOrderAddressRepository;
     private final KwtWaybillOrderAddressRepository waybillOrderAddressRepository;
     private final KwtGatekeeperWaybillOrderRepository gatekeeperWaybillOrderRepository;
     private final KwtGatekeeperWaybillOrderRepository gatekeeperWaybillOrderRepository;
     private final KwtWaybillOrderRepository waybillOrderRepository;
     private final KwtWaybillOrderRepository waybillOrderRepository;
+    private final KwtLogisticsOrderUnitRepository logisticsOrderUnitRepository;
 
 
     /**
     /**
      * 执行当前运力分析
      * 执行当前运力分析
@@ -54,18 +67,30 @@ public class SubtaskCapacityAnalysisService {
      * @return 当前运力分析结果 VO
      * @return 当前运力分析结果 VO
      */
      */
     public CurrentCapacityAnalysisVo analyze() {
     public CurrentCapacityAnalysisVo analyze() {
+        CurrentCapacityAnalysisVo vo = new CurrentCapacityAnalysisVo();
         log.info("=== 开始执行当前运力分析 ===");
         log.info("=== 开始执行当前运力分析 ===");
 
 
         // 1. 查询所有有效的子任务(排除已删除和已取消的)
         // 1. 查询所有有效的子任务(排除已删除和已取消的)
         // 仅查询必要字段以减少内存占用:运单ID、状态、更新时间
         // 仅查询必要字段以减少内存占用:运单ID、状态、更新时间
         log.debug("步骤1: 查询有效子任务...");
         log.debug("步骤1: 查询有效子任务...");
-        List<KwtWaybillOrderSubtask> subtasks = waybillOrderSubtaskRepository.list(
-                Wrappers.<KwtWaybillOrderSubtask>lambdaQuery()
-                        .select(KwtWaybillOrderSubtask::getWOrderId, KwtWaybillOrderSubtask::getStatus, KwtWaybillOrderSubtask::getUpdateTime)
-                        .eq(KwtWaybillOrderSubtask::getDelFlag, Global.NO)
-                        .ne(KwtWaybillOrderSubtask::getStatus, CarWaybillV1Enum.CANCELLED.getCode()));
+        List<Long> logisticsOrderIds = queryReportLogisticsOrderIds();
+        if (CollectionUtils.isEmpty(logisticsOrderIds)) {
+            log.warn("公开报表企业范围未查询到物流订单,返回空结果,entIds={}", REPORT_ENT_IDS);
+            vo.setPendingEntryCount(0);
+            vo.setLoadingOperationCount(0);
+            vo.setDeliveringCount(0);
+            return vo;
+        }
+        log.debug("步骤1.0: 公开报表企业范围匹配到 {} 个物流订单", logisticsOrderIds.size());
 
 
-        CurrentCapacityAnalysisVo vo = new CurrentCapacityAnalysisVo();
+        // 查询运单子任务列表,仅获取关键状态字段以优化性能
+        // 过滤条件:
+        // 1. 未删除 (delFlag = 0)
+        // 2. 属于当前报表企业范围内的物流订单
+        // 3. 状态不为“已取消”
+        List<KwtWaybillOrderSubtask> subtasks = waybillOrderSubtaskRepository.queryValidByLogisticsOrderIds(logisticsOrderIds);
+
+        // 若未查询到任何有效子任务,直接返回空统计结果
         if (CollectionUtils.isEmpty(subtasks)) {
         if (CollectionUtils.isEmpty(subtasks)) {
             log.warn("未查询到有效子任务,返回空结果");
             log.warn("未查询到有效子任务,返回空结果");
             vo.setPendingEntryCount(0);
             vo.setPendingEntryCount(0);
@@ -178,6 +203,28 @@ public class SubtaskCapacityAnalysisService {
                 && status < CarWaybillV1Enum.COMPLETION_LOADING.getCode();
                 && status < CarWaybillV1Enum.COMPLETION_LOADING.getCode();
     }
     }
 
 
+    /**
+     * 查询公开报表固定企业范围对应的物流订单ID。
+     * <p>
+     * 先从 kwt_logistics_order_unit 表按固定企业范围获取物流订单,
+     * 再进入后续子任务运力分析逻辑。
+     * </p>
+     *
+     * @return 公开报表企业范围内的物流订单ID
+     */
+    private List<Long> queryReportLogisticsOrderIds() {
+        List<KwtLogisticsOrderUnit> units = logisticsOrderUnitRepository.queryByReportEntIds(REPORT_ENT_IDS);
+        if (CollectionUtils.isEmpty(units)) {
+            return Collections.emptyList();
+        }
+        return units.stream()
+                .filter(Objects::nonNull)
+                .map(KwtLogisticsOrderUnit::getLOrderId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .toList();
+    }
+
     /**
     /**
      * 分类单个运单的状态
      * 分类单个运单的状态
      * <p>
      * <p>