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
package com.ruoyi.stock.support;
 
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.ruoyi.basic.mapper.ProductMapper;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.Product;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.production.mapper.ProductProcessMapper;
import com.ruoyi.production.mapper.ProductProcessRouteItemMapper;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.mapper.ProductionProductOutputMapper;
import com.ruoyi.production.pojo.ProductProcess;
import com.ruoyi.production.pojo.ProductProcessRouteItem;
import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.production.pojo.ProductionProductOutput;
import org.springframework.stereotype.Component;
 
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
 
@Component
public class FinishedProductStockDimensionResolver {
 
    private static final String PROCESS_COPPER = "印铜";
    private static final String PROCESS_SILVER = "印银";
    private static final String PROCESS_CATEGORY_COPPER = "铜极";
    private static final String PROCESS_CATEGORY_SILVER = "银极";
    private static final String VOLTAGE_PARAMETER = "电压";
 
    private final ProductionProductMainMapper productionProductMainMapper;
    private final ProductionProductOutputMapper productionProductOutputMapper;
    private final ProductProcessRouteItemMapper productProcessRouteItemMapper;
    private final ProductProcessMapper productProcessMapper;
    private final ProductMapper productMapper;
    private final ProductModelMapper productModelMapper;
 
    public FinishedProductStockDimensionResolver(ProductionProductMainMapper productionProductMainMapper,
                                                 ProductionProductOutputMapper productionProductOutputMapper,
                                                 ProductProcessRouteItemMapper productProcessRouteItemMapper,
                                                 ProductProcessMapper productProcessMapper,
                                                 ProductMapper productMapper,
                                                 ProductModelMapper productModelMapper) {
        this.productionProductMainMapper = productionProductMainMapper;
        this.productionProductOutputMapper = productionProductOutputMapper;
        this.productProcessRouteItemMapper = productProcessRouteItemMapper;
        this.productProcessMapper = productProcessMapper;
        this.productMapper = productMapper;
        this.productModelMapper = productModelMapper;
    }
 
    /**
     * 解析成品入库类别。
     * 当工序为印铜时返回铜极,当工序为印银时返回银极。
     */
    public String resolveProcessCategory(Long productMainId) {
        ProductionProductMain productionProductMain = loadProductionMain(productMainId);
        ProductProcessRouteItem routeItem = loadRouteItem(productionProductMain);
        if (routeItem.getProductRouteId() == null) {
            throw new ServiceException("工艺路线未绑定工艺明细,无法确定成品类别");
        }
        return resolveProcessCategoryByRouteId(routeItem.getProductRouteId());
    }
 
    /**
     * 解析成品入库电压,来源于报工 otherData 中的“电压”参数。
     */
    public String resolveVoltage(Long productMainId) {
        List<ProductionProductOutput> outputs = loadOutputs(productMainId);
        
        Long productModelId = outputs.stream()
                .map(ProductionProductOutput::getProductModelId)
                .filter(id -> id != null)
                .findFirst()
                .orElse(null);
        
        boolean isFinishedProduct = isFinishedProduct(productModelId);
        
        for (ProductionProductOutput output : outputs) {
            String voltage = resolveVoltageValue(output.getOtherData());
            if (StringUtils.isNotBlank(voltage)) {
                return voltage;
            }
        }
        
        if (isFinishedProduct) {
            throw new ServiceException("请配置参数电压");
        }
        
        return null;
    }
 
    private ProductionProductMain loadProductionMain(Long productMainId) {
        if (productMainId == null) {
            throw new ServiceException("报工ID不能为空");
        }
        ProductionProductMain productionProductMain = productionProductMainMapper.selectById(productMainId);
        if (productionProductMain == null) {
            throw new ServiceException("报工记录不存在,无法确定成品库存维度");
        }
        return productionProductMain;
    }
 
    private ProductProcessRouteItem loadRouteItem(ProductionProductMain productionProductMain) {
        if (productionProductMain.getProductProcessRouteItemId() == null) {
            throw new ServiceException("报工未关联工艺路线明细,无法确定成品库存维度");
        }
        ProductProcessRouteItem routeItem = productProcessRouteItemMapper.selectById(productionProductMain.getProductProcessRouteItemId());
        if (routeItem == null) {
            throw new ServiceException("工艺路线明细不存在,无法确定成品库存维度");
        }
        return routeItem;
    }
 
    private List<ProductionProductOutput> loadOutputs(Long productMainId) {
        List<ProductionProductOutput> outputs = productionProductOutputMapper.selectList(
                new LambdaQueryWrapper<ProductionProductOutput>()
                        .eq(ProductionProductOutput::getProductMainId, productMainId)
        );
        if (CollectionUtils.isEmpty(outputs)) {
            throw new ServiceException("报工输出记录不存在,无法确定成品库存维度");
        }
        return outputs;
    }
 
    private String resolveProcessCategoryByRouteId(Long productRouteId) {
        List<ProductProcessRouteItem> routeItems = productProcessRouteItemMapper.selectList(
                new LambdaQueryWrapper<ProductProcessRouteItem>()
                        .eq(ProductProcessRouteItem::getProductRouteId, productRouteId)
        );
        if (CollectionUtils.isEmpty(routeItems)) {
            throw new ServiceException("订单工艺路线未配置工序,无法确定成品类别");
        }
        
        Long productModelId = routeItems.stream()
                .map(ProductProcessRouteItem::getProductModelId)
                .filter(id -> id != null)
                .findFirst()
                .orElse(null);
        
        boolean isFinishedProduct = isFinishedProduct(productModelId);
        
        Set<Long> processIds = routeItems.stream()
                .map(ProductProcessRouteItem::getProcessId)
                .filter(id -> id != null)
                .collect(Collectors.toSet());
        if (processIds.isEmpty()) {
            throw new ServiceException("订单工艺路线未配置工序,无法确定成品类别");
        }
        List<ProductProcess> processes = productProcessMapper.selectBatchIds(processIds);
        Set<String> matchedCategories = processes.stream()
                .map(ProductProcess::getName)
                .filter(name -> PROCESS_COPPER.equals(name) || PROCESS_SILVER.equals(name))
                .collect(Collectors.toCollection(LinkedHashSet::new));
        
        if (matchedCategories.isEmpty() && isFinishedProduct) {
            throw new ServiceException("订单工艺路线未配置印铜/印银工序,无法确定成品类别");
        }
        
        if (matchedCategories.size() > 1) {
            throw new ServiceException("订单工艺路线同时配置了印铜和印银,无法确定成品类别");
        }
        
        if (matchedCategories.isEmpty()) {
            return null;
        }
        
        String matchedProcess = matchedCategories.iterator().next();
        if (PROCESS_COPPER.equals(matchedProcess)) {
            return PROCESS_CATEGORY_COPPER;
        }
        return PROCESS_CATEGORY_SILVER;
    }
 
    private String resolveVoltageValue(String otherData) {
        if (StringUtils.isBlank(otherData)) {
            return null;
        }
        Object parsed;
        try {
            parsed = JSON.parse(otherData);
        } catch (Exception ex) {
            throw new ServiceException("报工参数格式错误,无法解析电压");
        }
        String voltage = StringUtils.trim(findVoltageValue(parsed));
        return StringUtils.isBlank(voltage) ? null : voltage;
    }
 
    private String findVoltageValue(Object node) {
        if (node instanceof JSONArray) {
            JSONArray array = (JSONArray) node;
            for (Object item : array) {
                String voltage = findVoltageValue(item);
                if (StringUtils.isNotBlank(voltage)) {
                    return voltage;
                }
            }
            return null;
        }
        if (node instanceof JSONObject) {
            JSONObject object = (JSONObject) node;
            if (VOLTAGE_PARAMETER.equals(StringUtils.trim(object.getString("parameterItem")))) {
                String value = StringUtils.trim(object.getString("value"));
                if (StringUtils.isNotBlank(value)) {
                    return value;
                }
            }
            for (Object value : object.values()) {
                String voltage = findVoltageValue(value);
                if (StringUtils.isNotBlank(voltage)) {
                    return voltage;
                }
            }
        }
        return null;
    }
 
    private boolean isFinishedProduct(Long productModelId) {
        if (productModelId == null) {
            return false;
        }
        
        ProductModel productModel = productModelMapper.selectById(productModelId);
        if (productModel == null || productModel.getProductId() == null) {
            return false;
        }
        
        return isFinishedProductRecursive(productModel.getProductId());
    }
 
    private boolean isFinishedProductRecursive(Long productId) {
        if (productId == null) {
            return false;
        }
        
        Product product = productMapper.selectById(productId);
        if (product == null) {
            return false;
        }
        
        if ("成品".equals(product.getProductName())) {
            return true;
        }
        
        if (product.getParentId() == null) {
            return false;
        }
        
        return isFinishedProductRecursive(product.getParentId());
    }
}