刘光辉
昨天 bb638871a7fb692d80f1b7a758f991dc0879002c
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
package jnpf.limsService.impl;
 
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jnpf.base.ActionResult;
import jnpf.base.ActionResultCode;
import jnpf.flowable.TaskApi;
import jnpf.flowable.model.task.FlowModel;
import jnpf.limsEntity.JianyanJielun;
import jnpf.limsEntity.LimsAdDiaochabiaoEntity;
import jnpf.limsEntity.LimsJianyanResultItem;
import jnpf.limsEntity.LimsJianyanXiangEntity;
import jnpf.limsEntity.LimsOosOotDiaochabiaoEntity;
import jnpf.limsEntity.YichangDiaochaLeixing;
import jnpf.limsEntity.ZhiliangBiaozhunGuanliZixiangEntity;
import jnpf.limsService.LimsAdDiaochabiaoService;
import jnpf.limsService.LimsFlowTemplateResolver;
import jnpf.limsService.LimsOosOotDiaochabiaoService;
import jnpf.limsService.LimsYichangDiaochaService;
import jnpf.limsService.ZhiliangBiaozhunGuanliZixiangService;
import jnpf.util.UserProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
 
@Service
public class LimsYichangDiaochaServiceImpl implements LimsYichangDiaochaService {
 
    @Autowired
    private ZhiliangBiaozhunGuanliZixiangService zhiliangBiaozhunGuanliZixiangService;
 
    @Autowired
    private LimsAdDiaochabiaoService limsAdDiaochabiaoService;
 
    @Autowired
    private LimsOosOotDiaochabiaoService limsOosOotDiaochabiaoService;
 
    @Autowired
    private LimsFlowTemplateResolver limsFlowTemplateResolver;
 
    @Autowired
    private TaskApi taskApi;
 
    @Override
    public void createIfNeeded(LimsJianyanXiangEntity row, LimsJianyanResultItem item) {
        if (row == null || item == null) {
            return;
        }
        YichangDiaochaLeixing t = YichangDiaochaLeixing.fromCode(item.getYichangDiaochaLeixing());
        if (t == null || !t.isInvestigation()) {
            return;
        }
 
        // 确定性反查:lims_jianyan_xiang.f_foreign_id = zixiang.f_id(= 表单检验项目id);
        // zixiang.f_foreign_id = guanli.f_id(= 表单质量标准)。f_foreign_id 创建后稳定、报告流程不覆盖。
        String jianyanXiangmuId = row.getForeignId();
        String zhiliangBiaozhunId = null;
        if (jianyanXiangmuId != null && !jianyanXiangmuId.isEmpty()) {
            ZhiliangBiaozhunGuanliZixiangEntity zixiang =
                    zhiliangBiaozhunGuanliZixiangService.getById(jianyanXiangmuId);
            if (zixiang != null) {
                zhiliangBiaozhunId = zixiang.getForeignId();
            }
        }
 
        if (t == YichangDiaochaLeixing.AD) {
            launchAd(row, item, jianyanXiangmuId, zhiliangBiaozhunId, t);
        } else {
            // OOS / OOT:同一张表 + 同一模板,diaocha_fenlei 用枚举 code(oos/oot)
            launchOosOot(row, item, jianyanXiangmuId, zhiliangBiaozhunId, t);
        }
    }
 
    private void launchAd(LimsJianyanXiangEntity row, LimsJianyanResultItem item, String jianyanXiangmuId,
                          String zhiliangBiaozhunId, YichangDiaochaLeixing t) {
        // 判重:自然键 = qingyan_liushuihao + jianyan_xiangmu_id;已存在则跳过,防重复发起
        QueryWrapper<LimsAdDiaochabiaoEntity> dup = new QueryWrapper<>();
        dup.eq("qingyan_liushuihao", row.getJianyanLiushuihao());
        dup.eq("jianyan_xiangmu_id", jianyanXiangmuId);
        dup.and(w -> w.isNull("f_delete_mark").or().ne("f_delete_mark", 1));
        if (limsAdDiaochabiaoService.count(dup) > 0) {
            return;
        }
 
        Date now = new Date();
        Map<String, Object> formData = baseFormData(row, jianyanXiangmuId, zhiliangBiaozhunId, now);
        formData.put("ad_diaocha_bianhao", nextBianhao("AD", now, true));
        // 异常描述:AD 表单发起阶段必填,用检验结果数据拼一句客观描述,调查员可再改
        formData.put("yichang_miaoshu", describeAnomaly(row, item));
        launch(t, formData, row.getJianyanLiushuihao());
    }
 
    private void launchOosOot(LimsJianyanXiangEntity row, LimsJianyanResultItem item, String jianyanXiangmuId,
                              String zhiliangBiaozhunId, YichangDiaochaLeixing t) {
        String fenlei = t.getCode(); // oos / oot
        // 判重:自然键 = qingyan_liushuihao + jianyan_xiangmu_id + diaocha_fenlei
        QueryWrapper<LimsOosOotDiaochabiaoEntity> dup = new QueryWrapper<>();
        dup.eq("qingyan_liushuihao", row.getJianyanLiushuihao());
        dup.eq("jianyan_xiangmu_id", jianyanXiangmuId);
        dup.eq("diaocha_fenlei", fenlei);
        dup.and(w -> w.isNull("f_delete_mark").or().ne("f_delete_mark", 1));
        if (limsOosOotDiaochabiaoService.count(dup) > 0) {
            return;
        }
 
        Date now = new Date();
        Map<String, Object> formData = baseFormData(row, jianyanXiangmuId, zhiliangBiaozhunId, now);
        formData.put("diaocha_fenlei", fenlei);
        formData.put("diaocha_bianhao", nextBianhao(fenlei.toUpperCase(), now, false));
        // 检验描述:OOS/OOT 表单发起阶段必填,同 AD 异常描述
        formData.put("jianyan_miaoshu", describeAnomaly(row, item));
        launch(t, formData, row.getJianyanLiushuihao());
    }
 
    /** 公共表单字段(key = 物理列名,AD/OOS/OOT 同源)。日期用 yyyy-MM-dd 字符串(与库内现有数据一致)。 */
    private Map<String, Object> baseFormData(LimsJianyanXiangEntity row, String jianyanXiangmuId,
                                             String zhiliangBiaozhunId, Date now) {
        String today = new SimpleDateFormat("yyyy-MM-dd").format(now);
        String currentUserId = UserProvider.getUser().getUserId();
        Map<String, Object> m = new LinkedHashMap<>();
        m.put("yangpin_leixing", row.getBizType());
        m.put("yangpin_mingcheng", row.getChanpinMingcheng());
        m.put("yangpin_id", row.getChanpinId());
        m.put("pihao", row.getPihao());
        m.put("qingyan_liushuihao", row.getJianyanLiushuihao());
        m.put("zhiliang_biaozhun", zhiliangBiaozhunId);
        m.put("jianyan_xiangmu", row.getJianyanXiangmuMingcheng());
        m.put("jianyan_xiangmu_id", jianyanXiangmuId);
        m.put("faxian_riqi", today);
        m.put("jianyan_ren", currentUserId);
        m.put("jianyan_riqi", today);
        m.put("qingyandan_id", row.getQingyandanId());
        return m;
    }
 
    /**
     * 异常描述 / 检验描述(表单发起阶段必填):用 检验项目 + 本次检验结果 + 标准规定 + 检验结论
     * 拼一句客观描述,调查员可在表单里再修改。结果/结论取自本次录入 item,标准/项目取自检验项 row。
     */
    private String describeAnomaly(LimsJianyanXiangEntity row, LimsJianyanResultItem item) {
        String xiangmu = safe(row.getJianyanXiangmuMingcheng());
        String jieguo = safe(item.getJianyanJieguo());
        String biaozhun = safe(row.getBiaozhunGuiding());
        String jielun = jielunLabel(item.getJianyanJielun());
        return "检验项目【" + xiangmu + "】检验结果【" + jieguo + "】,标准规定【" + biaozhun + "】,检验结论【" + jielun + "】。";
    }
 
    private static String safe(String s) {
        return s == null ? "" : s;
    }
 
    /** 检验结论 code → 中文 label;空 / 未知 code 不抛异常(返回原值或空) */
    private static String jielunLabel(String code) {
        if (code == null || code.isEmpty()) {
            return "";
        }
        try {
            return JianyanJielun.fromCode(code).getLabel();
        } catch (Exception e) {
            return code;
        }
    }
 
    /** 解析 templateId → 以当前登录用户名义 launchFlow → 校验返回;失败抛异常(交 submit 收集为发起失败)。 */
    private void launch(YichangDiaochaLeixing t, Map<String, Object> formData, String liushuihao) {
        String enCode = t.getFlowTemplateEnCode();
        String templateId = limsFlowTemplateResolver.resolveTemplateId(enCode);
        if (templateId == null || templateId.isEmpty()) {
            throw new RuntimeException("未找到流程模板(enCode=" + enCode + "),无法发起 " + t.getCode() + " 调查流程");
        }
        FlowModel model = new FlowModel();
        model.setTemplateId(templateId);
        model.setUserIds(Collections.singletonList(UserProvider.getUser().getUserId()));
        model.setFormDataList(Collections.singletonList(formData));
        // hasPermission 默认 false:两模板 visibleType=1 全局可发起,不做发起权限过滤
        ActionResult result = taskApi.launchFlow(model);
        // 成功判定用平台常量 ActionResultCode.SUCCESS(与 VisualdevModelDataController 发起后判定一致);
        // 不能硬编码 "200"——launchFlow 成功返回的 code 是 SUCCESS 码、msg 为"提交成功,请耐心等待",并非 "200"。
        if (result == null || !ActionResultCode.SUCCESS.getCode().equals(result.getCode())) {
            throw new RuntimeException("发起 " + t.getCode() + " 调查流程失败(流水号=" + liushuihao + "):"
                    + (result == null ? "无返回" : result.getMsg()));
        }
    }
 
    /**
     * 生成"前缀+yyyyMMdd+3位日内流水"编号;流水 = 当天该前缀已有行数 + 1。
     * isAd=true 查 lims_ad_diaochabiao.ad_diaocha_bianhao,否则查 lims_oos_oot_diaochabiao.diaocha_bianhao。
     * LIMS 单据量低,并发碰撞可忽略;真正的重复由判重自然键拦截。
     */
    private String nextBianhao(String prefix, Date now, boolean isAd) {
        String head = prefix + new SimpleDateFormat("yyyy").format(now);
        long seq;
        if (isAd) {
            QueryWrapper<LimsAdDiaochabiaoEntity> w = new QueryWrapper<>();
            w.likeRight("ad_diaocha_bianhao", head);
            seq = limsAdDiaochabiaoService.count(w) + 1;
        } else {
            QueryWrapper<LimsOosOotDiaochabiaoEntity> w = new QueryWrapper<>();
            w.likeRight("diaocha_bianhao", head);
            seq = limsOosOotDiaochabiaoService.count(w) + 1;
        }
        return String.format("%s%04d", head, seq);
    }
}