feat: 库存入库/出库字段全链路、采购台账申请提交时间、储罐租客信息、油库储罐模块
已添加18个文件
已修改13个文件
1804 ■■■■■ 文件已修改
bin/run-file-service.bat 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260909_add_logistics_method_to_purchase_ledger.sql 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260909_add_pickup_method_to_purchase_ledger.sql 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260909_add_purchase_oil_product_to_purchase_ledger.sql 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260911_add_application_submit_time_to_purchase_ledger.sql 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260911_add_oil_out_fields_to_stock_out_record.sql 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260911_add_tenant_info_to_tank_info.sql 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260911_add_unload_and_weighing_fields_to_stock_in_record.sql 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/OilDepotController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/controller/TankInfoController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/mapper/OilDepotMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/mapper/TankInfoMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/OilDepot.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/pojo/TankInfo.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/IOilDepotService.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/ITankInfoService.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/OilDepotServiceImpl.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/basic/service/impl/TankInfoServiceImpl.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/controller/StockInventoryController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/dto/StockUninventoryDto.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/pojo/StockInRecord.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/StockInventoryService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/purchase/PurchaseLedgerMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInventoryMapper.xml 458 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
bin/run-file-service.bat
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
@echo off
echo.
echo [INFO] Starting file service with embedded Tomcat on port 7006 (same app, dev profile).
echo [INFO] Business backend stays on 7005; file preview/download URLs point to this instance.
echo.
%~d0
cd %~dp0
cd ..
title %cd% - file service (7006)
rem Project targets Java 25; use JDK 25 if present, otherwise rely on global JAVA_HOME.
if exist "D:\develop\JDK.25\bin\java.exe" set JAVA_HOME=D:\develop\JDK.25
set MAVEN_OPTS=%MAVEN_OPTS% -Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
call mvn spring-boot:run -Dmaven.test.skip=true -Dspring-boot.run.arguments="--server.port=7006"
pause
doc/20260909_add_logistics_method_to_purchase_ledger.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
-- é‡‡è´­å°è´¦æ–°å¢žâ€œç‰©æµæ–¹å¼â€å­—段(厂家配送、委外物流、客户自提)
-- æ‰§è¡Œåº“:product-inventory-management(各租户库同)
ALTER TABLE `purchase_ledger`
    ADD COLUMN `logistics_method` varchar(20) DEFAULT NULL COMMENT '物流方式:厂家配送、委外物流、客户自提' AFTER `purchase_oil_product`;
doc/20260909_add_pickup_method_to_purchase_ledger.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
-- é‡‡è´­å°è´¦æ–°å¢žâ€œæè´§æ–¹å¼â€å­—段(暂不提货、提货)
-- æ‰§è¡Œåº“:product-inventory-management(各租户库同)
ALTER TABLE `purchase_ledger`
    ADD COLUMN `pickup_method` varchar(20) DEFAULT NULL COMMENT '提货方式:暂不提货、提货' AFTER `logistics_method`;
doc/20260909_add_purchase_oil_product_to_purchase_ledger.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
-- é‡‡è´­å°è´¦æ–°å¢žâ€œé‡‡è´­æ²¹å“â€å­—段(汽#92、汽#95、柴#0、柴#-35)
-- æ‰§è¡Œåº“:product-inventory-management(各租户库同)
ALTER TABLE `purchase_ledger`
    ADD COLUMN `purchase_oil_product` varchar(20) DEFAULT NULL COMMENT '采购油品:汽#92、汽#95、柴#0、柴#-35' AFTER `purchase_category`;
doc/20260911_add_application_submit_time_to_purchase_ledger.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
-- é‡‡è´­å°è´¦æ–°å¢žâ€œç”³è¯·æäº¤æ—¶é—´â€å­—段
-- æ‰§è¡Œåº“:product-inventory-management(各租户库同)
ALTER TABLE `purchase_ledger`
    ADD COLUMN `application_submit_time` datetime DEFAULT NULL COMMENT '申请提交时间' AFTER `purchase_order_status`;
doc/20260911_add_oil_out_fields_to_stock_out_record.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
-- å‡ºåº“记录新增“油品出库”表单字段(出库信息/装油操作信息/过磅数据/物流信息)
-- æ‰§è¡Œåº“:product-inventory-management(各租户库同)
ALTER TABLE `stock_out_record`
    ADD COLUMN `out_category` varchar(20) DEFAULT NULL COMMENT '出库类别:客存/代储/销售' AFTER `record_type`,
    ADD COLUMN `oil_depot_id` bigint DEFAULT NULL COMMENT '储库id' AFTER `out_category`,
    ADD COLUMN `oil_depot_name` varchar(100) DEFAULT NULL COMMENT '储库名称' AFTER `oil_depot_id`,
    ADD COLUMN `tank_id` bigint DEFAULT NULL COMMENT '储罐id' AFTER `oil_depot_name`,
    ADD COLUMN `tank_no` varchar(50) DEFAULT NULL COMMENT '储罐号' AFTER `tank_id`,
    ADD COLUMN `out_date` date DEFAULT NULL COMMENT '出库日期' AFTER `tank_no`,
    ADD COLUMN `oil_product` varchar(20) DEFAULT NULL COMMENT '油品' AFTER `out_date`,
    ADD COLUMN `out_tonnage` decimal(14,2) DEFAULT NULL COMMENT '出库吨位(吨)' AFTER `oil_product`,
    ADD COLUMN `customer_id` bigint DEFAULT NULL COMMENT '客户id' AFTER `out_tonnage`,
    ADD COLUMN `customer_name` varchar(100) DEFAULT NULL COMMENT '客户名称' AFTER `customer_id`,
    ADD COLUMN `contact_person` varchar(50) DEFAULT NULL COMMENT '客户对接人' AFTER `customer_name`,
    ADD COLUMN `contact_phone` varchar(20) DEFAULT NULL COMMENT '对接人电话' AFTER `contact_person`,
    ADD COLUMN `loading_inspection_status` varchar(20) DEFAULT NULL COMMENT '车体/铅封检验是否正常' AFTER `contact_phone`,
    ADD COLUMN `loading_crane_position` varchar(20) DEFAULT NULL COMMENT '装车鹤位' AFTER `loading_inspection_status`,
    ADD COLUMN `loading_photos` text COMMENT '装油关键环节与车辆关键位置拍照(JSON数组)' AFTER `loading_crane_position`,
    ADD COLUMN `density` varchar(20) DEFAULT NULL COMMENT '密度(kg/L)' AFTER `loading_photos`,
    ADD COLUMN `gross_weight` decimal(14,2) DEFAULT NULL COMMENT '毛重(吨)' AFTER `density`,
    ADD COLUMN `tare_weight` decimal(14,2) DEFAULT NULL COMMENT '皮重(吨)' AFTER `gross_weight`,
    ADD COLUMN `net_weight` decimal(14,2) DEFAULT NULL COMMENT '净重(吨)' AFTER `tare_weight`,
    ADD COLUMN `loss_quantity` decimal(14,2) DEFAULT NULL COMMENT '亏量(吨)' AFTER `net_weight`,
    ADD COLUMN `actual_volume` decimal(14,2) DEFAULT NULL COMMENT '实际入库体积(升)' AFTER `loss_quantity`,
    ADD COLUMN `pound_photos` text COMMENT '磅单上传(JSON数组)' AFTER `actual_volume`,
    ADD COLUMN `driver_name` varchar(50) DEFAULT NULL COMMENT '司机姓名' AFTER `pound_photos`,
    ADD COLUMN `driver_phone` varchar(20) DEFAULT NULL COMMENT '司机电话' AFTER `driver_name`,
    ADD COLUMN `escort_name` varchar(50) DEFAULT NULL COMMENT '押运员姓名' AFTER `driver_phone`,
    ADD COLUMN `escort_phone` varchar(20) DEFAULT NULL COMMENT '押运员电话' AFTER `escort_name`,
    ADD COLUMN `truck_plate_no` varchar(20) DEFAULT NULL COMMENT '车牌号' AFTER `escort_phone`,
    ADD COLUMN `trailer_plate_no` varchar(20) DEFAULT NULL COMMENT '车挂牌号' AFTER `truck_plate_no`;
doc/20260911_add_tenant_info_to_tank_info.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
-- å‚¨ç½ä¿¡æ¯è¡¨æ–°å¢žâ€œç§Ÿå®¢ä¿¡æ¯â€å­—段
-- æ‰§è¡Œåº“:product-inventory-management(各租户库同)
ALTER TABLE `tank_info`
    ADD COLUMN `tenant_info` varchar(255) DEFAULT NULL COMMENT '租客信息' AFTER `remaining`;
doc/20260911_add_unload_and_weighing_fields_to_stock_in_record.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,13 @@
-- å…¥åº“记录新增“卸油操作信息”与“过磅数据”字段
-- æ‰§è¡Œåº“:product-inventory-management(各租户库同)
ALTER TABLE `stock_in_record`
    ADD COLUMN `tank_inspection_status` varchar(20) DEFAULT NULL COMMENT '车体/铅封检验是否正常:正常/异常' AFTER `trailer_plate_no`,
    ADD COLUMN `unloading_crane_position` varchar(20) DEFAULT NULL COMMENT '卸车鹤位:1#撬装/2#撬装/临时泵' AFTER `tank_inspection_status`,
    ADD COLUMN `density` varchar(20) DEFAULT NULL COMMENT '密度(kg/L)' AFTER `unloading_crane_position`,
    ADD COLUMN `unload_photos` text COMMENT '卸油关键环节与车辆关键位置拍照(JSON数组)' AFTER `density`,
    ADD COLUMN `gross_weight` decimal(14,2) DEFAULT NULL COMMENT '毛重(吨)' AFTER `unload_photos`,
    ADD COLUMN `tare_weight` decimal(14,2) DEFAULT NULL COMMENT '皮重(吨)' AFTER `gross_weight`,
    ADD COLUMN `net_weight` decimal(14,2) DEFAULT NULL COMMENT '净重(吨)' AFTER `tare_weight`,
    ADD COLUMN `loss_quantity` decimal(14,2) DEFAULT NULL COMMENT '亏量(吨)' AFTER `net_weight`,
    ADD COLUMN `actual_volume` decimal(14,2) DEFAULT NULL COMMENT '实际入库体积(升)' AFTER `loss_quantity`,
    ADD COLUMN `pound_photos` text COMMENT '磅单上传(JSON数组)' AFTER `actual_volume`;
src/main/java/com/ruoyi/basic/controller/OilDepotController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
package com.ruoyi.basic.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.pojo.OilDepot;
import com.ruoyi.basic.service.IOilDepotService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * æ²¹åº“信息Controller
 *
 * @author ruoyi
 * @date 2026-09-10
 */
@RestController
@RequestMapping("/basic/oilDepot")
@AllArgsConstructor
public class OilDepotController {
    private IOilDepotService oilDepotService;
    /**
     * åˆ†é¡µæŸ¥è¯¢æ²¹åº“信息列表
     */
    @GetMapping("/list")
    public R list(Page<OilDepot> page, OilDepot oilDepot) {
        IPage<OilDepot> result = oilDepotService.selectOilDepotList(page, oilDepot);
        return R.ok(result);
    }
    /**
     * æŸ¥è¯¢å…¨éƒ¨æ²¹åº“信息(供下拉选择)
     */
    @GetMapping("/all")
    public R all() {
        return R.ok(oilDepotService.selectAll());
    }
    /**
     * èŽ·å–æ²¹åº“ä¿¡æ¯è¯¦ç»†ä¿¡æ¯
     */
    @GetMapping(value = "/{id}")
    public R getInfo(@PathVariable("id") Long id) {
        return R.ok(oilDepotService.selectOilDepotById(id));
    }
    /**
     * æ–°å¢žæ²¹åº“信息
     */
    @Log(title = "油库信息", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public R add(@RequestBody OilDepot oilDepot) {
        return R.ok(oilDepotService.insertOilDepot(oilDepot));
    }
    /**
     * ä¿®æ”¹æ²¹åº“信息
     */
    @Log(title = "油库信息", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    public R edit(@RequestBody OilDepot oilDepot) {
        return R.ok(oilDepotService.updateOilDepot(oilDepot));
    }
    /**
     * åˆ é™¤æ²¹åº“信息
     */
    @Log(title = "油库信息", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public R remove(@RequestBody Long[] ids) {
        if (ids == null || ids.length == 0) {
            return R.fail("请传入要删除的ID");
        }
        return R.ok(oilDepotService.deleteOilDepotByIds(ids));
    }
}
src/main/java/com/ruoyi/basic/controller/TankInfoController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
package com.ruoyi.basic.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basic.pojo.TankInfo;
import com.ruoyi.basic.service.ITankInfoService;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * å‚¨ç½ä¿¡æ¯Controller
 *
 * @author ruoyi
 * @date 2026-09-10
 */
@RestController
@RequestMapping("/basic/tankInfo")
@AllArgsConstructor
public class TankInfoController {
    private ITankInfoService tankInfoService;
    /**
     * åˆ†é¡µæŸ¥è¯¢å‚¨ç½ä¿¡æ¯åˆ—表
     */
    @GetMapping("/list")
    public R list(Page<TankInfo> page, TankInfo tankInfo) {
        IPage<TankInfo> result = tankInfoService.selectTankInfoList(page, tankInfo);
        return R.ok(result);
    }
    /**
     * æŸ¥è¯¢å…¨éƒ¨å‚¨ç½ä¿¡æ¯ï¼ˆä¾›ä¸‹æ‹‰é€‰æ‹©ï¼‰
     */
    @GetMapping("/all")
    public R all() {
        return R.ok(tankInfoService.selectAll());
    }
    /**
     * èŽ·å–å‚¨ç½ä¿¡æ¯è¯¦ç»†ä¿¡æ¯
     */
    @GetMapping(value = "/{id}")
    public R getInfo(@PathVariable("id") Long id) {
        return R.ok(tankInfoService.selectTankInfoById(id));
    }
    /**
     * æ–°å¢žå‚¨ç½ä¿¡æ¯
     */
    @Log(title = "储罐信息", businessType = BusinessType.INSERT)
    @PostMapping("/add")
    public R add(@RequestBody TankInfo tankInfo) {
        return R.ok(tankInfoService.insertTankInfo(tankInfo));
    }
    /**
     * ä¿®æ”¹å‚¨ç½ä¿¡æ¯
     */
    @Log(title = "储罐信息", businessType = BusinessType.UPDATE)
    @PostMapping("/update")
    public R edit(@RequestBody TankInfo tankInfo) {
        return R.ok(tankInfoService.updateTankInfo(tankInfo));
    }
    /**
     * åˆ é™¤å‚¨ç½ä¿¡æ¯
     */
    @Log(title = "储罐信息", businessType = BusinessType.DELETE)
    @DeleteMapping("/del")
    public R remove(@RequestBody Long[] ids) {
        if (ids == null || ids.length == 0) {
            return R.fail("请传入要删除的ID");
        }
        return R.ok(tankInfoService.deleteTankInfoByIds(ids));
    }
}
src/main/java/com/ruoyi/basic/mapper/OilDepotMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package com.ruoyi.basic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.basic.pojo.OilDepot;
import org.apache.ibatis.annotations.Mapper;
/**
 * æ²¹åº“信息Mapper接口
 *
 * @author ruoyi
 * @date 2026-09-10
 */
@Mapper
public interface OilDepotMapper extends BaseMapper<OilDepot> {
}
src/main/java/com/ruoyi/basic/mapper/TankInfoMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
package com.ruoyi.basic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.basic.pojo.TankInfo;
import org.apache.ibatis.annotations.Mapper;
/**
 * å‚¨ç½ä¿¡æ¯Mapper接口
 *
 * @author ruoyi
 * @date 2026-09-10
 */
@Mapper
public interface TankInfoMapper extends BaseMapper<TankInfo> {
}
src/main/java/com/ruoyi/basic/pojo/OilDepot.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
package com.ruoyi.basic.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
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 com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
 * æ²¹åº“信息对象 oil_depot
 *
 * @author ruoyi
 * @date 2026-09-10
 */
@TableName(value = "oil_depot")
@Data
public class OilDepot implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * æ²¹åº“id
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    /**
     * æ²¹åº“名称
     */
    private String depotName;
    /**
     * è´Ÿè´£äºº
     */
    private String chargePerson;
    /**
     * è”系电话
     */
    private String phone;
    /**
     * å¯ç”¨æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate enableDate;
    /**
     * æ‰€å±žå…¬å¸
     */
    private String company;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    /**
     * åˆ›å»ºäºº
     */
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime updateTime;
    /**
     * æ›´æ–°äºº
     */
    private Long updateUser;
    /**
     * ç§Ÿæˆ·id
     */
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    /**
     * éƒ¨é—¨id
     */
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
}
src/main/java/com/ruoyi/basic/pojo/TankInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package com.ruoyi.basic.pojo;
import com.baomidou.mybatisplus.annotation.FieldFill;
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 lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * å‚¨ç½ä¿¡æ¯å¯¹è±¡ tank_info
 *
 * @author ruoyi
 * @date 2026-09-10
 */
@TableName(value = "tank_info")
@Data
public class TankInfo implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * å‚¨ç½id
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    /**
     * å‚¨ç½å·
     */
    private String tankNo;
    /**
     * å¤§ç±»
     */
    private String category;
    /**
     * æ²¹å“
     */
    private String oilProduct;
    /**
     * ç½å®¹
     */
    private BigDecimal capacity;
    /**
     * åº“存余量(m3)
     */
    private BigDecimal remaining;
    /**
     * ç§Ÿå®¢ä¿¡æ¯
     */
    private String tenantInfo;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    /**
     * åˆ›å»ºäºº
     */
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    /**
     * æ›´æ–°æ—¶é—´
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime updateTime;
    /**
     * æ›´æ–°äºº
     */
    private Long updateUser;
    /**
     * ç§Ÿæˆ·id
     */
    @TableField(fill = FieldFill.INSERT)
    private Long tenantId;
    /**
     * éƒ¨é—¨id
     */
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
}
src/main/java/com/ruoyi/basic/service/IOilDepotService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package com.ruoyi.basic.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.basic.pojo.OilDepot;
import java.util.List;
/**
 * æ²¹åº“信息Service接口
 *
 * @author ruoyi
 * @date 2026-09-10
 */
public interface IOilDepotService extends IService<OilDepot> {
    /**
     * åˆ†é¡µæŸ¥è¯¢æ²¹åº“信息列表
     *
     * @param page åˆ†é¡µå‚æ•°
     * @param oilDepot æŸ¥è¯¢æ¡ä»¶
     * @return æ²¹åº“信息集合
     */
    IPage<OilDepot> selectOilDepotList(Page<OilDepot> page, OilDepot oilDepot);
    /**
     * æŸ¥è¯¢å…¨éƒ¨æ²¹åº“信息(供下拉选择)
     *
     * @return æ²¹åº“信息集合
     */
    List<OilDepot> selectAll();
    /**
     * æŸ¥è¯¢æ²¹åº“信息
     *
     * @param id æ²¹åº“信息主键
     * @return æ²¹åº“信息
     */
    OilDepot selectOilDepotById(Long id);
    /**
     * æ–°å¢žæ²¹åº“信息
     *
     * @param oilDepot æ²¹åº“信息
     * @return ç»“æžœ
     */
    boolean insertOilDepot(OilDepot oilDepot);
    /**
     * ä¿®æ”¹æ²¹åº“信息
     *
     * @param oilDepot æ²¹åº“信息
     * @return ç»“æžœ
     */
    boolean updateOilDepot(OilDepot oilDepot);
    /**
     * æ‰¹é‡åˆ é™¤æ²¹åº“信息
     *
     * @param ids éœ€è¦åˆ é™¤çš„æ²¹åº“信息主键集合
     * @return ç»“æžœ
     */
    boolean deleteOilDepotByIds(Long[] ids);
}
src/main/java/com/ruoyi/basic/service/ITankInfoService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package com.ruoyi.basic.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.basic.pojo.TankInfo;
import java.util.List;
/**
 * å‚¨ç½ä¿¡æ¯Service接口
 *
 * @author ruoyi
 * @date 2026-09-10
 */
public interface ITankInfoService extends IService<TankInfo> {
    /**
     * åˆ†é¡µæŸ¥è¯¢å‚¨ç½ä¿¡æ¯åˆ—表
     *
     * @param page åˆ†é¡µå‚æ•°
     * @param tankInfo æŸ¥è¯¢æ¡ä»¶
     * @return å‚¨ç½ä¿¡æ¯é›†åˆ
     */
    IPage<TankInfo> selectTankInfoList(Page<TankInfo> page, TankInfo tankInfo);
    /**
     * æŸ¥è¯¢å…¨éƒ¨å‚¨ç½ä¿¡æ¯ï¼ˆä¾›ä¸‹æ‹‰é€‰æ‹©ï¼‰
     *
     * @return å‚¨ç½ä¿¡æ¯é›†åˆ
     */
    List<TankInfo> selectAll();
    /**
     * æŸ¥è¯¢å‚¨ç½ä¿¡æ¯
     *
     * @param id å‚¨ç½ä¿¡æ¯ä¸»é”®
     * @return å‚¨ç½ä¿¡æ¯
     */
    TankInfo selectTankInfoById(Long id);
    /**
     * æ–°å¢žå‚¨ç½ä¿¡æ¯
     *
     * @param tankInfo å‚¨ç½ä¿¡æ¯
     * @return ç»“æžœ
     */
    boolean insertTankInfo(TankInfo tankInfo);
    /**
     * ä¿®æ”¹å‚¨ç½ä¿¡æ¯
     *
     * @param tankInfo å‚¨ç½ä¿¡æ¯
     * @return ç»“æžœ
     */
    boolean updateTankInfo(TankInfo tankInfo);
    /**
     * æ‰¹é‡åˆ é™¤å‚¨ç½ä¿¡æ¯
     *
     * @param ids éœ€è¦åˆ é™¤çš„储罐信息主键集合
     * @return ç»“æžœ
     */
    boolean deleteTankInfoByIds(Long[] ids);
}
src/main/java/com/ruoyi/basic/service/impl/OilDepotServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package com.ruoyi.basic.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.mapper.OilDepotMapper;
import com.ruoyi.basic.pojo.OilDepot;
import com.ruoyi.basic.service.IOilDepotService;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * æ²¹åº“信息Service业务层处理
 *
 * @author ruoyi
 * @date 2026-09-10
 */
@Service
public class OilDepotServiceImpl extends ServiceImpl<OilDepotMapper, OilDepot> implements IOilDepotService {
    @Override
    public IPage<OilDepot> selectOilDepotList(Page<OilDepot> page, OilDepot oilDepot) {
        LambdaQueryWrapper<OilDepot> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(oilDepot.getDepotName()), OilDepot::getDepotName, oilDepot.getDepotName())
                .like(StringUtils.isNotBlank(oilDepot.getCompany()), OilDepot::getCompany, oilDepot.getCompany())
                .orderByAsc(OilDepot::getId);
        return this.page(page, queryWrapper);
    }
    @Override
    public List<OilDepot> selectAll() {
        LambdaQueryWrapper<OilDepot> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByAsc(OilDepot::getId);
        return this.list(queryWrapper);
    }
    @Override
    public OilDepot selectOilDepotById(Long id) {
        return this.getById(id);
    }
    @Override
    public boolean insertOilDepot(OilDepot oilDepot) {
        return this.save(oilDepot);
    }
    @Override
    public boolean updateOilDepot(OilDepot oilDepot) {
        return this.updateById(oilDepot);
    }
    @Override
    public boolean deleteOilDepotByIds(Long[] ids) {
        return this.removeByIds(List.of(ids));
    }
}
src/main/java/com/ruoyi/basic/service/impl/TankInfoServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package com.ruoyi.basic.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.mapper.TankInfoMapper;
import com.ruoyi.basic.pojo.TankInfo;
import com.ruoyi.basic.service.ITankInfoService;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * å‚¨ç½ä¿¡æ¯Service业务层处理
 *
 * @author ruoyi
 * @date 2026-09-10
 */
@Service
public class TankInfoServiceImpl extends ServiceImpl<TankInfoMapper, TankInfo> implements ITankInfoService {
    @Override
    public IPage<TankInfo> selectTankInfoList(Page<TankInfo> page, TankInfo tankInfo) {
        LambdaQueryWrapper<TankInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(tankInfo.getTankNo()), TankInfo::getTankNo, tankInfo.getTankNo())
                .like(StringUtils.isNotBlank(tankInfo.getCategory()), TankInfo::getCategory, tankInfo.getCategory())
                .orderByAsc(TankInfo::getId);
        return this.page(page, queryWrapper);
    }
    @Override
    public List<TankInfo> selectAll() {
        LambdaQueryWrapper<TankInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByAsc(TankInfo::getId);
        return this.list(queryWrapper);
    }
    @Override
    public TankInfo selectTankInfoById(Long id) {
        return this.getById(id);
    }
    @Override
    public boolean insertTankInfo(TankInfo tankInfo) {
        return this.save(tankInfo);
    }
    @Override
    public boolean updateTankInfo(TankInfo tankInfo) {
        return this.updateById(tankInfo);
    }
    @Override
    public boolean deleteTankInfoByIds(Long[] ids) {
        return this.removeByIds(List.of(ids));
    }
}
src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java
@@ -98,6 +98,13 @@
    private Date entryDate;
    /**
     * ç”³è¯·æäº¤æ—¶é—´
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date applicationSubmitTime;
    /**
     * ç­¾è®¢æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
@@ -194,6 +201,30 @@
    @Schema(description = "付款方式")
    private String paymentMethod;
    @Schema(description = "采购类别:客存、代储、采购入库、采购直销")
    private String purchaseCategory;
    @Schema(description = "采购油品:汽#92、汽#95、柴#0、柴#-35")
    private String purchaseOilProduct;
    @Schema(description = "物流方式:厂家配送、委外物流、客户自提")
    private String logisticsMethod;
    @Schema(description = "提货方式:暂不提货、提货")
    private String pickupMethod;
    @Schema(description = "采购油库id")
    private Long oilDepotId;
    @Schema(description = "采购油库名称")
    private String oilDepotName;
    @Schema(description = "客户名称")
    private String customerName;
    @Schema(description = "最佳到站日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date bestArrivalDate;
    @Schema(description = "采购数量(吨)")
    private Integer purchaseQuantity;
    @Schema(description = "采购申请订单状态")
    private String purchaseOrderStatus;
    @Schema(description = "审批状态")
    private Integer approvalStatus;
    @Schema(description = "模板名称")
src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java
@@ -73,6 +73,12 @@
    @Excel(name = "录入日期", width = 30, dateFormat = "yyyy-MM-dd")
    private Date entryDate;
    /**
     * ç”³è¯·æäº¤æ—¶é—´
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date applicationSubmitTime;
    /**
     * ç­¾è®¢æ—¥æœŸ
@@ -148,6 +154,72 @@
    @Schema(description = "付款方式")
    private String paymentMethod;
    /**
     * é‡‡è´­ç±»åˆ«ï¼šå®¢å­˜ã€ä»£å‚¨ã€é‡‡è´­å…¥åº“、采购直销
     */
    @Schema(description = "采购类别")
    @Excel(name = "采购类别")
    private String purchaseCategory;
    /**
     * é‡‡è´­æ²¹å“ï¼šæ±½#92、汽#95、柴#0、柴#-35
     */
    @Schema(description = "采购油品")
    @Excel(name = "采购油品")
    private String purchaseOilProduct;
    /**
     * ç‰©æµæ–¹å¼ï¼šåŽ‚å®¶é…é€ã€å§”å¤–ç‰©æµã€å®¢æˆ·è‡ªæ
     */
    @Schema(description = "物流方式")
    @Excel(name = "物流方式")
    private String logisticsMethod;
    /**
     * æè´§æ–¹å¼ï¼šæš‚不提货、提货
     */
    @Schema(description = "提货方式")
    @Excel(name = "提货方式")
    private String pickupMethod;
    /**
     * é‡‡è´­æ²¹åº“id
     */
    @Schema(description = "采购油库id")
    private Long oilDepotId;
    /**
     * é‡‡è´­æ²¹åº“名称
     */
    @Schema(description = "采购油库名称")
    @Excel(name = "采购油库")
    private String oilDepotName;
    /**
     * å®¢æˆ·åç§°
     */
    @Schema(description = "客户名称")
    private String customerName;
    /**
     * æœ€ä½³åˆ°ç«™æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Schema(description = "最佳到站日期")
    private Date bestArrivalDate;
    /**
     * é‡‡è´­æ•°é‡ï¼ˆå¨ï¼‰
     */
    @Schema(description = "采购数量(吨)")
    private Integer purchaseQuantity;
    /**
     * é‡‡è´­ç”³è¯·è®¢å•状态:已申请,未装车 / ä»¥å‡ºåŽ‚ï¼Œæœªå…¥åº“ / å·²å…¥åº“
     */
    @Schema(description = "采购申请订单状态")
    private String purchaseOrderStatus;
    @Schema(description = "审批状态  1-待审核,2-审批中,3-审批通过,4-审批失败,5表示是模板")
    @Excel(name = "审批状态", readConverterExp = "1=待审核,2=审批中,3=审批通过,4=审批失败")
    private Integer approvalStatus;
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -16,9 +16,11 @@
import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
import com.ruoyi.basic.enums.ApplicationTypeEnum;
import com.ruoyi.basic.enums.RecordTypeEnum;
import com.ruoyi.basic.mapper.OilDepotMapper;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.mapper.SupplierManageMapper;
import com.ruoyi.basic.pojo.OilDepot;
import com.ruoyi.basic.pojo.Product;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.pojo.SupplierManage;
@@ -118,6 +120,7 @@
    private final StockInRecordService stockInRecordService;
    private final StockUtils stockUtils;
    private final ApprovalTemplateMapper approvalTemplateMapper;
    private final OilDepotMapper oilDepotMapper;
    @Override
    public List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger) {
@@ -171,6 +174,14 @@
        purchaseLedger.setSupplierName(supplierManage.getSupplierName());
        purchaseLedger.setRecorderId(purchaseLedgerDto.getRecorderId());
        // é‡‡è´­æ²¹åº“:根据油库id回填油库名称
        if (purchaseLedgerDto.getOilDepotId() != null) {
            OilDepot oilDepot = oilDepotMapper.selectById(purchaseLedgerDto.getOilDepotId());
            if (oilDepot != null) {
                purchaseLedger.setOilDepotName(oilDepot.getDepotName());
            }
        }
        purchaseLedger.setApprovalStatus(1);
        // å®žé™…合同金额始终等于合同金额
        purchaseLedger.setNetContractAmount(purchaseLedger.getContractAmount());
src/main/java/com/ruoyi/stock/controller/StockInventoryController.java
@@ -9,6 +9,7 @@
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.execl.StockInventoryExportData;
import com.ruoyi.stock.service.StockInventoryService;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -97,6 +98,14 @@
        return R.ok(stockInventoryService.addStockOutRecordOnly(stockInventoryDto));
    }
    @PostMapping("/addOilOutRecordOnly")
    @Operation(summary = "新增油品出库记录(仅创建记录,不调整库存)")
    public R addOilOutRecordOnly(@RequestBody StockOutRecordDto stockOutRecordDto) {
        stockOutRecordDto.setRecordType(String.valueOf(StockOutQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_OUT.getCode()));
        stockOutRecordDto.setRecordId(0L);
        return R.ok(stockInventoryService.addOilOutRecordOnly(stockOutRecordDto));
    }
    @PostMapping("/importStockInventory")
    @Operation(summary = "导入库存")
src/main/java/com/ruoyi/stock/dto/StockInventoryDto.java
@@ -8,6 +8,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
public class StockInventoryDto extends StockInventory {
@@ -16,6 +17,44 @@
    private String model;
    private String unit;
    private String batchNo;
    private Long oilDepotId;
    private String oilDepotName;
    private String inboundCategory;
    private Long tankId;
    private String tankNo;
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime inboundTime;
    private String oilProduct;
    private BigDecimal originalQuantity;
    private Long customerId;
    private String customerName;
    private String contactPerson;
    private String contactPhone;
    //物流信息
    private String driverName;
    private String driverPhone;
    private String escortName;
    private String escortPhone;
    private String truckPlateNo;
    private String trailerPlateNo;
    //卸油操作信息
    private String tankInspectionStatus;
    private String unloadingCranePosition;
    private String density;
    // å…¥å‚为数组,列表返回为JSON字符串
    private Object unloadPhotos;
    //过磅数据
    private BigDecimal grossWeight;
    private BigDecimal tareWeight;
    private BigDecimal netWeight;
    private BigDecimal lossQuantity;
    private BigDecimal actualVolume;
    // å…¥å‚为数组,列表返回为JSON字符串
    private Object poundPhotos;
    //入库类型
@@ -80,4 +119,32 @@
    @Schema(description = "产品id")
    private Long productId;
    //油品出库记录展示字段(详情接口按产品关联最新出库记录)
    private String outCategory;
    private LocalDate outDate;
    private BigDecimal outTonnage;
    private String outCustomerName;
    private String loadingInspectionStatus;
    private String loadingCranePosition;
    private String outDensity;
    private String outDriverName;
    private String outTruckPlateNo;
    private String outOilDepotName;
    private String outTankNo;
    private String outOilProduct;
    private String outContactPerson;
    private String outContactPhone;
    private BigDecimal outGrossWeight;
    private BigDecimal outTareWeight;
    private BigDecimal outNetWeight;
    private BigDecimal outLossQuantity;
    private BigDecimal outActualVolume;
    private String outDriverPhone;
    private String outEscortName;
    private String outEscortPhone;
    private String outTrailerPlateNo;
    // åˆ—表返回为JSON字符串
    private Object loadingPhotos;
    private Object outPoundPhotos;
}
src/main/java/com/ruoyi/stock/dto/StockUninventoryDto.java
@@ -1,15 +1,56 @@
package com.ruoyi.stock.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.stock.pojo.StockUninventory;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class StockUninventoryDto  extends StockUninventory {
    private String productName;
    private String model;
    private String unit;
    private Long oilDepotId;
    private String oilDepotName;
    private String inboundCategory;
    private Long tankId;
    private String tankNo;
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime inboundTime;
    private String oilProduct;
    private BigDecimal originalQuantity;
    private Long customerId;
    private String customerName;
    private String contactPerson;
    private String contactPhone;
    //物流信息
    private String driverName;
    private String driverPhone;
    private String escortName;
    private String escortPhone;
    private String truckPlateNo;
    private String trailerPlateNo;
    //卸油操作信息
    private String tankInspectionStatus;
    private String unloadingCranePosition;
    private String density;
    // å…¥å‚为数组,列表返回为JSON字符串
    private Object unloadPhotos;
    //过磅数据
    private BigDecimal grossWeight;
    private BigDecimal tareWeight;
    private BigDecimal netWeight;
    private BigDecimal lossQuantity;
    private BigDecimal actualVolume;
    // å…¥å‚为数组,列表返回为JSON字符串
    private Object poundPhotos;
    //入库类型
src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
@@ -46,6 +46,92 @@
    @Schema(description = "预警数量")
    private BigDecimal warnNum;
    @Schema(description = "入库油库id")
    private Long oilDepotId;
    @Schema(description = "入库名称(油库名称)")
    private String oilDepotName;
    @Schema(description = "入库类别:自购入库/代储入库")
    private String inboundCategory;
    @Schema(description = "储罐id")
    private Long tankId;
    @Schema(description = "储罐号")
    private String tankNo;
    @Schema(description = "入库时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime inboundTime;
    @Schema(description = "油品:汽#92、汽#95、柴#0、柴#-35")
    private String oilProduct;
    @Schema(description = "原厂数量(吨)")
    private BigDecimal originalQuantity;
    @Schema(description = "客户id")
    private Long customerId;
    @Schema(description = "客户名称")
    private String customerName;
    @Schema(description = "客户对接人")
    private String contactPerson;
    @Schema(description = "对接人电话")
    private String contactPhone;
    @Schema(description = "司机姓名")
    private String driverName;
    @Schema(description = "司机电话")
    private String driverPhone;
    @Schema(description = "押运员姓名")
    private String escortName;
    @Schema(description = "押运员电话")
    private String escortPhone;
    @Schema(description = "车牌号")
    private String truckPlateNo;
    @Schema(description = "车挂牌号")
    private String trailerPlateNo;
    @Schema(description = "车体/铅封检验是否正常:正常/异常")
    private String tankInspectionStatus;
    @Schema(description = "卸车鹤位:1#撬装/2#撬装/临时泵")
    private String unloadingCranePosition;
    @Schema(description = "密度(kg/L)")
    private String density;
    @Schema(description = "卸油关键环节与车辆关键位置拍照(JSON数组)")
    private String unloadPhotos;
    @Schema(description = "毛重(吨)")
    private BigDecimal grossWeight;
    @Schema(description = "皮重(吨)")
    private BigDecimal tareWeight;
    @Schema(description = "净重(吨)")
    private BigDecimal netWeight;
    @Schema(description = "亏量(吨)")
    private BigDecimal lossQuantity;
    @Schema(description = "实际入库体积(升)")
    private BigDecimal actualVolume;
    @Schema(description = "磅单上传(JSON数组)")
    private String poundPhotos;
    @Schema(description = "类型  0合格入库 1不合格入库")
    private String type;
src/main/java/com/ruoyi/stock/pojo/StockOutRecord.java
@@ -10,6 +10,7 @@
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
@@ -46,6 +47,90 @@
    @Schema(description = "入库类型")
    private String recordType;
    @Schema(description = "出库类别:客存/代储/销售")
    private String outCategory;
    @Schema(description = "储库id")
    private Long oilDepotId;
    @Schema(description = "储库名称")
    private String oilDepotName;
    @Schema(description = "储罐id")
    private Long tankId;
    @Schema(description = "储罐号")
    private String tankNo;
    @Schema(description = "出库日期")
    private LocalDate outDate;
    @Schema(description = "油品")
    private String oilProduct;
    @Schema(description = "出库吨位(吨)")
    private BigDecimal outTonnage;
    @Schema(description = "客户id")
    private Long customerId;
    @Schema(description = "客户名称")
    private String customerName;
    @Schema(description = "客户对接人")
    private String contactPerson;
    @Schema(description = "对接人电话")
    private String contactPhone;
    @Schema(description = "车体/铅封检验是否正常")
    private String loadingInspectionStatus;
    @Schema(description = "装车鹤位")
    private String loadingCranePosition;
    @Schema(description = "装油关键环节与车辆关键位置拍照(JSON数组)")
    private String loadingPhotos;
    @Schema(description = "密度(kg/L)")
    private String density;
    @Schema(description = "毛重(吨)")
    private BigDecimal grossWeight;
    @Schema(description = "皮重(吨)")
    private BigDecimal tareWeight;
    @Schema(description = "净重(吨)")
    private BigDecimal netWeight;
    @Schema(description = "亏量(吨)")
    private BigDecimal lossQuantity;
    @Schema(description = "实际入库体积(升)")
    private BigDecimal actualVolume;
    @Schema(description = "磅单上传(JSON数组)")
    private String poundPhotos;
    @Schema(description = "司机姓名")
    private String driverName;
    @Schema(description = "司机电话")
    private String driverPhone;
    @Schema(description = "押运员姓名")
    private String escortName;
    @Schema(description = "押运员电话")
    private String escortPhone;
    @Schema(description = "车牌号")
    private String truckPlateNo;
    @Schema(description = "车挂牌号")
    private String trailerPlateNo;
    @Schema(description = "产品规格id")
    private Long productModelId;
src/main/java/com/ruoyi/stock/service/StockInventoryService.java
@@ -6,6 +6,7 @@
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockOutRecordDto;
import com.ruoyi.stock.pojo.StockInventory;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
@@ -34,6 +35,8 @@
    Boolean addStockOutRecordOnly(StockInventoryDto stockInventoryDto);
    Boolean addOilOutRecordOnly(StockOutRecordDto stockOutRecordDto);
    R importStockInventory(MultipartFile file);
    void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto);
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -7,8 +7,17 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.OilDepotMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.mapper.TankInfoMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.OilDepot;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.pojo.TankInfo;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
@@ -60,6 +69,10 @@
    private final StockUninventoryService stockUninventoryService;
    private final SalesLedgerProductMapper salesLedgerProductMapper;
    private final ProductModelMapper productModelMapper;
    private final OilDepotMapper oilDepotMapper;
    private final TankInfoMapper tankInfoMapper;
    private final CustomerMapper customerMapper;
    private final FileUtil fileUtil;
    @Override
    public IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto) {
@@ -173,6 +186,54 @@
        stockInRecordDto.setType("0");
        stockInRecordDto.setRemark(stockInventoryDto.getRemark());
        stockInRecordDto.setWarnNum(stockInventoryDto.getWarnNum());
        stockInRecordDto.setOilDepotId(stockInventoryDto.getOilDepotId());
        stockInRecordDto.setOilDepotName(stockInventoryDto.getOilDepotName());
        stockInRecordDto.setInboundCategory(stockInventoryDto.getInboundCategory());
        if (stockInventoryDto.getOilDepotId() != null && StringUtils.isEmpty(stockInRecordDto.getOilDepotName())) {
            OilDepot oilDepot = oilDepotMapper.selectById(stockInventoryDto.getOilDepotId());
            if (oilDepot != null) {
                stockInRecordDto.setOilDepotName(oilDepot.getDepotName());
            }
        }
        stockInRecordDto.setTankId(stockInventoryDto.getTankId());
        stockInRecordDto.setTankNo(stockInventoryDto.getTankNo());
        if (stockInventoryDto.getTankId() != null && StringUtils.isEmpty(stockInRecordDto.getTankNo())) {
            TankInfo tankInfo = tankInfoMapper.selectById(stockInventoryDto.getTankId());
            if (tankInfo != null) {
                stockInRecordDto.setTankNo(tankInfo.getTankNo());
            }
        }
        stockInRecordDto.setInboundTime(stockInventoryDto.getInboundTime());
        stockInRecordDto.setOilProduct(stockInventoryDto.getOilProduct());
        stockInRecordDto.setOriginalQuantity(stockInventoryDto.getOriginalQuantity());
        stockInRecordDto.setCustomerId(stockInventoryDto.getCustomerId());
        stockInRecordDto.setCustomerName(stockInventoryDto.getCustomerName());
        stockInRecordDto.setContactPerson(stockInventoryDto.getContactPerson());
        stockInRecordDto.setContactPhone(stockInventoryDto.getContactPhone());
        stockInRecordDto.setDriverName(stockInventoryDto.getDriverName());
        stockInRecordDto.setDriverPhone(stockInventoryDto.getDriverPhone());
        stockInRecordDto.setEscortName(stockInventoryDto.getEscortName());
        stockInRecordDto.setEscortPhone(stockInventoryDto.getEscortPhone());
        stockInRecordDto.setTruckPlateNo(stockInventoryDto.getTruckPlateNo());
        stockInRecordDto.setTrailerPlateNo(stockInventoryDto.getTrailerPlateNo());
        stockInRecordDto.setTankInspectionStatus(stockInventoryDto.getTankInspectionStatus());
        stockInRecordDto.setUnloadingCranePosition(stockInventoryDto.getUnloadingCranePosition());
        stockInRecordDto.setDensity(stockInventoryDto.getDensity());
        stockInRecordDto.setUnloadPhotos(stockInventoryDto.getUnloadPhotos() == null
                ? null : JSON.toJSONString(stockInventoryDto.getUnloadPhotos()));
        stockInRecordDto.setGrossWeight(stockInventoryDto.getGrossWeight());
        stockInRecordDto.setTareWeight(stockInventoryDto.getTareWeight());
        stockInRecordDto.setNetWeight(stockInventoryDto.getNetWeight());
        stockInRecordDto.setLossQuantity(stockInventoryDto.getLossQuantity());
        stockInRecordDto.setActualVolume(stockInventoryDto.getActualVolume());
        stockInRecordDto.setPoundPhotos(stockInventoryDto.getPoundPhotos() == null
                ? null : JSON.toJSONString(stockInventoryDto.getPoundPhotos()));
        if (stockInventoryDto.getCustomerId() != null && StringUtils.isEmpty(stockInRecordDto.getCustomerName())) {
            Customer customer = customerMapper.selectById(stockInventoryDto.getCustomerId());
            if (customer != null) {
                stockInRecordDto.setCustomerName(customer.getCustomerName());
            }
        }
        stockInRecordService.add(stockInRecordDto);
        return true;
    }
@@ -442,6 +503,50 @@
    @Override
    public IPage<StockInventoryDto> getBatchNoQty(Page page, StockInventoryDto stockInventoryDto) {
        return stockInventoryMapper.getBatchNoQty(page, stockInventoryDto);
        IPage<StockInventoryDto> result = stockInventoryMapper.getBatchNoQty(page, stockInventoryDto);
        // ç…§ç‰‡åº“里存的是上传时的签名URL(默认2小时过期),返回前重新签发,避免前端图片加载失败
        for (StockInventoryDto record : result.getRecords()) {
            record.setUnloadPhotos(refreshPhotoUrls(record.getUnloadPhotos()));
            record.setPoundPhotos(refreshPhotoUrls(record.getPoundPhotos()));
            record.setLoadingPhotos(refreshPhotoUrls(record.getLoadingPhotos()));
            record.setOutPoundPhotos(refreshPhotoUrls(record.getOutPoundPhotos()));
        }
        return result;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addOilOutRecordOnly(StockOutRecordDto stockOutRecordDto) {
        stockOutRecordDto.setStockOutNum(stockOutRecordDto.getOutTonnage());
        stockOutRecordDto.setApprovalStatus(0);
        return stockOutRecordService.add(stockOutRecordDto) > 0;
    }
    /**
     * é‡æ–°ç­¾å‘照片预览/下载URL
     */
    private Object refreshPhotoUrls(Object photos) {
        if (!(photos instanceof String)) {
            return photos;
        }
        String json = (String) photos;
        if (StringUtils.isEmpty(json)) {
            return photos;
        }
        try {
            List<StorageBlobVO> list = JSON.parseArray(json, StorageBlobVO.class);
            if (list == null || list.isEmpty()) {
                return photos;
            }
            for (StorageBlobVO vo : list) {
                String previewUrl = fileUtil.buildSignedUrl(vo, "/preview/", null);
                vo.setPreviewURL(previewUrl);
                vo.setUrl(previewUrl);
                vo.setDownloadURL(fileUtil.buildSignedUrl(vo, "/download/", null));
            }
            return JSON.toJSONString(list);
        } catch (Exception e) {
            return photos;
        }
    }
}
src/main/java/com/ruoyi/stock/service/impl/StockUninventoryServiceImpl.java
@@ -7,8 +7,15 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.basic.mapper.CustomerMapper;
import com.ruoyi.basic.mapper.OilDepotMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.mapper.TankInfoMapper;
import com.ruoyi.basic.pojo.Customer;
import com.ruoyi.basic.pojo.OilDepot;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.basic.pojo.TankInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.StringUtils;
@@ -54,6 +61,9 @@
    private final StockInRecordService stockInRecordService;
    private final ProductModelMapper productModelMapper;
    private final StockInventoryMapper stockInventoryMapper;
    private final OilDepotMapper oilDepotMapper;
    private final TankInfoMapper tankInfoMapper;
    private final CustomerMapper customerMapper;
    @Override
    public IPage<StockUninventoryDto> pageStockUninventory(Page page, StockUninventoryDto stockUninventoryDto) {
@@ -79,6 +89,54 @@
        stockInRecordDto.setBatchNo(stockUninventoryDto.getBatchNo());
        stockInRecordDto.setProductModelId(stockUninventoryDto.getProductModelId());
        stockInRecordDto.setType("1");
        stockInRecordDto.setOilDepotId(stockUninventoryDto.getOilDepotId());
        stockInRecordDto.setOilDepotName(stockUninventoryDto.getOilDepotName());
        stockInRecordDto.setInboundCategory(stockUninventoryDto.getInboundCategory());
        if (stockUninventoryDto.getOilDepotId() != null && StringUtils.isEmpty(stockInRecordDto.getOilDepotName())) {
            OilDepot oilDepot = oilDepotMapper.selectById(stockUninventoryDto.getOilDepotId());
            if (oilDepot != null) {
                stockInRecordDto.setOilDepotName(oilDepot.getDepotName());
            }
        }
        stockInRecordDto.setTankId(stockUninventoryDto.getTankId());
        stockInRecordDto.setTankNo(stockUninventoryDto.getTankNo());
        if (stockUninventoryDto.getTankId() != null && StringUtils.isEmpty(stockInRecordDto.getTankNo())) {
            TankInfo tankInfo = tankInfoMapper.selectById(stockUninventoryDto.getTankId());
            if (tankInfo != null) {
                stockInRecordDto.setTankNo(tankInfo.getTankNo());
            }
        }
        stockInRecordDto.setInboundTime(stockUninventoryDto.getInboundTime());
        stockInRecordDto.setOilProduct(stockUninventoryDto.getOilProduct());
        stockInRecordDto.setOriginalQuantity(stockUninventoryDto.getOriginalQuantity());
        stockInRecordDto.setCustomerId(stockUninventoryDto.getCustomerId());
        stockInRecordDto.setCustomerName(stockUninventoryDto.getCustomerName());
        stockInRecordDto.setContactPerson(stockUninventoryDto.getContactPerson());
        stockInRecordDto.setContactPhone(stockUninventoryDto.getContactPhone());
        stockInRecordDto.setDriverName(stockUninventoryDto.getDriverName());
        stockInRecordDto.setDriverPhone(stockUninventoryDto.getDriverPhone());
        stockInRecordDto.setEscortName(stockUninventoryDto.getEscortName());
        stockInRecordDto.setEscortPhone(stockUninventoryDto.getEscortPhone());
        stockInRecordDto.setTruckPlateNo(stockUninventoryDto.getTruckPlateNo());
        stockInRecordDto.setTrailerPlateNo(stockUninventoryDto.getTrailerPlateNo());
        stockInRecordDto.setTankInspectionStatus(stockUninventoryDto.getTankInspectionStatus());
        stockInRecordDto.setUnloadingCranePosition(stockUninventoryDto.getUnloadingCranePosition());
        stockInRecordDto.setDensity(stockUninventoryDto.getDensity());
        stockInRecordDto.setUnloadPhotos(stockUninventoryDto.getUnloadPhotos() == null
                ? null : JSON.toJSONString(stockUninventoryDto.getUnloadPhotos()));
        stockInRecordDto.setGrossWeight(stockUninventoryDto.getGrossWeight());
        stockInRecordDto.setTareWeight(stockUninventoryDto.getTareWeight());
        stockInRecordDto.setNetWeight(stockUninventoryDto.getNetWeight());
        stockInRecordDto.setLossQuantity(stockUninventoryDto.getLossQuantity());
        stockInRecordDto.setActualVolume(stockUninventoryDto.getActualVolume());
        stockInRecordDto.setPoundPhotos(stockUninventoryDto.getPoundPhotos() == null
                ? null : JSON.toJSONString(stockUninventoryDto.getPoundPhotos()));
        if (stockUninventoryDto.getCustomerId() != null && StringUtils.isEmpty(stockInRecordDto.getCustomerName())) {
            Customer customer = customerMapper.selectById(stockUninventoryDto.getCustomerId());
            if (customer != null) {
                stockInRecordDto.setCustomerName(customer.getCustomerName());
            }
        }
        stockInRecordService.add(stockInRecordDto);
        //审批再添加
        return 1;
@@ -121,6 +179,54 @@
        stockInRecordDto.setProductModelId(stockUninventoryDto.getProductModelId());
        stockInRecordDto.setType("1");
        stockInRecordDto.setRemark(stockUninventoryDto.getRemark());
        stockInRecordDto.setOilDepotId(stockUninventoryDto.getOilDepotId());
        stockInRecordDto.setOilDepotName(stockUninventoryDto.getOilDepotName());
        stockInRecordDto.setInboundCategory(stockUninventoryDto.getInboundCategory());
        if (stockUninventoryDto.getOilDepotId() != null && StringUtils.isEmpty(stockInRecordDto.getOilDepotName())) {
            OilDepot oilDepot = oilDepotMapper.selectById(stockUninventoryDto.getOilDepotId());
            if (oilDepot != null) {
                stockInRecordDto.setOilDepotName(oilDepot.getDepotName());
            }
        }
        stockInRecordDto.setTankId(stockUninventoryDto.getTankId());
        stockInRecordDto.setTankNo(stockUninventoryDto.getTankNo());
        if (stockUninventoryDto.getTankId() != null && StringUtils.isEmpty(stockInRecordDto.getTankNo())) {
            TankInfo tankInfo = tankInfoMapper.selectById(stockUninventoryDto.getTankId());
            if (tankInfo != null) {
                stockInRecordDto.setTankNo(tankInfo.getTankNo());
            }
        }
        stockInRecordDto.setInboundTime(stockUninventoryDto.getInboundTime());
        stockInRecordDto.setOilProduct(stockUninventoryDto.getOilProduct());
        stockInRecordDto.setOriginalQuantity(stockUninventoryDto.getOriginalQuantity());
        stockInRecordDto.setCustomerId(stockUninventoryDto.getCustomerId());
        stockInRecordDto.setCustomerName(stockUninventoryDto.getCustomerName());
        stockInRecordDto.setContactPerson(stockUninventoryDto.getContactPerson());
        stockInRecordDto.setContactPhone(stockUninventoryDto.getContactPhone());
        stockInRecordDto.setDriverName(stockUninventoryDto.getDriverName());
        stockInRecordDto.setDriverPhone(stockUninventoryDto.getDriverPhone());
        stockInRecordDto.setEscortName(stockUninventoryDto.getEscortName());
        stockInRecordDto.setEscortPhone(stockUninventoryDto.getEscortPhone());
        stockInRecordDto.setTruckPlateNo(stockUninventoryDto.getTruckPlateNo());
        stockInRecordDto.setTrailerPlateNo(stockUninventoryDto.getTrailerPlateNo());
        stockInRecordDto.setTankInspectionStatus(stockUninventoryDto.getTankInspectionStatus());
        stockInRecordDto.setUnloadingCranePosition(stockUninventoryDto.getUnloadingCranePosition());
        stockInRecordDto.setDensity(stockUninventoryDto.getDensity());
        stockInRecordDto.setUnloadPhotos(stockUninventoryDto.getUnloadPhotos() == null
                ? null : JSON.toJSONString(stockUninventoryDto.getUnloadPhotos()));
        stockInRecordDto.setGrossWeight(stockUninventoryDto.getGrossWeight());
        stockInRecordDto.setTareWeight(stockUninventoryDto.getTareWeight());
        stockInRecordDto.setNetWeight(stockUninventoryDto.getNetWeight());
        stockInRecordDto.setLossQuantity(stockUninventoryDto.getLossQuantity());
        stockInRecordDto.setActualVolume(stockUninventoryDto.getActualVolume());
        stockInRecordDto.setPoundPhotos(stockUninventoryDto.getPoundPhotos() == null
                ? null : JSON.toJSONString(stockUninventoryDto.getPoundPhotos()));
        if (stockUninventoryDto.getCustomerId() != null && StringUtils.isEmpty(stockInRecordDto.getCustomerName())) {
            Customer customer = customerMapper.selectById(stockUninventoryDto.getCustomerId());
            if (customer != null) {
                stockInRecordDto.setCustomerName(customer.getCustomerName());
            }
        }
        stockInRecordService.add(stockInRecordDto);
        return 1;
    }
src/main/resources/mapper/purchase/PurchaseLedgerMapper.xml
@@ -39,6 +39,16 @@
                pl.approval_status,
                pl.payment_method,
                pl.remarks,
                pl.purchase_category,
                pl.pickup_method,
                pl.purchase_oil_product,
                pl.logistics_method,
                pl.oil_depot_name,
                pl.customer_name,
                pl.best_arrival_date,
                pl.purchase_quantity,
                pl.purchase_order_status,
                pl.application_submit_time,
                CASE
                    WHEN IFNULL(ls.total_product_count, 0) = 0 THEN '待入库'
                    WHEN IFNULL(ls.full_product_count, 0) &gt;= IFNULL(ls.total_product_count, 0) THEN '完全入库'
src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -120,7 +120,84 @@
        unit,
        product_name,
        product_id,
        'combined' as stockType
        'combined' as stockType,
        (
        select sir.oil_product
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as oil_product,
        (
        select sir.tank_inspection_status
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as tank_inspection_status,
        (
        select sir.unloading_crane_position
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as unloading_crane_position,
        (
        select sir.density
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as density,
        (
        select sir.unload_photos
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as unload_photos,
        (
        select sir.gross_weight
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as gross_weight,
        (
        select sir.tare_weight
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as tare_weight,
        (
        select sir.net_weight
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as net_weight,
        (
        select sir.loss_quantity
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as loss_quantity,
        (
        select sir.actual_volume
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as actual_volume,
        (
        select sir.pound_photos
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        order by sir.id desc
        limit 1
        ) as pound_photos
        from (
        select
        si.batch_no,
@@ -222,7 +299,7 @@
        unit,
        product_name,
        product_id
        order by combined.create_time desc
        order by create_time desc
    </select>
    <select id="listStockInventoryExportData" resultType="com.ruoyi.stock.execl.StockInventoryExportData">
@@ -573,7 +650,382 @@
        MAX(version) as version,
        MAX(remark) as remark,
        'combined' as stockType
        'combined' as stockType,
        (
        select sir.oil_product
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as oil_product,
        (
        select sir.tank_inspection_status
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as tank_inspection_status,
        (
        select sir.unloading_crane_position
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as unloading_crane_position,
        (
        select sir.density
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as density,
        (
        select sir.unload_photos
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as unload_photos,
        (
        select sir.gross_weight
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as gross_weight,
        (
        select sir.tare_weight
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as tare_weight,
        (
        select sir.net_weight
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as net_weight,
        (
        select sir.loss_quantity
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as loss_quantity,
        (
        select sir.actual_volume
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as actual_volume,
        (
        select sir.pound_photos
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as pound_photos,
        (
        select sor.out_category
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_category,
        (
        select sor.out_date
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_date,
        (
        select sor.out_tonnage
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_tonnage,
        (
        select sor.customer_name
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_customer_name,
        (
        select sor.loading_inspection_status
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as loading_inspection_status,
        (
        select sor.loading_crane_position
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as loading_crane_position,
        (
        select sor.density
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_density,
        (
        select sor.driver_name
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_driver_name,
        (
        select sor.truck_plate_no
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_truck_plate_no,
        (
        select sor.loading_photos
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as loading_photos,
        (
        select sor.pound_photos
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_pound_photos,
        (
        select sor.oil_depot_name
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_oil_depot_name,
        (
        select sor.tank_no
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_tank_no,
        (
        select sor.oil_product
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_oil_product,
        (
        select sor.contact_person
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_contact_person,
        (
        select sor.contact_phone
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_contact_phone,
        (
        select sor.gross_weight
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_gross_weight,
        (
        select sor.tare_weight
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_tare_weight,
        (
        select sor.net_weight
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_net_weight,
        (
        select sor.loss_quantity
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_loss_quantity,
        (
        select sor.actual_volume
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_actual_volume,
        (
        select sor.driver_phone
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_driver_phone,
        (
        select sor.escort_name
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_escort_name,
        (
        select sor.escort_phone
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_escort_phone,
        (
        select sor.trailer_plate_no
        from stock_out_record sor
        where sor.product_model_id = combined.product_model_id
        order by sor.id desc
        limit 1
        ) as out_trailer_plate_no,
        (
        select sir.oil_depot_name
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as oil_depot_name,
        (
        select sir.inbound_category
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as inbound_category,
        (
        select sir.tank_no
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as tank_no,
        (
        select sir.inbound_time
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as inbound_time,
        (
        select sir.original_quantity
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as original_quantity,
        (
        select sir.customer_name
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as customer_name,
        (
        select sir.contact_person
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as contact_person,
        (
        select sir.contact_phone
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as contact_phone,
        (
        select sir.driver_name
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as driver_name,
        (
        select sir.driver_phone
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as driver_phone,
        (
        select sir.escort_name
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as escort_name,
        (
        select sir.escort_phone
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as escort_phone,
        (
        select sir.truck_plate_no
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as truck_plate_no,
        (
        select sir.trailer_plate_no
        from stock_in_record sir
        where sir.product_model_id = combined.product_model_id
        and (sir.batch_no = combined.batch_no or (combined.batch_no is null and sir.batch_no is null))
        order by sir.id desc
        limit 1
        ) as trailer_plate_no
        from (
        select
        si.batch_no,