gaoluyang
昨天 8c65f898ed6e5b0c7a94c792c4562f6ec831dccd
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
package cn.iocoder.yudao.module.mes.controller.admin.qc.attachment;
 
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService;
import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService;
import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService;
import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcService;
import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
/** 质检附件独立于检验结论,上传附件不自动将产品判为合格。 */
@RestController
@RequestMapping("/mes/qc/attachment")
@Validated
public class MesQcAttachmentController {
    @Resource private MesQcIqcService iqcService;
    @Resource private MesQcIpqcService ipqcService;
    @Resource private MesQcOqcService oqcService;
    @Resource private MesQcRqcService rqcService;
    @Resource private StorageAttachmentApi storageApi;
    @Resource private org.springframework.jdbc.core.JdbcTemplate jdbcTemplate;
 
    @GetMapping("/list")
    @PreAuthorize("@ss.hasPermission('mes:qc-' + #type.toLowerCase() + ':query')")
    public CommonResult<List<StorageBlobRespDTO>> list(
            @RequestParam @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") String type,
            @RequestParam @NotNull Long id) {
        validateDocument(type, id);
        var files = storageApi.listAttachments(recordType(type), id);
        return success(files == null ? List.of() : files);
    }
 
    @PutMapping("/save")
    @PreAuthorize("@ss.hasPermission('mes:qc-' + #req.type.toLowerCase() + ':update')")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> save(@Valid @RequestBody SaveReq req) {
        lockDocument(req.getType(), req.getId());
        validateDocument(req.getType(), req.getId());
        // bindAttachments 仅替换关联,不删除共享文件本体。
        storageApi.bindAttachments("file", recordType(req.getType()), req.getId(), req.getBlobIds().stream().distinct().toList());
        return success(true);
    }
 
    @PostMapping("/append")
    @PreAuthorize("@ss.hasPermission('mes:qc-' + #req.type.toLowerCase() + ':update')")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> append(@Valid @RequestBody AppendReq req) {
        lockDocument(req.getType(), req.getId());
        validateDocument(req.getType(), req.getId());
        var existing = storageApi.listAttachments(recordType(req.getType()), req.getId());
        var ids = new java.util.LinkedHashSet<Long>();
        if (existing != null) existing.forEach(blob -> ids.add(blob.getId()));
        ids.add(req.getBlobId());
        if (ids.size() > 50) throw new ServiceException(400, "每张质检单最多上传50份附件");
        storageApi.bindAttachments("file", recordType(req.getType()), req.getId(), List.copyOf(ids));
        return success(true);
    }
 
    @DeleteMapping("/remove")
    @PreAuthorize("@ss.hasPermission('mes:qc-' + #type.toLowerCase() + ':update')")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> remove(
            @RequestParam @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") String type,
            @RequestParam @NotNull Long id, @RequestParam @NotNull Long blobId) {
        lockDocument(type, id);
        validateDocument(type, id);
        storageApi.removeReference("file", recordType(type), id, blobId);
        return success(true);
    }
 
    // ==================== 过程检验「检验行」附件 ====================
 
    /** 过程检验行附件:recordId 为检验结果明细 ID,每行独立上传 */
    @GetMapping("/ipqc-detail/list")
    @PreAuthorize("@ss.hasPermission('mes:qc-ipqc:query')")
    public CommonResult<List<StorageBlobRespDTO>> listIpqcDetail(@RequestParam @NotNull Long detailId) {
        validateIpqcDetail(detailId);
        var files = storageApi.listAttachments(IPQC_DETAIL_RECORD_TYPE, detailId);
        return success(files == null ? List.of() : files);
    }
 
    @PostMapping("/ipqc-detail/save")
    @PreAuthorize("@ss.hasPermission('mes:qc-ipqc:update')")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> saveIpqcDetail(@Valid @RequestBody IpqcDetailSaveReq req) {
        validateIpqcDetail(req.getDetailId());
        storageApi.bindAttachments("file", IPQC_DETAIL_RECORD_TYPE, req.getDetailId(),
                req.getBlobIds().stream().distinct().toList());
        return success(true);
    }
 
    @DeleteMapping("/ipqc-detail/remove")
    @PreAuthorize("@ss.hasPermission('mes:qc-ipqc:update')")
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<Boolean> removeIpqcDetail(@RequestParam @NotNull Long detailId,
                                                  @RequestParam @NotNull Long blobId) {
        validateIpqcDetail(detailId);
        storageApi.removeReference("file", IPQC_DETAIL_RECORD_TYPE, detailId, blobId);
        return success(true);
    }
 
    private static final String IPQC_DETAIL_RECORD_TYPE = "mes_qc_ipqc_detail_attachment";
 
    /** 校验明细存在且属于过程检验结果 */
    private void validateIpqcDetail(Long detailId) {
        var ids = jdbcTemplate.queryForList(
                "SELECT d.id FROM mes_qc_indicator_result_detail d "
                        + "JOIN mes_qc_indicator_result r ON r.id = d.result_id "
                        + "WHERE d.id = ? AND r.qc_type = ? AND d.deleted = 0 AND r.deleted = 0",
                Long.class, detailId, cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum.IPQC.getType());
        if (ids.isEmpty()) {
            throw new ServiceException(400, "过程检验行不存在或不属于过程检验单,无法上传附件");
        }
    }
 
    @Data public static class IpqcDetailSaveReq {
        @NotNull private Long detailId;
        /** 允许空列表:表示清空该行附件 */
        @NotNull @Size(max = 50) private List<@NotNull Long> blobIds;
    }
 
    private String recordType(String type) {
        return switch (type) {
            case "iqc", "ipqc", "oqc", "rqc", "patrol" -> "mes_qc_" + type + "_attachment";
            default -> throw new ServiceException(400, "质检类型无效");
        };
    }
 
    private void lockDocument(String type, Long id) {
        String table = switch (type) {
            case "iqc" -> "mes_qc_iqc";
            case "ipqc" -> "mes_qc_ipqc";
            case "oqc" -> "mes_qc_oqc";
            case "rqc" -> "mes_qc_rqc";
            case "patrol" -> "mes_qc_patrol";
            default -> throw new ServiceException(400, "质检类型无效");
        };
        var ids = jdbcTemplate.queryForList("SELECT id FROM " + table + " WHERE id = ? AND deleted = 0 FOR UPDATE", Long.class, id);
        if (ids.isEmpty()) throw new ServiceException(400, "质检单不存在");
    }
 
    private Integer validateDocument(String type, Long id) {
        return switch (type) {
            case "iqc" -> iqcService.validateIqcExists(id).getStatus();
            case "ipqc" -> ipqcService.validateIpqcExists(id).getStatus();
            case "oqc" -> oqcService.validateOqcExists(id).getStatus();
            case "rqc" -> rqcService.validateRqcExists(id).getStatus();
            case "patrol" -> {
                var ids = jdbcTemplate.queryForList(
                        "SELECT id FROM mes_qc_patrol WHERE id = ? AND tenant_id = ? AND deleted = 0",
                        Long.class, id, TenantContextHolder.getRequiredTenantId());
                if (ids.isEmpty()) throw new ServiceException(400, "巡检记录不存在");
                yield 0;
            }
            default -> throw new ServiceException(400, "质检类型无效");
        };
    }
 
    @Data public static class SaveReq {
        @NotNull @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") private String type;
        @NotNull private Long id;
        @NotEmpty @Size(max = 50) private List<@NotNull Long> blobIds;
    }
    @Data public static class AppendReq {
        @NotNull @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") private String type;
        @NotNull private Long id;
        @NotNull private Long blobId;
    }
}