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
package com.ruoyi.quality.controller;
 
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.quality.dto.QualityMetricBindingDto;
import com.ruoyi.quality.dto.QualityMetricDto;
import com.ruoyi.quality.mapper.QualityTestStandardBindingMapper;
import com.ruoyi.quality.mapper.QualityTestStandardParamMapper;
import com.ruoyi.quality.pojo.QualityTestStandard;
import com.ruoyi.quality.pojo.QualityTestStandardBinding;
import com.ruoyi.quality.pojo.QualityTestStandardParam;
import com.ruoyi.quality.service.IQualityTestStandardService;
import com.ruoyi.quality.service.QualityTestStandardBindingService;
import com.ruoyi.quality.service.QualityTestStandardParamService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
 
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.stream.Collectors;
 
/**
 * 按产品维护检测指标。
 *
 * 前端的“指标维护”页面以产品为入口,本接口将产品、检测标准与标准参数的
 * 三层数据结构封装为页面所需的单层指标操作,并按检验类别/工序隔离指标池。
 */
@RestController
@RequestMapping("/quality/qualityMetric")
@RequiredArgsConstructor
public class QualityMetricController {
 
    private static final int INSPECT_TYPE_PROCESS = 1;
 
    private final QualityTestStandardParamMapper qualityTestStandardParamMapper;
    private final QualityTestStandardParamService qualityTestStandardParamService;
    private final QualityTestStandardBindingService qualityTestStandardBindingService;
    private final IQualityTestStandardService qualityTestStandardService;
    private final QualityTestStandardBindingMapper qualityTestStandardBindingMapper;
 
    @GetMapping("/listPage")
    public AjaxResult listPage(Page<QualityTestStandardParam> page,
                               @RequestParam("productId") Long productId,
                               @RequestParam(value = "inspectType", required = false) Integer inspectType,
                               @RequestParam(value = "processId", required = false) Long processId) {
        IPage<QualityTestStandardParam> result = qualityTestStandardParamMapper
                .selectPageByProductId(page, productId, inspectType, processId);
        return AjaxResult.success(result);
    }
 
    @GetMapping("/product/{productId}")
    public AjaxResult listByProduct(@PathVariable("productId") Long productId,
                                    @RequestParam(value = "inspectType", required = false) Integer inspectType,
                                    @RequestParam(value = "process", required = false) String process) {
        return AjaxResult.success(qualityTestStandardParamMapper
                .selectListByProductId(productId, inspectType, process));
    }
 
    @PostMapping("/add")
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult add(@RequestBody QualityMetricDto metric) {
        normalizeInspectType(metric);
        String validationMessage = validateMetric(metric, true);
        if (validationMessage != null) {
            return AjaxResult.error(validationMessage);
        }
 
        Long testStandardId = getOrCreateTestStandard(metric.getProductId(),
                metric.getInspectType(), metric.getProcessId());
        QualityTestStandardParam parameter = copyEditableFields(metric, new QualityTestStandardParam());
        parameter.setTestStandardId(testStandardId);
        if (!qualityTestStandardParamService.save(parameter)) {
            throw new ServiceException("保存检测指标失败");
        }
        return AjaxResult.success(true);
    }
 
    @PostMapping("/update")
    public AjaxResult update(@RequestBody QualityMetricDto metric) {
        String validationMessage = validateMetric(metric, false);
        if (validationMessage != null) {
            return AjaxResult.error(validationMessage);
        }
        if (metric.getId() == null) {
            return AjaxResult.error("指标ID不能为空");
        }
 
        QualityTestStandardParam parameter = qualityTestStandardParamService.getById(metric.getId());
        if (parameter == null) {
            return AjaxResult.error("检测指标不存在");
        }
        copyEditableFields(metric, parameter);
        if (!qualityTestStandardParamService.updateById(parameter)) {
            throw new ServiceException("修改检测指标失败");
        }
        return AjaxResult.success(true);
    }
 
    @DeleteMapping("/del")
    public AjaxResult delete(@RequestBody List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return AjaxResult.error("请选择至少一条数据");
        }
        if (!qualityTestStandardParamService.removeBatchByIds(ids)) {
            throw new ServiceException("删除检测指标失败");
        }
        return AjaxResult.success(true);
    }
 
    @PostMapping("/export")
    public void export(HttpServletResponse response,
                       @RequestParam("productId") Long productId,
                       @RequestParam(value = "inspectType", required = false) Integer inspectType,
                       @RequestParam(value = "process", required = false) String process) {
        List<QualityTestStandardParam> list = qualityTestStandardParamMapper
                .selectListByProductId(productId, inspectType, process);
        ExcelUtil<QualityTestStandardParam> util = new ExcelUtil<>(QualityTestStandardParam.class);
        util.exportExcel(response, list, "检测指标");
    }
 
    /**
     * 查询全部检测标准,供“指标绑定”页面选择。
     */
    @GetMapping("/standardList")
    public AjaxResult standardList() {
        return AjaxResult.success(qualityTestStandardService.list(
                Wrappers.<QualityTestStandard>lambdaQuery()
                        .orderByDesc(QualityTestStandard::getId)));
    }
 
    /**
     * 查询产品已绑定的检测标准。
     */
    @GetMapping("/bindingList")
    public AjaxResult bindingList(@RequestParam("productId") Long productId) {
        return AjaxResult.success(qualityTestStandardBindingMapper.selectBindingByProductId(productId));
    }
 
    /**
     * 绑定产品与检测标准。
     */
    @PostMapping("/binding")
    public AjaxResult bind(@RequestBody QualityMetricBindingDto binding) {
        if (binding.getProductId() == null) {
            return AjaxResult.error("请先选择产品");
        }
        if (binding.getTestStandardId() == null) {
            return AjaxResult.error("请选择检测标准");
        }
        long exists = qualityTestStandardBindingService.count(
                Wrappers.<QualityTestStandardBinding>lambdaQuery()
                        .eq(QualityTestStandardBinding::getProductId, binding.getProductId())
                        .eq(QualityTestStandardBinding::getTestStandardId, binding.getTestStandardId()));
        if (exists > 0) {
            return AjaxResult.error("该标准已绑定到此产品");
        }
        QualityTestStandardBinding entity = new QualityTestStandardBinding();
        entity.setProductId(binding.getProductId());
        entity.setTestStandardId(binding.getTestStandardId());
        if (!qualityTestStandardBindingService.save(entity)) {
            throw new ServiceException("绑定检测标准失败");
        }
        return AjaxResult.success(true);
    }
 
    /**
     * 解除产品与检测标准的绑定。
     */
    @DeleteMapping("/binding")
    public AjaxResult unbind(@RequestBody List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return AjaxResult.error("请选择至少一条数据");
        }
        if (!qualityTestStandardBindingService.removeBatchByIds(ids)) {
            throw new ServiceException("解除绑定失败");
        }
        return AjaxResult.success(true);
    }
 
    private void normalizeInspectType(QualityMetricDto metric) {
        if (metric != null && metric.getInspectType() == null) {
            metric.setInspectType(0);
        }
    }
 
    /**
     * 查找或创建与“产品 + 检验类别 + 工序”匹配的检测标准,并建立产品绑定。
     */
    private Long getOrCreateTestStandard(Long productId, Integer inspectType, Long processId) {
        int type = inspectType == null ? 0 : inspectType;
        List<QualityTestStandardBinding> bindings = qualityTestStandardBindingService.list(
                Wrappers.<QualityTestStandardBinding>lambdaQuery()
                        .eq(QualityTestStandardBinding::getProductId, productId)
                        .orderByAsc(QualityTestStandardBinding::getId));
        if (!bindings.isEmpty()) {
            List<Integer> standardIds = bindings.stream()
                    .map(QualityTestStandardBinding::getTestStandardId)
                    .collect(Collectors.toList());
            com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<QualityTestStandard> wrapper =
                    Wrappers.<QualityTestStandard>lambdaQuery()
                            .in(QualityTestStandard::getId, standardIds)
                            .eq(QualityTestStandard::getInspectType, type);
            // 仅过程检验按工序区分标准,原材料/出厂检验忽略工序
            if (type == INSPECT_TYPE_PROCESS && processId != null) {
                wrapper.eq(QualityTestStandard::getProcessId, processId.intValue());
            }
            QualityTestStandard matched = qualityTestStandardService.getOne(
                    wrapper.orderByAsc(QualityTestStandard::getId).last("limit 1"));
            if (matched != null) {
                return matched.getId();
            }
        }
 
        QualityTestStandard standard = new QualityTestStandard();
        String suffix = inspectTypeSuffix(type) + (processId == null ? "" : "-" + processId);
        standard.setStandardNo("AUTO-" + productId + "-" + suffix);
        standard.setStandardName("产品检测指标-" + productId + "-" + suffix);
        standard.setState("1");
        standard.setInspectType(type);
        if (processId != null) {
            standard.setProcessId(processId.intValue());
        }
        if (!qualityTestStandardService.save(standard) || standard.getId() == null) {
            throw new ServiceException("创建产品检测标准失败");
        }
 
        QualityTestStandardBinding binding = new QualityTestStandardBinding();
        binding.setProductId(productId);
        binding.setTestStandardId(Math.toIntExact(standard.getId()));
        if (!qualityTestStandardBindingService.save(binding)) {
            throw new ServiceException("建立产品与检测标准关联失败");
        }
        return standard.getId();
    }
 
    private String inspectTypeSuffix(int inspectType) {
        switch (inspectType) {
            case 1:
                return "PROCESS";
            case 2:
                return "FINAL";
            default:
                return "RAW";
        }
    }
 
    private String validateMetric(QualityMetricDto metric, boolean requireProduct) {
        if (metric == null) {
            return "指标数据不能为空";
        }
        if (requireProduct && metric.getProductId() == null) {
            return "请先选择产品";
        }
        if (metric.getInspectType() != null
                && metric.getInspectType() == INSPECT_TYPE_PROCESS
                && metric.getProcessId() == null) {
            return "过程检验请先选择工序";
        }
        if (StringUtils.isBlank(metric.getParameterItem())) {
            return "指标名称不能为空";
        }
        if (StringUtils.isBlank(metric.getUnit())) {
            return "单位不能为空";
        }
        if (StringUtils.isBlank(metric.getStandardValue())) {
            return "标准值不能为空";
        }
        return null;
    }
 
    private QualityTestStandardParam copyEditableFields(QualityMetricDto source,
                                                         QualityTestStandardParam target) {
        target.setParameterItem(source.getParameterItem());
        target.setUnit(source.getUnit());
        target.setStandardValue(source.getStandardValue());
        target.setControlValue(source.getControlValue());
        target.setDefaultValue(source.getDefaultValue());
        return target;
    }
}