Преглед изворни кода

[insert] 日统计逻辑开发

miajio пре 1 месец
родитељ
комит
dd821bc9f2
30 измењених фајлова са 1390 додато и 43 уклоњено
  1. 13 4
      pom.xml
  2. 4 0
      warehouse-admin-component/pom.xml
  3. 29 0
      warehouse-admin-component/src/main/java/com/yr/warehouse/admin/component/apollo/ApolloConfigEvent.java
  4. 36 0
      warehouse-admin-component/src/main/java/com/yr/warehouse/admin/component/areastaff/AreaStaffComponent.java
  5. 39 0
      warehouse-admin-component/src/main/java/com/yr/warehouse/admin/component/driver/DriverDeviceSalesComponent.java
  6. 110 0
      warehouse-admin-component/src/main/java/com/yr/warehouse/admin/component/vo/OrgOperatorVo.java
  7. 8 0
      warehouse-admin-data/src/main/java/com/yr/warehouse/admin/common/menu/BoolEnum.java
  8. 62 0
      warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/data/DriverDeviceStockSnapshot.java
  9. 12 0
      warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/data/DriverGoodsLossRecord.java
  10. 34 0
      warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/mapper/DriverDeviceStockSnapshotMapper.java
  11. 16 0
      warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/mapper/DriverEquipmentLossRecordMapper.java
  12. 16 0
      warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/mapper/DriverGoodsLossRecordMapper.java
  13. 18 2
      warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/mapper/DriverOnRouteDetailLogMapper.java
  14. 32 0
      warehouse-admin-data/src/main/java/com/yr/warehouse/admin/replenish/vo/DriverGoodsOrderNumVo.java
  15. 72 0
      warehouse-admin-data/src/main/java/com/yr/warehouse/admin/statistics/mapper/BillingQuantityStatisticsMapper.java
  16. 33 0
      warehouse-admin-data/src/main/resources/mapper/driver/DriverDeviceStockSnapshotMapper.xml
  17. 31 12
      warehouse-admin-data/src/main/resources/mapper/driver/DriverEquipmentLossRecordMapper.xml
  18. 40 15
      warehouse-admin-data/src/main/resources/mapper/driver/DriverGoodsLossRecordMapper.xml
  19. 16 0
      warehouse-admin-data/src/main/resources/mapper/driver/DriverOnRouteDetailLogMapper.xml
  20. 118 0
      warehouse-admin-data/src/main/resources/mapper/statistics/BillingQuantityStatisticsMapper.xml
  21. 2 8
      warehouse-admin-data/src/main/test/com/supplychain/CodeGenerator.java
  22. 9 2
      warehouse-admin-event/src/main/java/com/yr/warehouse/admin/event/task/DriverCargoDamageStatisticsTask.java
  23. 42 0
      warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/context/TransactionAwareComponent.java
  24. 10 0
      warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/context/TransactionMethod.java
  25. 16 0
      warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/driver/DriverDeviceStockSnapshotService.java
  26. 20 0
      warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/driver/impl/DriverDeviceStockSnapshotServiceImpl.java
  27. 17 0
      warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/statistics/DriverStatistics.java
  28. 398 0
      warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/statistics/impl/DriverStatisticsImpl.java
  29. 109 0
      warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/statistics/logic/DriverStatisticsMergeLogic.java
  30. 28 0
      warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/statistics/mapstruct/DriverStatisticsMapStruct.java

+ 13 - 4
pom.xml

@@ -31,7 +31,8 @@
         <bluecat.common.version>1.1.0-SNAPSHOT</bluecat.common.version>
         <bluecat.common.version>1.1.0-SNAPSHOT</bluecat.common.version>
         <prism-auth-core.version>1.9.0-SNAPSHOT</prism-auth-core.version>
         <prism-auth-core.version>1.9.0-SNAPSHOT</prism-auth-core.version>
         <prism-auth-support.version>1.9.0-SNAPSHOT</prism-auth-support.version>
         <prism-auth-support.version>1.9.0-SNAPSHOT</prism-auth-support.version>
-        <yr-supply-chain-api.version>1.1.8-SNAPSHOT</yr-supply-chain-api.version>
+        <yr-supply-chain-api.version>1.2.1-SNAPSHOT</yr-supply-chain-api.version>
+        <yr-shopping-admin.version>1.0.3-SNAPSHOT</yr-shopping-admin.version>
         <dubbo.version>3.2.9</dubbo.version>
         <dubbo.version>3.2.9</dubbo.version>
         <nacos.version>2.2.4</nacos.version>
         <nacos.version>2.2.4</nacos.version>
     </properties>
     </properties>
@@ -141,6 +142,11 @@
                 <version>${prism-auth-support.version}</version>
                 <version>${prism-auth-support.version}</version>
             </dependency>
             </dependency>
             <dependency>
             <dependency>
+                <groupId>com.yr.shopping.admin</groupId>
+                <artifactId>shopping-admin-support</artifactId>
+                <version>${yr-shopping-admin.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>com.supplychain.support</groupId>
                 <groupId>com.supplychain.support</groupId>
                 <artifactId>yr-supply-chain-api</artifactId>
                 <artifactId>yr-supply-chain-api</artifactId>
                 <version>${yr-supply-chain-api.version}</version>
                 <version>${yr-supply-chain-api.version}</version>
@@ -209,7 +215,8 @@
                 <bluecat.common.version>1.1.0-SNAPSHOT</bluecat.common.version>
                 <bluecat.common.version>1.1.0-SNAPSHOT</bluecat.common.version>
                 <prism-auth-support.version>1.9.0-SNAPSHOT</prism-auth-support.version>
                 <prism-auth-support.version>1.9.0-SNAPSHOT</prism-auth-support.version>
                 <prism-auth-core.version>1.9.0-SNAPSHOT</prism-auth-core.version>
                 <prism-auth-core.version>1.9.0-SNAPSHOT</prism-auth-core.version>
-                <yr-supply-chain-api.version>1.1.8-SNAPSHOT</yr-supply-chain-api.version>
+                <yr-supply-chain-api.version>1.2.1-SNAPSHOT</yr-supply-chain-api.version>
+                <yr-shopping-admin.version>1.0.3-SNAPSHOT</yr-shopping-admin.version>
             </properties>
             </properties>
             <distributionManagement>
             <distributionManagement>
                 <snapshotRepository>
                 <snapshotRepository>
@@ -227,7 +234,8 @@
                 <bluecat.common.version>1.1.0-SNAPSHOT</bluecat.common.version>
                 <bluecat.common.version>1.1.0-SNAPSHOT</bluecat.common.version>
                 <prism-auth-support.version>1.9.0-SNAPSHOT</prism-auth-support.version>
                 <prism-auth-support.version>1.9.0-SNAPSHOT</prism-auth-support.version>
                 <prism-auth-core.version>1.9.0-SNAPSHOT</prism-auth-core.version>
                 <prism-auth-core.version>1.9.0-SNAPSHOT</prism-auth-core.version>
-                <yr-supply-chain-api.version>1.1.8-SNAPSHOT</yr-supply-chain-api.version>
+                <yr-supply-chain-api.version>1.2.1-SNAPSHOT</yr-supply-chain-api.version>
+                <yr-shopping-admin.version>1.0.3-SNAPSHOT</yr-shopping-admin.version>
             </properties>
             </properties>
             <distributionManagement>
             <distributionManagement>
                 <snapshotRepository>
                 <snapshotRepository>
@@ -244,7 +252,8 @@
                 <bluecat.common.version>1.1.0-RELEASE</bluecat.common.version>
                 <bluecat.common.version>1.1.0-RELEASE</bluecat.common.version>
                 <prism-auth-support.version>1.9.0-RELEASE</prism-auth-support.version>
                 <prism-auth-support.version>1.9.0-RELEASE</prism-auth-support.version>
                 <prism-auth-core.version>1.9.0-RELEASE</prism-auth-core.version>
                 <prism-auth-core.version>1.9.0-RELEASE</prism-auth-core.version>
-                <yr-supply-chain-api.version>1.1.8-RELEASE</yr-supply-chain-api.version>
+                <yr-supply-chain-api.version>1.2.1-RELEASE</yr-supply-chain-api.version>
+                <yr-shopping-admin.version>1.0.3-RELEASE</yr-shopping-admin.version>
             </properties>
             </properties>
             <distributionManagement>
             <distributionManagement>
                 <repository>
                 <repository>

+ 4 - 0
warehouse-admin-component/pom.xml

@@ -41,6 +41,10 @@
 			<groupId>com.supplychain.support</groupId>
 			<groupId>com.supplychain.support</groupId>
 			<artifactId>yr-supply-chain-api</artifactId>
 			<artifactId>yr-supply-chain-api</artifactId>
 		</dependency>
 		</dependency>
+		<dependency>
+			<groupId>com.yr.shopping.admin</groupId>
+			<artifactId>shopping-admin-support</artifactId>
+		</dependency>
 		<!-- 外部dubbo接口 end-->
 		<!-- 外部dubbo接口 end-->
 		<!-- apollo -->
 		<!-- apollo -->
 		<dependency>
 		<dependency>

+ 29 - 0
warehouse-admin-component/src/main/java/com/yr/warehouse/admin/component/apollo/ApolloConfigEvent.java

@@ -0,0 +1,29 @@
+package com.yr.warehouse.admin.component.apollo;
+
+
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
+import com.yr.warehouse.admin.component.vo.OrgOperatorVo;
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @ClassName ApolloConfigEvnt
+ * @Description: 类描述
+ * @Author: pete
+ * @CreateDate: 2024/6/7 11:58
+ * @Version: 1.0
+ */
+@Data
+@Configuration
+public class ApolloConfigEvent {
+
+    @Value("${app.id}")
+    private String appId;
+
+    /**
+     * 总部运营商id
+     */
+    @ApolloJsonValue("${org.operator.super:{\"id\":1,\"operatorName\":\"有人总\"}}")
+    private OrgOperatorVo superOperator;
+}

+ 36 - 0
warehouse-admin-component/src/main/java/com/yr/warehouse/admin/component/areastaff/AreaStaffComponent.java

@@ -0,0 +1,36 @@
+package com.yr.warehouse.admin.component.areastaff;
+
+import com.supplychain.api.request.areastaff.AreaStaffSearchRequest;
+import com.supplychain.api.response.areastaff.AreaStaffResponse;
+import com.supplychain.api.server.areastaff.AreaStaffApi;
+import com.yr.warehouse.admin.component.apollo.ApolloConfigEvent;
+import jakarta.annotation.Resource;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 区域员工组件
+ */
+@Component
+public class AreaStaffComponent {
+    @DubboReference(timeout = 5000, retries = 0 /*, url="dubbo://127.0.0.1:21887" */)
+    private AreaStaffApi areaStaffApi;
+    @Resource
+    private ApolloConfigEvent apolloConfigEvent;
+
+    /**
+     * 根据员工id查询区域员工信息
+     *
+     * @param staffIds 员工id
+     * @return 区域员工信息
+     */
+    public List<AreaStaffResponse> searchByStaffIds(List<Long> staffIds) {
+        AreaStaffSearchRequest request = new AreaStaffSearchRequest();
+        request.setAppId(apolloConfigEvent.getAppId());
+        request.setAreaStaffIds(staffIds);
+        return areaStaffApi.searchByStaffIds(request).getData();
+    }
+
+}

+ 39 - 0
warehouse-admin-component/src/main/java/com/yr/warehouse/admin/component/driver/DriverDeviceSalesComponent.java

@@ -0,0 +1,39 @@
+package com.yr.warehouse.admin.component.driver;
+
+import com.yr.bluecat.common.entity.response.Result;
+import com.yr.shopping.admin.support.DriverDeviceSalesApi;
+import com.yr.shopping.admin.support.request.DriverDeviceSalesRequest;
+import com.yr.shopping.admin.support.response.DriverDeviceSalesResponse;
+import com.yr.warehouse.admin.component.apollo.ApolloConfigEvent;
+import jakarta.annotation.Resource;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 司机设备销售数据
+ */
+@Component
+public class DriverDeviceSalesComponent {
+    @DubboReference(timeout = 5000, retries = 0 /*, url="dubbo://127.0.0.1:21888" */)
+    private DriverDeviceSalesApi driverDeviceSalesApi;
+    @Resource
+    private ApolloConfigEvent apolloConfigEvent;
+    /**
+     * 列表
+     * @param startTime 开始时间
+     * @param endTime 结束时间
+     * @return
+     */
+    public List<DriverDeviceSalesResponse> list(LocalDateTime startTime, LocalDateTime endTime) {
+        DriverDeviceSalesRequest request = new DriverDeviceSalesRequest();
+        request.setAppId(apolloConfigEvent.getAppId());
+        request.setStartTime(startTime);
+        request.setEndTime(endTime);
+        Result<List<DriverDeviceSalesResponse>> res = driverDeviceSalesApi.list(request);
+        return res.getData();
+    }
+
+}

+ 110 - 0
warehouse-admin-component/src/main/java/com/yr/warehouse/admin/component/vo/OrgOperatorVo.java

@@ -0,0 +1,110 @@
+/**
+ * 湖南有人网络有限公司
+ **/
+package com.yr.warehouse.admin.component.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * @ClassName OrgOperatorAddRequest
+ * @Description: 类描述
+ * @Author: pete
+ * @CreateDate: 2024/4/22 17:03
+ * @Version: 1.0
+ */
+@Data
+public class OrgOperatorVo {
+    /**
+     * 运营商id
+     */
+    private Long id;
+    /**
+     * 运营商名称
+     */
+    private String operatorName;
+
+    /**
+     * 运营商编号
+     */
+    private String operatorNumber;
+
+    /**
+     * 运营商父级id
+     */
+    private Long operatorParentId;
+
+    /**
+     * 运营商Id链
+     */
+    private String operatorChain;
+
+    /**
+     * 运营商名称链
+     */
+    private String operatorChainName;
+
+    /**
+     * 负责人名称
+     */
+    private String personInCharge;
+
+    /**
+     * 负责人联系电话
+     */
+    private String mobile;
+
+    /**
+     * 联系邮箱
+     */
+    private String email;
+
+    /**
+     * 详细地址
+     */
+    private String detailedAddress;
+
+    /**
+     * logo图片地址
+     */
+    private String logo;
+
+    /**
+     * 状态:NORMAL:正常|DISABLE:禁用
+     */
+    private String status;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
+    /**
+     * 创建人uid
+     */
+    private Long createUid;
+
+    /**
+     * 创建人名称
+     */
+    private String createName;
+
+    /**
+     * 修改时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime modifyTime;
+
+    /**
+     * 修改人uid
+     */
+    private Long modifyUid;
+
+    /**
+     * 修改人名称
+     */
+    private String modifyName;
+}

+ 8 - 0
warehouse-admin-data/src/main/java/com/yr/warehouse/admin/common/menu/BoolEnum.java

@@ -25,6 +25,14 @@ public enum BoolEnum {
         return false;
         return false;
     }
     }
 
 
+    public static boolean isYes(String name) {
+        return YES.name().equals(name);
+    }
+
+    public static boolean isNo(String name) {
+        return NO.name().equals(name);
+    }
+
     public static BoolEnum getByCode(Integer code) {
     public static BoolEnum getByCode(Integer code) {
         for (BoolEnum item : BoolEnum.values()) {
         for (BoolEnum item : BoolEnum.values()) {
             if (item.getCode().equals(code)) {
             if (item.getCode().equals(code)) {

+ 62 - 0
warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/data/DriverDeviceStockSnapshot.java

@@ -0,0 +1,62 @@
+package com.yr.warehouse.admin.driver.data;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 司机设备库存快照
+ * </p>
+ *
+ * @author 
+ * @since 2025-12-04 15:21:10
+ */
+@Getter
+@Setter
+@TableName("yr_driver_device_stock_snapshot")
+public class DriverDeviceStockSnapshot implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 自增主键
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 统计开始日期(YYYY-MM-dd HH:mm:ss)
+     */
+    @TableField("statBeginDateTime")
+    private LocalDateTime statBeginDateTime;
+
+    /**
+     * 统计结束日期(YYYY-MM-dd HH:mm:ss)
+     */
+    @TableField("statEndDateTime")
+    private LocalDateTime statEndDateTime;
+
+    /**
+     * 区域员工关系id
+     */
+    @TableField("areaStaffId")
+    private Long areaStaffId;
+
+    /**
+     * 统计总数
+     */
+    @TableField("totalNum")
+    private Long totalNum;
+
+    /**
+     * 创建时间
+     */
+    @TableField("createTime")
+    private LocalDateTime createTime;
+}

+ 12 - 0
warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/data/DriverGoodsLossRecord.java

@@ -104,6 +104,18 @@ public class DriverGoodsLossRecord implements Serializable {
     private Long currentOnRouteNum;
     private Long currentOnRouteNum;
 
 
     /**
     /**
+     * 核算数
+     */
+    @TableField("verifiedNum")
+    private Long verifiedNum;
+
+    /**
+     * 是否核算 NO 未核算 YES 已核算
+     */
+    @TableField("useVerified")
+    private String useVerified;
+
+    /**
      * 创建时间
      * 创建时间
      */
      */
     @TableField("createTime")
     @TableField("createTime")

+ 34 - 0
warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/mapper/DriverDeviceStockSnapshotMapper.java

@@ -0,0 +1,34 @@
+package com.yr.warehouse.admin.driver.mapper;
+
+import com.yr.warehouse.admin.driver.data.DriverDeviceStockSnapshot;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * <p>
+ * 司机设备库存快照 Mapper 接口
+ * </p>
+ *
+ * @author 
+ * @since 2025-12-04 15:21:10
+ */
+public interface DriverDeviceStockSnapshotMapper extends BaseMapper<DriverDeviceStockSnapshot> {
+
+    /**
+     * 获取司机设备库存数
+     * @return 司机设备库存数
+     */
+    List<DriverDeviceStockSnapshot> queryDriverDeviceInventoryNum(@Param("beginTime") LocalDateTime beginTime, @Param("endTime") LocalDateTime endTime);
+
+    /**
+     * 批量插入司机设备库存数
+     * @param beginTime 开始时间
+     * @param endTime 结束时间
+     * @param deviceInventoryNumVos 司机设备库存数
+     */
+    void insertBatch(@Param("beginTime") LocalDateTime beginTime, @Param("endTime") LocalDateTime endTime, @Param("deviceInventoryNumVos") List<DriverGoodsOrderNumVo> deviceInventoryNumVos);
+}

+ 16 - 0
warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/mapper/DriverEquipmentLossRecordMapper.java

@@ -2,6 +2,10 @@ package com.yr.warehouse.admin.driver.mapper;
 
 
 import com.yr.warehouse.admin.driver.data.DriverEquipmentLossRecord;
 import com.yr.warehouse.admin.driver.data.DriverEquipmentLossRecord;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDate;
+import java.util.List;
 
 
 /**
 /**
  * <p>
  * <p>
@@ -13,4 +17,16 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
  */
 public interface DriverEquipmentLossRecordMapper extends BaseMapper<DriverEquipmentLossRecord> {
 public interface DriverEquipmentLossRecordMapper extends BaseMapper<DriverEquipmentLossRecord> {
 
 
+    /**
+     * 批量插入
+     * @param driverEquipmentLossRecords
+     */
+    void insertBatch(@Param("driverEquipmentLossRecords") List<DriverEquipmentLossRecord> driverEquipmentLossRecords);
+
+    /**
+     * 按统计日期查询
+     * @param statDate 统计日期
+     * @return
+     */
+    List<DriverEquipmentLossRecord> searchByStatDate(@Param("statDate") LocalDate statDate);
 }
 }

+ 16 - 0
warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/mapper/DriverGoodsLossRecordMapper.java

@@ -2,6 +2,10 @@ package com.yr.warehouse.admin.driver.mapper;
 
 
 import com.yr.warehouse.admin.driver.data.DriverGoodsLossRecord;
 import com.yr.warehouse.admin.driver.data.DriverGoodsLossRecord;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDate;
+import java.util.List;
 
 
 /**
 /**
  * <p>
  * <p>
@@ -13,4 +17,16 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
  */
 public interface DriverGoodsLossRecordMapper extends BaseMapper<DriverGoodsLossRecord> {
 public interface DriverGoodsLossRecordMapper extends BaseMapper<DriverGoodsLossRecord> {
 
 
+    /**
+     * 批量插入
+     * @param driverGoodsLossRecords
+     */
+    void insertBatch(@Param("driverGoodsLossRecords") List<DriverGoodsLossRecord> driverGoodsLossRecords);
+
+    /**
+     * 查询司机货损记录
+     * @param statDate 统计日期
+     * @return 司机货损记录
+     */
+    List<DriverGoodsLossRecord> searchByStatDate(@Param("statDate") LocalDate statDate);
 }
 }

+ 18 - 2
warehouse-admin-data/src/main/java/com/yr/warehouse/admin/driver/mapper/DriverOnRouteDetailLogMapper.java

@@ -1,16 +1,32 @@
 package com.yr.warehouse.admin.driver.mapper;
 package com.yr.warehouse.admin.driver.mapper;
 
 
-import com.yr.warehouse.admin.driver.data.DriverOnRouteDetailLog;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.yr.warehouse.admin.driver.data.DriverOnRouteDetailLog;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDate;
+import java.util.List;
 
 
 /**
 /**
  * <p>
  * <p>
  * 司机在途明细日志表 Mapper 接口
  * 司机在途明细日志表 Mapper 接口
  * </p>
  * </p>
  *
  *
- * @author 
+ * @author
  * @since 2025-12-03 15:26:38
  * @since 2025-12-03 15:26:38
  */
  */
 public interface DriverOnRouteDetailLogMapper extends BaseMapper<DriverOnRouteDetailLog> {
 public interface DriverOnRouteDetailLogMapper extends BaseMapper<DriverOnRouteDetailLog> {
 
 
+    /**
+     * 批量插入
+     * @param driverOnRouteDetailLogs
+     */
+    void insertBatch(@Param("driverOnRouteDetailLogs") List<DriverOnRouteDetailLog> driverOnRouteDetailLogs);
+
+    /**
+     * 按统计日期查询
+     * @param statDate 统计日期
+     * @return
+     */
+    List<DriverOnRouteDetailLog> searchByStatDate(@Param("statDate") LocalDate statDate);
 }
 }

+ 32 - 0
warehouse-admin-data/src/main/java/com/yr/warehouse/admin/replenish/vo/DriverGoodsOrderNumVo.java

@@ -0,0 +1,32 @@
+package com.yr.warehouse.admin.replenish.vo;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 司机货单数量视图
+ */
+@Data
+public class DriverGoodsOrderNumVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 司机id
+     */
+    private Long areaStaffId;
+
+    /**
+     * 商品id
+     */
+    private Integer goodsId;
+
+    /**
+     * 货单数量
+     */
+    private Long totalNum;
+
+}

+ 72 - 0
warehouse-admin-data/src/main/java/com/yr/warehouse/admin/statistics/mapper/BillingQuantityStatisticsMapper.java

@@ -0,0 +1,72 @@
+package com.yr.warehouse.admin.statistics.mapper;
+
+import com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+public interface BillingQuantityStatisticsMapper {
+
+    /**
+     * 查询拣货单开单数
+     * @param beginTime 开始时间
+     * @param endTime 结束时间
+     * @return 开单数
+     */
+    List<DriverGoodsOrderNumVo> queryPickingBillingQuantity(@Param("beginTime") LocalDateTime beginTime, @Param("endTime") LocalDateTime endTime);
+
+    /**
+     * 获取整件单开单数
+     * @param beginTime 开始时间
+     * @param endTime 结束时间
+     * @return
+     */
+    List<DriverGoodsOrderNumVo> queryAggregationBillingQuantity(@Param("beginTime") LocalDateTime beginTime, @Param("endTime") LocalDateTime endTime);
+
+    /**
+     * 查询未出库取消数
+     * @param beginTime 开始时间
+     * @param endTime 结束时间
+     * @return
+     */
+    List<DriverGoodsOrderNumVo> queryUnOutStockCancelNum(@Param("beginTime") LocalDateTime beginTime, @Param("endTime") LocalDateTime endTime);
+
+    /**
+     * 查询已出库取消数
+     * @param beginTime 开始时间
+     * @param endTime 接受时间
+     * @return
+     */
+    List<DriverGoodsOrderNumVo> queryOutStockCancelNum(@Param("beginTime") LocalDateTime beginTime, @Param("endTime") LocalDateTime endTime);
+
+    /**
+     * 查询司机补货数
+     * @param beginTime 开始时间
+     * @param endTime 结束时间
+     * @return
+     */
+    List<DriverGoodsOrderNumVo> queryDriverReplenishNum(@Param("beginTime") LocalDateTime beginTime, @Param("endTime") LocalDateTime endTime);
+
+    /**
+     * 查询设备补货数
+     * @param beginTime 开始时间
+     * @param endTime 结束时间
+     * @return
+     */
+    List<DriverGoodsOrderNumVo> queryDeviceReplenishNum(@Param("beginTime") LocalDateTime beginTime, @Param("endTime") LocalDateTime endTime);
+
+    /**
+     * 查询司机回仓数
+     * @param beginTime 开始时间
+     * @param endTime 结束时间
+     * @return
+     */
+    List<DriverGoodsOrderNumVo> queryDriverReturnNum(@Param("beginTime") LocalDateTime beginTime, @Param("endTime") LocalDateTime endTime);
+
+    /**
+     * 获取设备库存数
+     * @return
+     */
+    List<DriverGoodsOrderNumVo> queryDriverDeviceInventoryNum();
+}

+ 33 - 0
warehouse-admin-data/src/main/resources/mapper/driver/DriverDeviceStockSnapshotMapper.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.yr.warehouse.admin.driver.mapper.DriverDeviceStockSnapshotMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.yr.warehouse.admin.driver.data.DriverDeviceStockSnapshot">
+        <id column="id" property="id" />
+        <result column="statBeginDateTime" property="statBeginDateTime" />
+        <result column="statEndDateTime" property="statEndDateTime" />
+        <result column="areaStaffId" property="areaStaffId" />
+        <result column="totalNum" property="totalNum" />
+        <result column="createTime" property="createTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, statBeginDateTime, statEndDateTime, areaStaffId, totalNum, createTime
+    </sql>
+    <insert id="insertBatch">
+        insert into yr_driver_device_stock_snapshot
+        (statBeginDateTime, statEndDateTime, areaStaffId, totalNum)
+        values
+        <foreach collection="deviceInventoryNumVos" item="deviceInventoryNumVo" separator=",">
+            (#{beginTime}, #{endTime}, #{deviceInventoryNumVo.areaStaffId}, #{deviceInventoryNumVo.totalNum})
+        </foreach>
+    </insert>
+    <select id="queryDriverDeviceInventoryNum"
+            resultType="com.yr.warehouse.admin.driver.data.DriverDeviceStockSnapshot">
+        select <include refid="Base_Column_List" /> from yr_driver_device_stock_snapshot
+        where statBeginDateTime = #{beginTime} and statEndDateTime = #{endTime}
+    </select>
+
+</mapper>

+ 31 - 12
warehouse-admin-data/src/main/resources/mapper/driver/DriverEquipmentLossRecordMapper.xml

@@ -4,23 +4,42 @@
 
 
     <!-- 通用查询映射结果 -->
     <!-- 通用查询映射结果 -->
     <resultMap id="BaseResultMap" type="com.yr.warehouse.admin.driver.data.DriverEquipmentLossRecord">
     <resultMap id="BaseResultMap" type="com.yr.warehouse.admin.driver.data.DriverEquipmentLossRecord">
-        <id column="id" property="id" />
-        <result column="operatorId" property="operatorId" />
-        <result column="operatorChain" property="operatorChain" />
-        <result column="areaStaffId" property="areaStaffId" />
-        <result column="statDate" property="statDate" />
-        <result column="equipmentStockNum" property="equipmentStockNum" />
-        <result column="equipmentReplenishNum" property="equipmentReplenishNum" />
-        <result column="equipmentSalesNum" property="equipmentSalesNum" />
-        <result column="theoreticalEquipmentStockNum" property="theoreticalEquipmentStockNum" />
-        <result column="actualEquipmentStockNum" property="actualEquipmentStockNum" />
-        <result column="profitLossNum" property="profitLossNum" />
-        <result column="createTime" property="createTime" />
+        <id column="id" property="id"/>
+        <result column="operatorId" property="operatorId"/>
+        <result column="operatorChain" property="operatorChain"/>
+        <result column="areaStaffId" property="areaStaffId"/>
+        <result column="statDate" property="statDate"/>
+        <result column="equipmentStockNum" property="equipmentStockNum"/>
+        <result column="equipmentReplenishNum" property="equipmentReplenishNum"/>
+        <result column="equipmentSalesNum" property="equipmentSalesNum"/>
+        <result column="theoreticalEquipmentStockNum" property="theoreticalEquipmentStockNum"/>
+        <result column="actualEquipmentStockNum" property="actualEquipmentStockNum"/>
+        <result column="profitLossNum" property="profitLossNum"/>
+        <result column="createTime" property="createTime"/>
     </resultMap>
     </resultMap>
 
 
     <!-- 通用查询结果列 -->
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
     <sql id="Base_Column_List">
         id, operatorId, operatorChain, areaStaffId, statDate, equipmentStockNum, equipmentReplenishNum, equipmentSalesNum, theoreticalEquipmentStockNum, actualEquipmentStockNum, profitLossNum, createTime
         id, operatorId, operatorChain, areaStaffId, statDate, equipmentStockNum, equipmentReplenishNum, equipmentSalesNum, theoreticalEquipmentStockNum, actualEquipmentStockNum, profitLossNum, createTime
     </sql>
     </sql>
+    <insert id="insertBatch" parameterType="java.util.List" keyProperty="id" useGeneratedKeys="true">
+        insert into yr_driver_equipment_loss_record
+        (operatorId, operatorChain, areaStaffId, statDate, equipmentStockNum, equipmentReplenishNum, equipmentSalesNum,
+        theoreticalEquipmentStockNum, actualEquipmentStockNum, profitLossNum)
+        values
+        <foreach item="item" collection="driverEquipmentLossRecords" separator="," index="index">
+            (
+            #{item.operatorId}, #{item.operatorChain}, #{item.areaStaffId}, #{item.statDate}, #{item.equipmentStockNum},
+            #{item.equipmentReplenishNum}, #{item.equipmentSalesNum}, #{item.theoreticalEquipmentStockNum},
+            #{item.actualEquipmentStockNum}, #{item.profitLossNum}
+            )
+        </foreach>
+    </insert>
+    <select id="searchByStatDate" resultType="com.yr.warehouse.admin.driver.data.DriverEquipmentLossRecord"
+            parameterType="java.time.LocalDate">
+        select
+        <include refid="Base_Column_List"/>
+        from yr_driver_equipment_loss_record where statDate = #{statDate}
+    </select>
 
 
 </mapper>
 </mapper>

+ 40 - 15
warehouse-admin-data/src/main/resources/mapper/driver/DriverGoodsLossRecordMapper.xml

@@ -4,25 +4,50 @@
 
 
     <!-- 通用查询映射结果 -->
     <!-- 通用查询映射结果 -->
     <resultMap id="BaseResultMap" type="com.yr.warehouse.admin.driver.data.DriverGoodsLossRecord">
     <resultMap id="BaseResultMap" type="com.yr.warehouse.admin.driver.data.DriverGoodsLossRecord">
-        <id column="id" property="id" />
-        <result column="operatorId" property="operatorId" />
-        <result column="operatorChain" property="operatorChain" />
-        <result column="areaStaffId" property="areaStaffId" />
-        <result column="statDate" property="statDate" />
-        <result column="yesterdayOnRouteNum" property="yesterdayOnRouteNum" />
-        <result column="pickingOrderCreateNum" property="pickingOrderCreateNum" />
-        <result column="wholeOrderCreateNum" property="wholeOrderCreateNum" />
-        <result column="unshippedCancelNum" property="unshippedCancelNum" />
-        <result column="shippedCancelNum" property="shippedCancelNum" />
-        <result column="replenishNum" property="replenishNum" />
-        <result column="returnWarehouseNum" property="returnWarehouseNum" />
-        <result column="currentOnRouteNum" property="currentOnRouteNum" />
-        <result column="createTime" property="createTime" />
+        <id column="id" property="id"/>
+        <result column="operatorId" property="operatorId"/>
+        <result column="operatorChain" property="operatorChain"/>
+        <result column="areaStaffId" property="areaStaffId"/>
+        <result column="statDate" property="statDate"/>
+        <result column="yesterdayOnRouteNum" property="yesterdayOnRouteNum"/>
+        <result column="pickingOrderCreateNum" property="pickingOrderCreateNum"/>
+        <result column="wholeOrderCreateNum" property="wholeOrderCreateNum"/>
+        <result column="unshippedCancelNum" property="unshippedCancelNum"/>
+        <result column="shippedCancelNum" property="shippedCancelNum"/>
+        <result column="replenishNum" property="replenishNum"/>
+        <result column="returnWarehouseNum" property="returnWarehouseNum"/>
+        <result column="currentOnRouteNum" property="currentOnRouteNum"/>
+        <result column="verifiedNum" property="verifiedNum" />
+        <result column="useVerified" property="useVerified" />
+        <result column="createTime" property="createTime"/>
     </resultMap>
     </resultMap>
 
 
     <!-- 通用查询结果列 -->
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
     <sql id="Base_Column_List">
-        id, operatorId, operatorChain, areaStaffId, statDate, yesterdayOnRouteNum, pickingOrderCreateNum, wholeOrderCreateNum, unshippedCancelNum, shippedCancelNum, replenishNum, returnWarehouseNum, currentOnRouteNum, createTime
+        id, operatorId, operatorChain, areaStaffId, statDate, yesterdayOnRouteNum, pickingOrderCreateNum, wholeOrderCreateNum, unshippedCancelNum, shippedCancelNum, replenishNum, returnWarehouseNum, currentOnRouteNum, verifiedNum, useVerified, createTime
     </sql>
     </sql>
+    <insert id="insertBatch"
+            parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
+        insert into yr_driver_goods_loss_record
+        (operatorId, operatorChain, areaStaffId, statDate, yesterdayOnRouteNum, pickingOrderCreateNum,
+        wholeOrderCreateNum, unshippedCancelNum, shippedCancelNum, replenishNum, returnWarehouseNum, currentOnRouteNum)
+        values
+        <foreach item="item" collection="driverGoodsLossRecords" separator="," index="index">
+            (
+            #{item.operatorId}, #{item.operatorChain}, #{item.areaStaffId}, #{item.statDate},
+            #{item.yesterdayOnRouteNum}, #{item.pickingOrderCreateNum}, #{item.wholeOrderCreateNum},
+            #{item.unshippedCancelNum}, #{item.shippedCancelNum}, #{item.replenishNum}, #{item.returnWarehouseNum},
+            #{item.currentOnRouteNum}
+            )
+        </foreach>
+    </insert>
+    <select id="searchByStatDate"
+            resultType="com.yr.warehouse.admin.driver.data.DriverGoodsLossRecord"
+            parameterType="java.time.LocalDate">
+        select
+        <include refid="Base_Column_List"/>
+        from yr_driver_goods_loss_record
+        where statDate = #{statDate}
+    </select>
 
 
 </mapper>
 </mapper>

+ 16 - 0
warehouse-admin-data/src/main/resources/mapper/driver/DriverOnRouteDetailLogMapper.xml

@@ -26,5 +26,21 @@
     <sql id="Base_Column_List">
     <sql id="Base_Column_List">
         id, operatorId, operatorChain, areaStaffId, statDate, goodsLossRecordId, goodsId, createNum, unshippedCancelNum, shippedCancelNum, replenishNum, returnWarehouseNum, yesterdayStockNum, todayStockNum, profitLossNum, createTime
         id, operatorId, operatorChain, areaStaffId, statDate, goodsLossRecordId, goodsId, createNum, unshippedCancelNum, shippedCancelNum, replenishNum, returnWarehouseNum, yesterdayStockNum, todayStockNum, profitLossNum, createTime
     </sql>
     </sql>
+    <insert id="insertBatch"
+            parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
+        insert into yr_driver_on_route_detail_log
+        (operatorId, operatorChain, areaStaffId, statDate, goodsLossRecordId, goodsId, createNum, unshippedCancelNum,
+        shippedCancelNum, replenishNum, returnWarehouseNum, yesterdayStockNum, todayStockNum, profitLossNum)
+        values
+        <foreach collection="driverOnRouteDetailLogs" item="item" open="(" close=")" separator=",">
+            (#{item.operatorId}, #{item.operatorChain}, #{item.areaStaffId}, #{item.statDate}, #{item.goodsLossRecordId}, #{item.goodsId}, #{item.createNum},
+            #{item.unshippedCancelNum}, #{item.shippedCancelNum}, #{item.replenishNum}, #{item.returnWarehouseNum}, #{item.yesterdayStockNum}, #{item.todayStockNum},
+            #{item.profitLossNum})
+        </foreach>
+    </insert>
+    <select id="searchByStatDate" resultType="com.yr.warehouse.admin.driver.data.DriverOnRouteDetailLog"
+            parameterType="java.time.LocalDate">
+        select <include refid="Base_Column_List" /> from yr_driver_on_route_detail_log where statDate = #{statDate}
+    </select>
 
 
 </mapper>
 </mapper>

+ 118 - 0
warehouse-admin-data/src/main/resources/mapper/statistics/BillingQuantityStatisticsMapper.xml

@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.yr.warehouse.admin.statistics.mapper.BillingQuantityStatisticsMapper">
+
+    <select id="queryPickingBillingQuantity" resultType="com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo">
+        select poi.area_staff_id as areaStaffId, pod.goods_id as goodsId, SUM(pod.picking_number) as totalNum
+        from yr_picking_order_detail as pod
+                 left join yr_picking_order_info as poi on pod.order_number = poi.order_number
+        where poi.create_time between #{beginTime} and #{endTime}
+        group by poi.area_staff_id, pod.goods_id
+    </select>
+
+    <select id="queryAggregationBillingQuantity" resultType="com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo">
+        select api.area_staff_id                                                    as areaStaffId,
+               apd.goods_id                                                         as goodsId,
+               IFNULL(sum(apd.picking_number), 0) + IFNULL(sum(apd.more_number), 0) as totalNum
+        from yr_aggregation_pick_detail as apd
+                 left join yr_aggregation_pick_info as api on apd.order_number = api.order_number
+        where api.modify_time between #{beginTime} and #{endTime}
+        group by api.area_staff_id, apd.goods_id
+    </select>
+
+    <select id="queryUnOutStockCancelNum"
+            resultType="com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo">
+        with cancel_data as (select poi.area_staff_id                  as areaStaffId,
+                                    pod.goods_id                       as goodsId,
+                                    IFNULL(SUM(pod.picking_number), 0) as pickingNumber
+                             from yr_picking_order_detail as pod
+                                      left join yr_picking_order_info as poi on pod.order_number = poi.order_number
+                             where poi.modify_time between #{beginTime} and #{endTime}
+                               and poi.picking_status = 3
+                             group by poi.area_staff_id, pod.goods_id
+                             union all
+                             select api.area_staff_id                                                    as areaStaffId,
+                                    apd.goods_id                                                         as goodsId,
+                                    IFNULL(sum(apd.picking_number), 0) + IFNULL(sum(apd.more_number), 0) as pickingNumber
+                             from yr_aggregation_pick_detail as apd
+                                      left join yr_aggregation_pick_info as api on apd.order_number = api.order_number
+                             where api.modify_time between #{beginTime} and #{endTime}
+                               and api.status = 'CANCEL')
+        select areaStaffId, goodsId, sum(pickingNumber) as totalNum
+        from cancel_data
+        where areaStaffId IS NOT NULL
+        group by areaStaffId, goodsId
+    </select>
+    <select id="queryOutStockCancelNum" resultType="com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo">
+        with cancel_data as (select poi.area_staff_id                  as areaStaffId,
+                                    pod.goods_id                       as goodsId,
+                                    IFNULL(SUM(pod.picking_number), 0) as pickingNumber
+                             from yr_picking_order_detail as pod
+                                      left join yr_picking_order_info as poi on pod.order_number = poi.order_number
+                             where poi.modify_time between #{beginTime} and #{endTime}
+                               and poi.picking_status = 4
+                             group by poi.area_staff_id, pod.goods_id
+                             union all
+                             select api.area_staff_id                                                    as areaStaffId,
+                                    apd.goods_id                                                         as goodsId,
+                                    IFNULL(sum(apd.picking_number), 0) + IFNULL(sum(apd.more_number), 0) as pickingNumber
+                             from yr_aggregation_pick_detail as apd
+                                      left join yr_aggregation_pick_info as api on apd.order_number = api.order_number
+                             where api.modify_time between #{beginTime} and #{endTime}
+                               and api.status = 'OUT_CANCEL')
+        select areaStaffId, goodsId, sum(pickingNumber) as totalNum
+        from cancel_data
+        where areaStaffId IS NOT NULL
+        group by areaStaffId, goodsId
+    </select>
+
+    <select id="queryDriverReplenishNum" resultType="com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo">
+        select roi.area_staff_id as areaStaffId, rod.goods_id as goodsId, sum(rod.real_picking_number) as totalNum
+        from yr_replenish_order_detail as rod
+                 left join yr_replenish_order_info as roi on rod.order_number = roi.order_number
+        where roi.replenish_time between #{beginTime} and #{endTime}
+          and roi.replenish_status = 2
+        group by roi.area_staff_id, rod.goods_id
+    </select>
+
+    <select id="queryDeviceReplenishNum" resultType="com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo">
+        select roi.area_staff_id as areaStaffId, sum(rod.real_picking_number) as totalNum
+        from yr_replenish_order_detail as rod
+                 left join yr_replenish_order_info as roi on rod.order_number = roi.order_number
+        where roi.replenish_time between #{beginTime} and #{endTime}
+          and roi.replenish_status = 2
+          and rod.real_picking_number > 0
+        group by roi.area_staff_id
+    </select>
+
+    <select id="queryDriverReturnNum" resultType="com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo">
+        select oas.id as areaStaffId, wbod.productSpecId as goodsId, SUM(wbod.realBackNumber) as totalNum
+        from yr_warehouse_back_order_detail as wbod
+                 left join yr_warehouse_back_order_info as wboi on wbod.backOrderId = wboi.id
+                 left join yr_operators_area_staff as oas on wboi.replenishId = oas.accountId
+        where wboi.backTime between #{beginTime} and #{endTime}
+          and wboi.backStatus = 'BACK_SUCCESS'
+        group by wboi.replenishId, wbod.productSpecId
+    </select>
+    <select id="queryDriverDeviceInventoryNum"
+            resultType="com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo">
+        with device_stock as (select odcw.deviceId as deviceId, SUM(odcw.stockNumber) as stockNumber
+                              from yr_operators_device_cargo_way as odcw
+                              where odcw.isDelete = 'NORMAL'
+                                and odcw.goodsId > 0
+                              group by odcw.deviceId
+                              union all
+                              select odrg.deviceId as deviceId, SUM(odrg.stockNumber) as stockNumber
+                              from yr_operators_device_replace_goods as odrg
+                              where odrg.status = 'INIT'
+                                and odrg.way = 'HOLD'
+                              group by odrg.deviceId)
+        select ods.areaStaffId as areaStaffId, sum(ds.stockNumber) as totalNum
+        from device_stock as ds
+                 left join yr_operators_device_site as ods on ds.deviceId = ods.deviceId
+                 left join yr_operators_device as od on ds.deviceId = od.id
+        where od.bindStatus = 'BIND'
+          and putStatus = 'USED'
+        group by ods.areaStaffId
+    </select>
+</mapper>

+ 2 - 8
warehouse-admin-data/src/main/test/com/supplychain/CodeGenerator.java

@@ -58,14 +58,8 @@ public class CodeGenerator {
      * 需要生成的表名,必填
      * 需要生成的表名,必填
      */
      */
     private static final List<String> TABLES = List.of(
     private static final List<String> TABLES = List.of(
-            "yr_driver_goods_loss_record",
-            "yr_driver_equipment_loss_record",
-            "yr_driver_on_route_detail_log",
-            "yr_driver_goods_loss_interval_main",
-            "yr_driver_goods_loss_interval_detail",
-            "yr_equipment_loss_interval_detail",
-            "yr_driver_on_route_account_record_main",
-            "yr_driver_on_route_account_record_detail"
+
+            "yr_driver_device_stock_snapshot"
     );
     );
 
 
 
 

+ 9 - 2
warehouse-admin-event/src/main/java/com/yr/warehouse/admin/event/task/DriverCargoDamageStatisticsTask.java

@@ -1,9 +1,13 @@
 package com.yr.warehouse.admin.event.task;
 package com.yr.warehouse.admin.event.task;
 
 
 import com.xxl.job.core.handler.annotation.XxlJob;
 import com.xxl.job.core.handler.annotation.XxlJob;
+import com.yr.warehouse.admin.service.statistics.DriverStatistics;
+import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
+import java.time.LocalDate;
+
 /**
 /**
  * 司机货损统计任务
  * 司机货损统计任务
  */
  */
@@ -11,14 +15,17 @@ import org.springframework.stereotype.Component;
 @Component
 @Component
 public class DriverCargoDamageStatisticsTask {
 public class DriverCargoDamageStatisticsTask {
 
 
+    @Resource
+    private DriverStatistics driverStatistics;
+
     /**
     /**
      * 货损统计任务
      * 货损统计任务
      */
      */
     @XxlJob("driverCargoDamageStatisticsTask")
     @XxlJob("driverCargoDamageStatisticsTask")
     public void driverCargoDamageStatisticsTask() {
     public void driverCargoDamageStatisticsTask() {
         log.info("开始执行司机货损统计任务");
         log.info("开始执行司机货损统计任务");
-
-
+        driverStatistics.cargoDamageStatistics(LocalDate.now());
+        log.info("结束执行司机货损统计任务");
     }
     }
 
 
 }
 }

+ 42 - 0
warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/context/TransactionAwareComponent.java

@@ -0,0 +1,42 @@
+package com.yr.warehouse.admin.service.context;
+
+import jakarta.annotation.Resource;
+import org.apache.poi.ss.formula.functions.T;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.DefaultTransactionDefinition;
+import org.springframework.transaction.support.TransactionCallback;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
+import org.springframework.transaction.support.TransactionTemplate;
+
+/**
+ * 事务感知组件
+ */
+@Component
+public class TransactionAwareComponent {
+
+    @Resource
+    private TransactionTemplate transactionTemplate;
+    @Resource
+    private PlatformTransactionManager transactionManager;
+
+    /**
+     * 执行事务
+     * @param callback 回调
+     * @return 结果
+     */
+    public T execute(TransactionCallback<T> callback) {
+        // 判断当前是否处于事务中
+        boolean isActive = TransactionSynchronizationManager.isActualTransactionActive();
+
+        if (isActive) {
+            TransactionDefinition definition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_SUPPORTS);
+            TransactionStatus currentTransactionStatus = transactionManager.getTransaction(definition);
+            return callback.doInTransaction(currentTransactionStatus);
+        }
+        return transactionTemplate.execute(callback);
+    }
+
+}

+ 10 - 0
warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/context/TransactionMethod.java

@@ -0,0 +1,10 @@
+package com.yr.warehouse.admin.service.context;
+
+import org.apache.poi.ss.formula.functions.T;
+
+@FunctionalInterface
+public interface TransactionMethod {
+
+    T execute(T t);
+
+}

+ 16 - 0
warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/driver/DriverDeviceStockSnapshotService.java

@@ -0,0 +1,16 @@
+package com.yr.warehouse.admin.service.driver;
+
+import com.yr.warehouse.admin.driver.data.DriverDeviceStockSnapshot;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ * 司机设备库存快照 服务类
+ * </p>
+ *
+ * @author 
+ * @since 2025-12-04 15:21:10
+ */
+public interface DriverDeviceStockSnapshotService extends IService<DriverDeviceStockSnapshot> {
+
+}

+ 20 - 0
warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/driver/impl/DriverDeviceStockSnapshotServiceImpl.java

@@ -0,0 +1,20 @@
+package com.yr.warehouse.admin.service.driver.impl;
+
+import com.yr.warehouse.admin.driver.data.DriverDeviceStockSnapshot;
+import com.yr.warehouse.admin.driver.mapper.DriverDeviceStockSnapshotMapper;
+import com.yr.warehouse.admin.driver.service.DriverDeviceStockSnapshotService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 司机设备库存快照 服务实现类
+ * </p>
+ *
+ * @author 
+ * @since 2025-12-04 15:21:10
+ */
+@Service
+public class DriverDeviceStockSnapshotServiceImpl extends ServiceImpl<DriverDeviceStockSnapshotMapper, DriverDeviceStockSnapshot> implements DriverDeviceStockSnapshotService {
+
+}

+ 17 - 0
warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/statistics/DriverStatistics.java

@@ -0,0 +1,17 @@
+package com.yr.warehouse.admin.service.statistics;
+
+import java.time.LocalDate;
+
+/**
+ * 司机统计
+ */
+public interface DriverStatistics {
+
+
+    /**
+     * 货损统计
+     * @param statDate 统计日期
+     */
+    void cargoDamageStatistics(LocalDate statDate);
+
+}

+ 398 - 0
warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/statistics/impl/DriverStatisticsImpl.java

@@ -0,0 +1,398 @@
+package com.yr.warehouse.admin.service.statistics.impl;
+
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.supplychain.api.response.areastaff.AreaStaffResponse;
+import com.yr.bluecat.common.redis.RedisComponent;
+import com.yr.shopping.admin.support.response.DriverDeviceSalesResponse;
+import com.yr.warehouse.admin.common.menu.BoolEnum;
+import com.yr.warehouse.admin.component.areastaff.AreaStaffComponent;
+import com.yr.warehouse.admin.component.driver.DriverDeviceSalesComponent;
+import com.yr.warehouse.admin.driver.data.DriverDeviceStockSnapshot;
+import com.yr.warehouse.admin.driver.data.DriverEquipmentLossRecord;
+import com.yr.warehouse.admin.driver.data.DriverGoodsLossRecord;
+import com.yr.warehouse.admin.driver.data.DriverOnRouteDetailLog;
+import com.yr.warehouse.admin.driver.mapper.DriverDeviceStockSnapshotMapper;
+import com.yr.warehouse.admin.driver.mapper.DriverEquipmentLossRecordMapper;
+import com.yr.warehouse.admin.driver.mapper.DriverGoodsLossRecordMapper;
+import com.yr.warehouse.admin.driver.mapper.DriverOnRouteDetailLogMapper;
+import com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo;
+import com.yr.warehouse.admin.service.statistics.DriverStatistics;
+import com.yr.warehouse.admin.service.statistics.logic.DriverStatisticsMergeLogic;
+import com.yr.warehouse.admin.service.statistics.mapstruct.DriverStatisticsMapStruct;
+import com.yr.warehouse.admin.statistics.mapper.BillingQuantityStatisticsMapper;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 司机统计服务实现类
+ */
+@Slf4j
+@Service
+public class DriverStatisticsImpl implements DriverStatistics {
+    @Resource
+    private RedisComponent redisComponent;
+
+    @Resource
+    private AreaStaffComponent areaStaffComponent;
+
+    @Resource
+    private DriverDeviceSalesComponent driverDeviceSalesComponent;
+
+    @Resource
+    private DriverStatisticsMergeLogic driverStatisticsMergeLogic;
+
+    @Resource
+    private BillingQuantityStatisticsMapper billingQuantityStatisticsMapper;
+
+    @Resource
+    private DriverDeviceStockSnapshotMapper driverDeviceStockSnapshotMapper;
+
+    @Resource
+    private DriverGoodsLossRecordMapper driverGoodsLossRecordMapper;
+
+    @Resource
+    private DriverEquipmentLossRecordMapper driverEquipmentLossRecordMapper;
+
+    @Resource
+    private DriverOnRouteDetailLogMapper driverOnRouteDetailLogMapper;
+
+    @Override
+    public void cargoDamageStatistics(LocalDate statDate) {
+        // 开始时间 = statDate - 1 day 04:00:00
+        LocalDateTime beginTime = statDate.minusDays(1).atTime(4, 0, 0);
+        // 结束时间 = statDate 03:59:59
+        LocalDateTime endTime = statDate.atTime(3, 59, 59);
+        // 货损统计业务开始
+        log.info(String.format("司机货损统计开始:开始时间[%s], 结束时间[%s]", beginTime, endTime));
+
+        // 司机设备库存 - 不含商品id - 需锚定司机设备库存, 保证异常情况后重新查询时设备库存值不变
+        List<DriverGoodsOrderNumVo> deviceInventoryNumVos = queryDriverDeviceInventoryNum(beginTime, endTime);
+        // 司机拣货单开单数 - 含商品id
+        List<DriverGoodsOrderNumVo> pickingBillingQuantityNumVos = billingQuantityStatisticsMapper.queryPickingBillingQuantity(beginTime, endTime);
+        // 司机整件单开单数 - 含商品id
+        List<DriverGoodsOrderNumVo> aggregationBillingQuantityNumVos = billingQuantityStatisticsMapper.queryAggregationBillingQuantity(beginTime, endTime);
+        // 未出库取消数 - 含商品id
+        List<DriverGoodsOrderNumVo> unOutStockCancelNumVos = billingQuantityStatisticsMapper.queryUnOutStockCancelNum(beginTime, endTime);
+        // 已出库取消数 - 含商品id
+        List<DriverGoodsOrderNumVo> outStockCancelNumVos = billingQuantityStatisticsMapper.queryOutStockCancelNum(beginTime, endTime);
+        // 查询司机补货数 - 含商品id
+        List<DriverGoodsOrderNumVo> driverReplenishNumVos = billingQuantityStatisticsMapper.queryDriverReplenishNum(beginTime, endTime);
+        // 查询设备补货数 - 不含商品id
+        List<DriverGoodsOrderNumVo> deviceReplenishNumVos = billingQuantityStatisticsMapper.queryDeviceReplenishNum(beginTime, endTime);
+        // 查询司机回仓数 - 含商品id
+        List<DriverGoodsOrderNumVo> driverReturnNumVos = billingQuantityStatisticsMapper.queryDriverReturnNum(beginTime, endTime);
+        // 司机设备销量统计 - 不含商品id
+        List<DriverDeviceSalesResponse> driverDeviceSalesResponses = driverDeviceSalesComponent.list(beginTime, endTime);
+        List<DriverGoodsOrderNumVo> driverDeviceSalesNumVos = DriverStatisticsMapStruct.INSTANCE.driverDeviceSalesToVo(driverDeviceSalesResponses);
+
+        // 提取所有数据中的司机ID并去重
+        List<Long> areaStaffIds = driverStatisticsMergeLogic.getAreaStaffIds(driverDeviceSalesNumVos,
+                pickingBillingQuantityNumVos,
+                aggregationBillingQuantityNumVos,
+                unOutStockCancelNumVos,
+                outStockCancelNumVos,
+                driverReplenishNumVos,
+                deviceReplenishNumVos,
+                driverReturnNumVos);
+
+        List<AreaStaffResponse> areaStaffResponses = areaStaffComponent.searchByStaffIds(areaStaffIds);
+        Map<Long, AreaStaffResponse> areaStaffMap = areaStaffResponses.stream().collect(Collectors.toMap(AreaStaffResponse::getId, areaStaffResponse -> areaStaffResponse));
+
+        // 生成司机货损记录
+        List<DriverGoodsLossRecord> driverGoodsLossRecords = generatorDriverGoodsLossRecord(statDate,
+                areaStaffIds,
+                areaStaffMap,
+                driverStatisticsMergeLogic.groupByAreaStaffId(pickingBillingQuantityNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffId(aggregationBillingQuantityNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffId(unOutStockCancelNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffId(outStockCancelNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffId(driverReplenishNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffId(driverReturnNumVos));
+
+        driverGoodsLossRecordMapper.insertBatch(driverGoodsLossRecords);
+
+
+        // 生成司机设备货损记录
+        List<DriverEquipmentLossRecord> driverEquipmentLossRecords = generatorDriverEquipmentLossRecord(statDate,
+                areaStaffIds,
+                areaStaffMap,
+                driverStatisticsMergeLogic.groupByAreaStaffId(deviceReplenishNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffId(driverDeviceSalesNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffId(deviceInventoryNumVos)
+        );
+        driverEquipmentLossRecordMapper.insertBatch(driverEquipmentLossRecords);
+
+        List<DriverOnRouteDetailLog> driverOnRouteDetailLogs = generatorDriverOnRouteDetailLog(statDate,
+                driverGoodsLossRecords,
+                driverStatisticsMergeLogic.groupByAreaStaffGoods(pickingBillingQuantityNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffGoods(aggregationBillingQuantityNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffGoods(unOutStockCancelNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffGoods(outStockCancelNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffGoods(driverReplenishNumVos),
+                driverStatisticsMergeLogic.groupByAreaStaffGoods(driverReturnNumVos)
+        );
+
+        driverOnRouteDetailLogMapper.insertBatch(driverOnRouteDetailLogs);
+        log.info(String.format("司机货损统计结束:开始时间[%s], 结束时间[%s]", beginTime, endTime));
+    }
+
+    /**
+     * 获取司机设备库存数
+     * @return 司机设备库存数
+     */
+    private List<DriverGoodsOrderNumVo> queryDriverDeviceInventoryNum(LocalDateTime beginTime, LocalDateTime endTime) {
+        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+        String key = String.format("BILLING_QUANTITY_STATISTICS:%s-%s", dateTimeFormatter.format(beginTime), dateTimeFormatter.format(endTime));
+        String driverDeviceInventoryStr = redisComponent.getValue(key);
+
+        if (driverDeviceInventoryStr != null) {
+            return JSONArray.parseArray(driverDeviceInventoryStr, DriverGoodsOrderNumVo.class);
+        }
+
+        List<DriverDeviceStockSnapshot> driverDeviceStockSnapshots = driverDeviceStockSnapshotMapper.queryDriverDeviceInventoryNum(beginTime, endTime);
+        List<DriverGoodsOrderNumVo> result;
+
+        if (driverDeviceStockSnapshots == null || driverDeviceStockSnapshots.isEmpty()) {
+            result = billingQuantityStatisticsMapper.queryDriverDeviceInventoryNum();
+            // 持久化司机设备库存数
+            driverDeviceStockSnapshotMapper.insertBatch(beginTime, endTime, result);
+        } else {
+            result = driverDeviceStockSnapshots.stream().map(snapshot -> {
+                DriverGoodsOrderNumVo vo = new DriverGoodsOrderNumVo();
+                vo.setAreaStaffId(snapshot.getAreaStaffId());
+                vo.setTotalNum(snapshot.getTotalNum());
+                return vo;
+            }).collect(Collectors.toList());
+        }
+
+        // 缓存司机设备库存数
+        redisComponent.cacheValue(key, JSONObject.toJSONString(result));
+        return result;
+    }
+
+    /**
+     * 生成司机货损记录
+     * @param statDate 统计日期
+     * @param areaStaffIds 司机id
+     * @param driverGoodsOrderNumVoMap 拣货单
+     * @param aggregationBillingQuantityNumVoMap 整件单
+     * @param unOutStockCancelNumVoMap 未出库取消
+     * @param outStockCancelNumVoMap 已出库取消
+     * @param driverReplenishNumVoMap 补货
+     * @param driverReturnNumVoMap 回仓
+     * @return 司机货损记录
+     */
+    private List<DriverGoodsLossRecord> generatorDriverGoodsLossRecord(LocalDate statDate,
+                                                                       List<Long> areaStaffIds,
+                                                                       Map<Long, AreaStaffResponse> areaStaffMap,
+                                                                       Map<Long, Long> driverGoodsOrderNumVoMap,
+                                                                       Map<Long, Long> aggregationBillingQuantityNumVoMap,
+                                                                       Map<Long, Long> unOutStockCancelNumVoMap,
+                                                                       Map<Long, Long> outStockCancelNumVoMap,
+                                                                       Map<Long, Long> driverReplenishNumVoMap,
+                                                                       Map<Long, Long> driverReturnNumVoMap) {
+        List<DriverGoodsLossRecord> yesterdayDriverGoodsLossRecords = driverGoodsLossRecordMapper.searchByStatDate(statDate.minusDays(1));
+        Map<Long, DriverGoodsLossRecord> yesterdayDriverGoodsLossRecordMap;
+        if (null != yesterdayDriverGoodsLossRecords && !yesterdayDriverGoodsLossRecords.isEmpty()) {
+            yesterdayDriverGoodsLossRecordMap = yesterdayDriverGoodsLossRecords.stream().collect(Collectors.toMap(DriverGoodsLossRecord::getAreaStaffId, Function.identity()));
+        } else {
+            yesterdayDriverGoodsLossRecordMap = new HashMap<>();
+        }
+        return areaStaffIds.stream().map(areaStaffId -> {
+            DriverGoodsLossRecord yesterdayDriverGoodsLossRecord = yesterdayDriverGoodsLossRecordMap.get(areaStaffId);
+            Long yesterdayCurrentOnRouteNum = 0L;
+            if (yesterdayDriverGoodsLossRecord != null) {
+                if (BoolEnum.isNo(yesterdayDriverGoodsLossRecord.getUseVerified())) {
+                    yesterdayCurrentOnRouteNum = yesterdayDriverGoodsLossRecord.getCurrentOnRouteNum();
+                } else {
+                    yesterdayCurrentOnRouteNum = yesterdayDriverGoodsLossRecord.getVerifiedNum();
+                }
+            }
+
+            DriverGoodsLossRecord driverGoodsLossRecord = new DriverGoodsLossRecord();
+            driverGoodsLossRecord.setStatDate(statDate);
+            driverGoodsLossRecord.setYesterdayOnRouteNum(yesterdayCurrentOnRouteNum);
+            driverGoodsLossRecord.setAreaStaffId(areaStaffId);
+
+            AreaStaffResponse areaStaffResponse = areaStaffMap.get(areaStaffId);
+            if (null != areaStaffResponse) {
+                driverGoodsLossRecord.setOperatorId(areaStaffResponse.getOperatorId());
+                driverGoodsLossRecord.setOperatorChain(areaStaffResponse.getOperatorChain());
+                driverGoodsLossRecord.setAreaStaffId(areaStaffResponse.getId());
+            }
+            driverGoodsLossRecord.setPickingOrderCreateNum(driverStatisticsMergeLogic.getValueOrDefault(driverGoodsOrderNumVoMap, areaStaffId, 0L));
+            driverGoodsLossRecord.setWholeOrderCreateNum(driverStatisticsMergeLogic.getValueOrDefault(aggregationBillingQuantityNumVoMap, areaStaffId, 0L));
+            driverGoodsLossRecord.setUnshippedCancelNum(driverStatisticsMergeLogic.getValueOrDefault(unOutStockCancelNumVoMap, areaStaffId, 0L));
+            driverGoodsLossRecord.setShippedCancelNum(driverStatisticsMergeLogic.getValueOrDefault(outStockCancelNumVoMap, areaStaffId, 0L));
+            driverGoodsLossRecord.setReplenishNum(driverStatisticsMergeLogic.getValueOrDefault(driverReplenishNumVoMap, areaStaffId, 0L));
+            driverGoodsLossRecord.setReturnWarehouseNum(driverStatisticsMergeLogic.getValueOrDefault(driverReturnNumVoMap, areaStaffId, 0L));
+
+            // 当日在途 = 昨日在途 + 拣货单开单数 + 整件单开单数 - 司机补货数 - 司机回仓数 - 未出库取消数
+            Long nowDayCurrentOnRouteNum = yesterdayCurrentOnRouteNum + driverGoodsLossRecord.getPickingOrderCreateNum() + driverGoodsLossRecord.getWholeOrderCreateNum()
+                    - driverGoodsLossRecord.getReplenishNum() - driverGoodsLossRecord.getReturnWarehouseNum() - driverGoodsLossRecord.getUnshippedCancelNum();
+            driverGoodsLossRecord.setCurrentOnRouteNum(nowDayCurrentOnRouteNum);
+
+            return driverGoodsLossRecord;
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * 生成司机设备货损记录
+     * @param statDate 统计日期
+     * @param areaStaffIds 司机id
+     * @param driverGoodsOrderNumVoMap 设备补货数
+     * @param driverDeviceSalesNumVoMap 设备销售数
+     * @param deviceInventoryNumVoMap 设备库存数
+     * @return 司机设备货损记录
+     */
+    private List<DriverEquipmentLossRecord> generatorDriverEquipmentLossRecord(LocalDate statDate,
+                                                                               List<Long> areaStaffIds,
+                                                                               Map<Long, AreaStaffResponse> areaStaffMap,
+                                                                               Map<Long, Long> driverGoodsOrderNumVoMap,
+                                                                               Map<Long, Long> driverDeviceSalesNumVoMap,
+                                                                               Map<Long, Long> deviceInventoryNumVoMap
+    ) {
+
+        List<DriverEquipmentLossRecord> yesterdayDriverEquipmentLossRecords = driverEquipmentLossRecordMapper.searchByStatDate(statDate.minusDays(1));
+        Map<Long, DriverEquipmentLossRecord> yesterdayDriverEquipmentLossRecordMap;
+        if (null != yesterdayDriverEquipmentLossRecords && !yesterdayDriverEquipmentLossRecords.isEmpty()) {
+            yesterdayDriverEquipmentLossRecordMap = yesterdayDriverEquipmentLossRecords.stream().collect(Collectors.toMap(DriverEquipmentLossRecord::getAreaStaffId, Function.identity()));
+        } else {
+            yesterdayDriverEquipmentLossRecordMap = new HashMap<>();
+        }
+        return areaStaffIds.stream().map(areaStaffId -> {
+            DriverEquipmentLossRecord driverEquipmentLossRecord = new DriverEquipmentLossRecord();
+            driverEquipmentLossRecord.setStatDate(statDate);
+            AreaStaffResponse areaStaffResponse = areaStaffMap.get(areaStaffId);
+            if (null != areaStaffResponse) {
+                driverEquipmentLossRecord.setOperatorId(areaStaffResponse.getOperatorId());
+                driverEquipmentLossRecord.setOperatorChain(areaStaffResponse.getOperatorChain());
+                driverEquipmentLossRecord.setAreaStaffId(areaStaffResponse.getId());
+            }
+
+
+            DriverEquipmentLossRecord yesterdayDriverEquipmentLossRecord = yesterdayDriverEquipmentLossRecordMap.get(areaStaffId);
+            Long yesterdayEquipmentStockNum = 0L;
+            if (yesterdayDriverEquipmentLossRecord != null) {
+                yesterdayEquipmentStockNum = yesterdayDriverEquipmentLossRecord.getActualEquipmentStockNum();
+            }
+
+            driverEquipmentLossRecord.setEquipmentStockNum(yesterdayEquipmentStockNum);
+            driverEquipmentLossRecord.setEquipmentReplenishNum(driverStatisticsMergeLogic.getValueOrDefault(driverGoodsOrderNumVoMap, areaStaffId, 0L));
+            driverEquipmentLossRecord.setEquipmentSalesNum(driverStatisticsMergeLogic.getValueOrDefault(driverDeviceSalesNumVoMap, areaStaffId, 0L));
+            Long theoreticalEquipmentStockNum = yesterdayEquipmentStockNum + driverEquipmentLossRecord.getEquipmentReplenishNum() - driverEquipmentLossRecord.getEquipmentSalesNum();
+            driverEquipmentLossRecord.setTheoreticalEquipmentStockNum(theoreticalEquipmentStockNum);
+            driverEquipmentLossRecord.setActualEquipmentStockNum(driverStatisticsMergeLogic.getValueOrDefault(deviceInventoryNumVoMap, areaStaffId, 0L));
+            driverEquipmentLossRecord.setProfitLossNum(driverEquipmentLossRecord.getActualEquipmentStockNum() - theoreticalEquipmentStockNum);
+            return driverEquipmentLossRecord;
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * 生成司机在途明细日志
+     * @param statDate 统计日期
+     * @param driverGoodsLossRecords 货损记录列表
+     * @param pickingBillingQuantityNumVoMap 拣货
+     * @param aggregationBillingQuantityNumVoMap 整件
+     * @param unOutStockCancelNumVoMap 未出库取消
+     * @param outStockCancelNumVoMap 已出库取消
+     * @param driverReplenishNumVoMap 补货
+     * @param driverReturnNumVoMap 回仓
+     *
+     * @return
+     */
+    private List<DriverOnRouteDetailLog> generatorDriverOnRouteDetailLog(LocalDate statDate,
+                                                                         List<DriverGoodsLossRecord> driverGoodsLossRecords,
+                                                                         Map<Long, Map<Integer, Long>> pickingBillingQuantityNumVoMap,
+                                                                         Map<Long, Map<Integer, Long>> aggregationBillingQuantityNumVoMap,
+                                                                         Map<Long, Map<Integer, Long>> unOutStockCancelNumVoMap,
+                                                                         Map<Long, Map<Integer, Long>> outStockCancelNumVoMap,
+                                                                         Map<Long, Map<Integer, Long>> driverReplenishNumVoMap,
+                                                                         Map<Long, Map<Integer, Long>> driverReturnNumVoMap
+    ) {
+        List<DriverOnRouteDetailLog> yesterdayDriverOnRouteDetailLogs = driverOnRouteDetailLogMapper.searchByStatDate(statDate.minusDays(1));
+        Map<Long, Map<Integer, Long>> yesterdayDriverOnRouteDetailLogMap;
+        if (null != yesterdayDriverOnRouteDetailLogs && !yesterdayDriverOnRouteDetailLogs.isEmpty()) {
+            yesterdayDriverOnRouteDetailLogMap = yesterdayDriverOnRouteDetailLogs.stream()
+                    .collect(Collectors.groupingBy(
+                            DriverOnRouteDetailLog::getAreaStaffId, // 外层key:areaStaffId
+                            Collectors.toMap(
+                                    DriverOnRouteDetailLog::getGoodsId, // 内层key:goodsId
+                                    DriverOnRouteDetailLog::getTodayStockNum, // 内层value:原对象
+                                    (existing, replacement) -> replacement // 重复goodsId时覆盖(也可改为existing保留第一个)
+                            )
+                    ));
+        } else {
+            yesterdayDriverOnRouteDetailLogMap = new HashMap<>();
+        }
+
+        Map<Long, DriverGoodsLossRecord> driverGoodsLossRecordMap = driverGoodsLossRecords.stream().collect(Collectors.toMap(DriverGoodsLossRecord::getAreaStaffId, v -> v));
+
+        List<Map<Long, Map<Integer, Long>>> mapList = new ArrayList<>();
+        mapList.add(pickingBillingQuantityNumVoMap);
+        mapList.add(aggregationBillingQuantityNumVoMap);
+        mapList.add(unOutStockCancelNumVoMap);
+        mapList.add(outStockCancelNumVoMap);
+        mapList.add(driverReplenishNumVoMap);
+        mapList.add(driverReturnNumVoMap);
+
+        List<DriverOnRouteDetailLog> result = new ArrayList<>();
+
+        // 获取司机id及司机对应的商品ids
+        Map<Long, List<Integer>> driverGoodsIdsMap = driverStatisticsMergeLogic.groupByAreaStaffGoodsIds(mapList, driverGoodsLossRecords.size());
+        driverGoodsIdsMap.forEach((areaStaffId, goodsIds) -> {
+
+            Map<Integer, Long> yesterdayDriverDetailLogMap = yesterdayDriverOnRouteDetailLogMap.get(areaStaffId);
+
+            DriverGoodsLossRecord driverGoodsLossRecord = driverGoodsLossRecordMap.get(areaStaffId);
+
+            Map<Integer, Long> pickingBillingQuantityNumMap = pickingBillingQuantityNumVoMap.get(areaStaffId);
+            Map<Integer, Long> aggregationBillingQuantityNumMap = aggregationBillingQuantityNumVoMap.get(areaStaffId);
+            Map<Integer, Long> unOutStockCancelNumMap = unOutStockCancelNumVoMap.get(areaStaffId);
+            Map<Integer, Long> outStockCancelNumMap = outStockCancelNumVoMap.get(areaStaffId);
+            Map<Integer, Long> driverReplenishMap = driverReplenishNumVoMap.get(areaStaffId);
+            Map<Integer, Long> driverReturnNumMap = driverReturnNumVoMap.get(areaStaffId);
+
+            goodsIds.forEach(goodsId -> {
+                Long yesterdayStockNum = driverStatisticsMergeLogic.getValueOrDefault(yesterdayDriverDetailLogMap, goodsId, 0L);
+
+                Long pickingBillingQuantityNum = driverStatisticsMergeLogic.getValueOrDefault(pickingBillingQuantityNumMap, goodsId, 0L);
+                Long aggregationBillingQuantityNum = driverStatisticsMergeLogic.getValueOrDefault(aggregationBillingQuantityNumMap, goodsId, 0L);
+
+                DriverOnRouteDetailLog driverOnRouteDetailLog = new DriverOnRouteDetailLog();
+                driverOnRouteDetailLog.setOperatorId(driverGoodsLossRecord.getOperatorId());
+                driverOnRouteDetailLog.setOperatorChain(driverGoodsLossRecord.getOperatorChain());
+                driverOnRouteDetailLog.setAreaStaffId(areaStaffId);
+                driverOnRouteDetailLog.setStatDate(statDate);
+                driverOnRouteDetailLog.setGoodsLossRecordId(driverGoodsLossRecord.getId());
+                driverOnRouteDetailLog.setGoodsId(goodsId);
+
+                driverOnRouteDetailLog.setCreateNum(pickingBillingQuantityNum + aggregationBillingQuantityNum);
+                driverOnRouteDetailLog.setUnshippedCancelNum(driverStatisticsMergeLogic.getValueOrDefault(unOutStockCancelNumMap, goodsId, 0L));
+                driverOnRouteDetailLog.setShippedCancelNum(driverStatisticsMergeLogic.getValueOrDefault(outStockCancelNumMap, goodsId, 0L));
+                driverOnRouteDetailLog.setReplenishNum(driverStatisticsMergeLogic.getValueOrDefault(driverReplenishMap, goodsId, 0L));
+                driverOnRouteDetailLog.setReturnWarehouseNum(driverStatisticsMergeLogic.getValueOrDefault(driverReturnNumMap, goodsId, 0L));
+                driverOnRouteDetailLog.setYesterdayStockNum(yesterdayStockNum);
+
+                Long todayStockNum = driverOnRouteDetailLog.getYesterdayStockNum() + driverOnRouteDetailLog.getCreateNum() - driverOnRouteDetailLog.getUnshippedCancelNum() - driverOnRouteDetailLog.getReplenishNum() - driverOnRouteDetailLog.getReturnWarehouseNum();
+                driverOnRouteDetailLog.setTodayStockNum(todayStockNum);
+                Long profitLossNum = driverOnRouteDetailLog.getReplenishNum() > 0 ? 0 : -driverOnRouteDetailLog.getReplenishNum();
+                driverOnRouteDetailLog.setProfitLossNum(profitLossNum);
+                result.add(driverOnRouteDetailLog);
+            });
+        });
+        return result;
+    }
+}

+ 109 - 0
warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/statistics/logic/DriverStatisticsMergeLogic.java

@@ -0,0 +1,109 @@
+package com.yr.warehouse.admin.service.statistics.logic;
+
+import com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 司机统计合并逻辑
+ */
+@Component
+public class DriverStatisticsMergeLogic {
+
+    /**
+     * 基于司机id进行分组
+     * @return 获取areaStaffId对应的货单数量
+     */
+    public Map<Long, Long> groupByAreaStaffId(List<DriverGoodsOrderNumVo> driverGoodsOrderNumVos) {
+        if (driverGoodsOrderNumVos == null || driverGoodsOrderNumVos.isEmpty()) {
+            return new HashMap<>();
+        }
+        // 基于areaStaffId进行分组, 获取areaStaffId对应的货单数量
+        return driverGoodsOrderNumVos.stream().collect(Collectors.groupingBy(DriverGoodsOrderNumVo::getAreaStaffId, Collectors.summingLong(DriverGoodsOrderNumVo::getTotalNum)));
+    }
+
+    /**
+     * 基于司机id和商品id进行分组
+     * @return 获取areaStaffId对应的货单数量
+     */
+    public Map<Long, Map<Integer, Long>> groupByAreaStaffGoods(List<DriverGoodsOrderNumVo> driverGoodsOrderNumVos) {
+        if (driverGoodsOrderNumVos == null || driverGoodsOrderNumVos.isEmpty()) {
+            return new HashMap<>();
+        }
+        return driverGoodsOrderNumVos.stream().collect(Collectors.groupingBy(DriverGoodsOrderNumVo::getAreaStaffId, Collectors.groupingBy(DriverGoodsOrderNumVo::getGoodsId, Collectors.summingLong(DriverGoodsOrderNumVo::getTotalNum))));
+    }
+
+    /**
+     * 基于司机id和商品id进行分组
+     * @param mapList 列表
+     * @param initialMapCapacity 预计的初始容量
+     * @return 获取areaStaffId对应的货单数量
+     *
+     */
+    public Map<Long, List<Integer>> groupByAreaStaffGoodsIds(List<Map<Long, Map<Integer, Long>>> mapList, int initialMapCapacity) {
+        Map<Long, Set<Integer>> tempSetMap = new HashMap<>(initialMapCapacity);
+        for (Map<Long, Map<Integer, Long>> singleMap : mapList) {
+            // 空Map跳过
+            if (singleMap == null || singleMap.isEmpty()) {
+                continue;
+            }
+
+            for (Map.Entry<Long, Map<Integer, Long>> outerEntry : singleMap.entrySet()) {
+                Long outerKey = outerEntry.getKey();
+                Map<Integer, Long> innerMap = outerEntry.getValue();
+                if (innerMap == null || innerMap.isEmpty()) {
+                    continue;
+                }
+
+                Set<Integer> innerKeySet = tempSetMap.computeIfAbsent(outerKey, k -> {
+                    int initialSetCapacity = (int) Math.ceil(innerMap.size() / 0.75f);
+                    return new HashSet<>(initialSetCapacity);
+                });
+                // 添加
+                innerKeySet.addAll(innerMap.keySet());
+            }
+        }
+
+        Map<Long, List<Integer>> resultMap = new HashMap<>(tempSetMap.size());
+        for (Map.Entry<Long, Set<Integer>> entry : tempSetMap.entrySet()) {
+            List<Integer> innerList = new ArrayList<>(entry.getValue());
+            resultMap.put(entry.getKey(), innerList);
+        }
+        tempSetMap.clear();
+        return resultMap;
+    }
+
+    /**
+     * 获取司机id列表
+     * @return 司机id列表
+     */
+    @SafeVarargs
+    public final List<Long> getAreaStaffIds(List<DriverGoodsOrderNumVo>... driverGoodsOrderNumVosList) {
+        return Stream.of(driverGoodsOrderNumVosList)
+                .flatMap(List::stream)
+                .map(DriverGoodsOrderNumVo::getAreaStaffId)
+                .distinct()
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 获取Map中的值,如果为null则返回默认值
+     * @param map Map
+     * @param key key
+     * @param defaultValue 默认值
+     * @return 值
+     */
+    public <T, V> V getValueOrDefault(Map<T, V> map, T key, V defaultValue) {
+        if (map == null) {
+            return defaultValue;
+        }
+        V val = map.get(key);
+        if (val == null) {
+            return defaultValue;
+        }
+        return val;
+    }
+}

+ 28 - 0
warehouse-admin-server/src/main/java/com/yr/warehouse/admin/service/statistics/mapstruct/DriverStatisticsMapStruct.java

@@ -0,0 +1,28 @@
+package com.yr.warehouse.admin.service.statistics.mapstruct;
+
+import com.yr.shopping.admin.support.response.DriverDeviceSalesResponse;
+import com.yr.warehouse.admin.replenish.vo.DriverGoodsOrderNumVo;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Mappings;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 司机统计
+ */
+@Mapper
+public interface DriverStatisticsMapStruct {
+
+    DriverStatisticsMapStruct INSTANCE = Mappers.getMapper(DriverStatisticsMapStruct.class);
+
+
+    List<DriverGoodsOrderNumVo> driverDeviceSalesToVo(List<DriverDeviceSalesResponse> driverDeviceSalesResponses);
+
+    @Mappings({
+            @Mapping(source = "areaStaffId", target = "areaStaffId"),
+            @Mapping(source = "salesVolume", target = "totalNum"),
+    })
+    DriverGoodsOrderNumVo driverDeviceSalesToVo(DriverDeviceSalesResponse driverDeviceSalesResponse);
+}