sgj
2026-03-10 ea4b6749adbd7dae4ccff32f9f7bd5fcdc0ab3b2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
package com.fzzy.igds.service;
 
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fzzy.igds.constant.Constant;
import com.fzzy.igds.data.SuperInventoryReportData;
import com.fzzy.igds.data.SuperInventoryReportParam;
import com.fzzy.igds.domain.*;
import com.fzzy.igds.mapper.*;
import com.fzzy.igds.utils.ContextUtil;
import com.fzzy.igds.utils.DateUtil;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
 
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
 
/**
 * 监管库存报表Service
 *
 * @author sgj
 * @date 2025/12/19
 */
@Slf4j
@Service
public class SuperInventoryReportService {
 
    @Resource
    private DepotStoreMapper depotStoreMapper;
    @Resource
    private InoutRecordMapper inoutRecordMapper;
    @Resource
    private InoutStockChangeMapper inoutStockChangeMapper;
    @Resource
    private CoreCompanyMapper coreCompanyMapper;
    @Resource
    private InoutLossOverMapper inoutLossOverMapper;
 
    @Resource
    private CoreDeptService coreDeptService;
    @Resource
    private DepotService depotService;
 
    /**
     * 查询报表数据
     *
     * @param param
     * @author sgj
     * @date 2025/12/19
     */
    public List<SuperInventoryReportData> listSuperInventoryReportData(SuperInventoryReportParam param) {
        //1.获取查询得起止时间,如果没有则,默认截止时间为当天,起始时间为30天前
        Date end = new Date();
        Date start = DateUtil.getNewByDay(end, -30);
        if (null == param.getStart()) {
            param.setStart(DateUtil.getCurZero(start));
        }
        if (null == param.getEnd()) {
            param.setEnd(DateUtil.getCurZero(end));
        }
 
        //默认组织编码
        String companyId = ContextUtil.getCompanyId();
        //默认库区编码
        String deptId = ContextUtil.subDeptId(null);
        param.setCompanyId(companyId);
 
        //region 查询条件收储公司和所属库区处理
        if (StringUtils.isNotEmpty(param.getDeptId()) && StringUtils.isNotEmpty(param.getCustomerId())) {
            String substring = param.getDeptId().substring(0, (param.getDeptId().length() - 3));
            if (!substring.equals(param.getCustomerId())) {
                return new ArrayList<SuperInventoryReportData>();
            }
        }
        if (StringUtils.isEmpty(param.getDeptId())) {
            param.setDeptId(param.getCustomerId());
        }
        //收储公司,所属库区查询均为空,则查询默认库区
        if (StringUtils.isEmpty(param.getCustomerId()) && StringUtils.isEmpty(param.getDeptId())) {
            param.setDeptId(deptId);
        }
        //endregion
 
        //region 初始化需要范围的数据
        Map<String, SuperInventoryReportData> columsMap = new HashMap<>();
 
        //查询当前用户下属的所有库区
        List<Dept> deptList = coreDeptService.getDeptData();
        if (null == deptList || deptList.isEmpty()) {
            return new ArrayList<>();
        }
 
        //预加载所有部门下的仓库信息,减少重复查询
        Map<String, Dept> deptCache = new HashMap<>();
        Map<String, List<Depot>> deptDepotsMap = new HashMap<>();
        Map<String, Company> companyCache = new HashMap<>();
 
        for (Dept dept : deptList) {
            deptCache.put(dept.getId(), dept);
            //获取库区下所有仓库
            List<Depot> depotList = depotService.getData(companyId, dept.getId(), true);
            if (null == depotList || depotList.isEmpty()) {
                continue;
            }
            deptDepotsMap.put(dept.getId(), depotList);
 
            //缓存公司信息
            int deptIdLength = dept.getId().length();
            String companyCode = dept.getId().substring(0, deptIdLength - 3);
            if (!companyCache.containsKey(companyCode)) {
                Company company = coreCompanyMapper.selectById(companyCode);
                companyCache.put(companyCode, company);
            }
        }
 
        //构建结果数据映射map
        for (Map.Entry<String, List<Depot>> entry : deptDepotsMap.entrySet()) {
            String deptIdKey = entry.getKey();
            Company company = companyCache.get(deptIdKey.substring(0, deptIdKey.length() - 3));
            Dept dept = deptCache.get(deptIdKey);
            for (Depot depot : entry.getValue()) {
                //库区编码+仓库编码组成唯一键
                String key = deptIdKey + "_" + depot.getId();
                SuperInventoryReportData data = new SuperInventoryReportData();
                data.setCustomerId(Optional.ofNullable(company)
                        .map(Company::getId)
                        .orElse("-"));
                data.setCustomerName(Optional.ofNullable(company)
                        .map(Company::getDwmc)
                        .orElse("-"));
                data.setDeptId(deptIdKey);
                data.setDeptName(dept.getKqmc());
                data.setDepotId(depot.getId());
                data.setDepotName(depot.getName());
                columsMap.put(key, data);
            }
        }
        //endregion
        if (columsMap.isEmpty()) {
            return new ArrayList<>();
        }
 
        //2.查询时间范围内的各种数据
        List<DepotStore> depotStores = queryDepotStores(param);
        List<InoutRecord> inoutRecords = queryInoutRecords(param);
        List<InoutStockChange> inoutStockChanges = queryInoutStockChanges(param);
        List<InoutLossOver> inoutLossOvers = queryInoutLossOvers(param);
 
        //3. 数据分组预处理,提高后续查找效率
        Map<String, List<DepotStore>> storeGroupMap = depotStores.stream()
                .collect(Collectors.groupingBy(store -> store.getDeptId() + "_" + store.getDepotId()));
 
        Map<String, List<InoutRecord>> recordGroupMap = inoutRecords.stream()
                .collect(Collectors.groupingBy(record -> record.getDeptId() + "_" + record.getDepotId()));
 
        //4. 根据库区、仓库为唯一键,组装报表数据
        List<SuperInventoryReportData> result = new ArrayList<>();
        Date todayZero = DateUtil.getCurZero(new Date());
        boolean isEndDateBeforeToday = param.getEnd().before(todayZero);
 
        for (SuperInventoryReportData data : columsMap.values()) {
            String dataKey = data.getDeptId() + "_" + data.getDepotId();
 
            //获取库存数据
            List<DepotStore> depotStoreList = storeGroupMap.getOrDefault(dataKey, new ArrayList<>());
            if (depotStoreList.isEmpty()) {
                continue;
            }
 
            //排序库存数据
            depotStoreList.sort(Comparator.comparing(DepotStore::getCreateTime));
 
            //获取出入库记录
            List<InoutRecord> recordList = recordGroupMap.getOrDefault(dataKey, new ArrayList<>());
            Map<Boolean, List<InoutRecord>> recordPartition = recordList.stream()
                    .collect(Collectors.partitioningBy(record -> Constant.TYPE_IN.equals(record.getType())));
 
            List<InoutRecord> recordInList = recordPartition.get(true);
            List<InoutRecord> recordOutList = recordPartition.get(false);
 
            //获取倒仓数据
            Map<Boolean, List<InoutStockChange>> stockChangePartition = inoutStockChanges.stream()
                    .filter(change -> data.getDeptId().equals(change.getDeptId()))
                    .collect(Collectors.partitioningBy(change -> data.getDepotId().equals(change.getDepotIdIn())));
 
            List<InoutStockChange> stockChangeInList = new ArrayList<>();
            List<InoutStockChange> stockChangeOutList = new ArrayList<>();
 
            //倒入数据
            stockChangeInList.addAll(stockChangePartition.get(true).stream()
                    .filter(change -> data.getDepotId().equals(change.getDepotIdIn()))
                    .collect(Collectors.toList()));
 
            //倒出数据
            stockChangeOutList.addAll(stockChangePartition.get(false).stream()
                    .filter(change -> data.getDepotId().equals(change.getDepotIdOut()))
                    .collect(Collectors.toList()));
 
            //损益数据
            List<InoutLossOver> lossOverList = inoutLossOvers.stream()
                    .filter(loss -> data.getDeptId().equals(loss.getDeptId())
                            && data.getDepotId().equals(loss.getDepotId()))
                    .collect(Collectors.toList());
 
            //计算各项统计数据
            double sumRecordIn = recordInList.stream()
                    .mapToDouble(InoutRecord::getRecordWeight)
                    .sum();
 
            double sumRecordOut = recordOutList.stream()
                    .mapToDouble(InoutRecord::getRecordWeight)
                    .sum();
 
            double sumStockChangeIn = stockChangeInList.stream()
                    .mapToDouble(InoutStockChange::getNumber)
                    .sum();
 
            double sumStockChangeOut = stockChangeOutList.stream()
                    .mapToDouble(InoutStockChange::getNumber)
                    .sum();
 
            double sumLossOver = lossOverList.stream()
                    .mapToDouble(lossOver -> Constant.TYPE_LOSS.equals(lossOver.getType())
                            ? -lossOver.getAmount()
                            : lossOver.getAmount())
                    .sum();
 
            //计算期初期末重量
            Double initialWeight = depotStoreList.get(0).getStorageReal();
            Double finalWeight;
 
            if (isEndDateBeforeToday) {
                //如果截止时间为昨天(当天的0点0分0秒0毫秒)及之前,则最后一条数据为期末数量
                finalWeight = depotStoreList.get(depotStoreList.size() - 1).getStorageReal();
            } else {
                //如果截止时间为今天及之后,则需要结合入出库记录数据来计算期末数量,规则:期末数量 = 期初数量 + 入库数量 - 出库数量 + 倒入数量 - 倒出数量+ 损益数据
                finalWeight = initialWeight + sumRecordIn - sumRecordOut + sumStockChangeIn - sumStockChangeOut + sumLossOver;
            }
 
            //设置数据
            data.setInitialWeight(initialWeight);
            data.setRecordInWeight(sumRecordIn);
            data.setChangeInWeight(sumStockChangeIn);
            data.setRecordOutWeight(sumRecordOut);
            data.setChangeOutWeight(sumStockChangeOut);
            data.setLossWeight(sumLossOver);
            data.setFinalWeight(finalWeight);
            result.add(data);
        }
 
        if(result.isEmpty()){
            //没有数据时将columsMap中的数据添加到result中作为默认数据
            columsMap.values().forEach(data -> {
                String deptId_Item = data.getDeptId();
                if(StringUtils.isNotBlank(param.getDeptId()) && deptId_Item.startsWith(param.getDeptId())){
                    result.add(data);
                }
            });
        }
 
        return result;
    }
 
    /**
     * 查询库存数据
     */
    private List<DepotStore> queryDepotStores(SuperInventoryReportParam param) {
        QueryWrapper<DepotStore> depotStoreQueryWrapper = new QueryWrapper<>();
        depotStoreQueryWrapper.eq("company_id", param.getCompanyId());
        if (StringUtils.isNotBlank(param.getDeptId())) {
            depotStoreQueryWrapper.likeRight("dept_id", param.getDeptId());
        }
        depotStoreQueryWrapper.ge("create_time", param.getStart());
        depotStoreQueryWrapper.le("create_time", param.getEnd());
        depotStoreQueryWrapper.orderByAsc("create_time");
        return depotStoreMapper.selectList(depotStoreQueryWrapper);
    }
 
    /**
     * 查询出入库记录数据
     */
    private List<InoutRecord> queryInoutRecords(SuperInventoryReportParam param) {
        QueryWrapper<InoutRecord> inoutRecordQueryWrapper = new QueryWrapper<>();
        inoutRecordQueryWrapper.eq("company_id", param.getCompanyId());
        if (StringUtils.isNotBlank(param.getDeptId())) {
            inoutRecordQueryWrapper.likeRight("dept_id", param.getDeptId());
        }
        inoutRecordQueryWrapper.likeRight("progress", Constant.PROGRESS_RECORD); //已完成的单据
        inoutRecordQueryWrapper.ne("record_status", Constant.RECORD_STATUS_DEL); //不是删除的单子,即正常的单子
        inoutRecordQueryWrapper.ge("create_time", param.getStart());
        inoutRecordQueryWrapper.le("create_time", param.getEnd());
        inoutRecordQueryWrapper.orderByAsc("create_time");
        return inoutRecordMapper.selectList(inoutRecordQueryWrapper);
    }
 
    /**
     * 查询倒仓数据
     */
    private List<InoutStockChange> queryInoutStockChanges(SuperInventoryReportParam param) {
        QueryWrapper<InoutStockChange> inoutStockChangeQueryWrapper = new QueryWrapper<>();
        inoutStockChangeQueryWrapper.eq("company_id", param.getCompanyId());
        if (StringUtils.isNotBlank(param.getDeptId())) {
            inoutStockChangeQueryWrapper.likeRight("dept_id", param.getDeptId());
        }
        inoutStockChangeQueryWrapper.ge("create_time", param.getStart());
        inoutStockChangeQueryWrapper.le("create_time", param.getEnd());
        inoutStockChangeQueryWrapper.orderByAsc("create_time");
        return inoutStockChangeMapper.selectList(inoutStockChangeQueryWrapper);
    }
 
    /**
     * 查询损益数据
     */
    private List<InoutLossOver> queryInoutLossOvers(SuperInventoryReportParam param) {
        QueryWrapper<InoutLossOver> inoutLossOverQueryWrapper = new QueryWrapper<>();
        inoutLossOverQueryWrapper.eq("company_id", param.getCompanyId());
        if (StringUtils.isNotBlank(param.getDeptId())) {
            inoutLossOverQueryWrapper.likeRight("dept_id", param.getDeptId());
        }
        inoutLossOverQueryWrapper.ge("create_time", param.getStart());
        inoutLossOverQueryWrapper.le("create_time", param.getEnd());
        inoutLossOverQueryWrapper.orderByAsc("create_time");
        return inoutLossOverMapper.selectList(inoutLossOverQueryWrapper);
    }
}