刘光辉
15 小时以前 34981c30a78e8bbd7791131059a9210f9928b62c
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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
package jnpf.limsService.impl;
 
import jnpf.audit.sdk.annotation.AuditLog;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jnpf.base.UserInfo;
import jnpf.exception.DataException;
import jnpf.limsEntity.BizStatus;
import jnpf.limsEntity.LimsQingyandanEntity;
import jnpf.limsEntity.LimsSxtQuyangJihuaEntity;
import jnpf.limsEntity.LimsSxtQuyangJihuaZhongzhiEntity;
import jnpf.limsEntity.LimsSxtQuyangZhongzhiShenqingEntity;
import jnpf.limsEntity.LimsSxtQuyangZhongzhiShenqingZixiangEntity;
import jnpf.limsMapper.LimsSxtQuyangJihuaMapper;
import jnpf.limsMapper.LimsSxtQuyangJihuaZhongzhiMapper;
import jnpf.limsMapper.LimsSxtQuyangZhongzhiShenqingMapper;
import jnpf.limsMapper.LimsSxtQuyangZhongzhiShenqingZixiangMapper;
import jnpf.limsMapper.LimsQingyandanMapper;
import jnpf.limsService.LimsQingyandanService;
import jnpf.limsService.LimsSxtJihuaService;
import jnpf.limsService.LimsSxtQuyangJihuaRenwuService;
import jnpf.limsService.event.LimsSxtPlanApprovedEvent;
import jnpf.limsService.support.LimsSxtFlowStatusResolver;
import jnpf.limsService.support.LimsSxtPlanStatusResolver;
import jnpf.util.UserProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
 
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
 
/**
 * 水系统取样计划定时任务,结构照搬 LimsJianceTaskRemindServiceImpl。
 *
 * <p>注意:@Scheduled 是进程内定时器,多实例部署会每个实例各触发一次(重复发)。
 * 当前按单实例部署,无此问题;若将来横向扩容,需在 cronTrigger 入口加分布式锁
 * (如 Redis SETNX 当天 key)保证单点执行。
 */
@Slf4j
@Service
public class LimsSxtJihuaServiceImpl implements LimsSxtJihuaService {
 
    private static final Integer FLOW_STATE_APPROVED = 2;
 
    private final ApplicationEventPublisher eventPublisher;
 
    @Autowired
    public LimsSxtJihuaServiceImpl(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }
 
    @Autowired
    private LimsSxtQuyangJihuaMapper jihuaMapper;
    @Autowired
    private LimsSxtQuyangJihuaZhongzhiMapper zhongzhiMapper;
    @Autowired
    private LimsSxtQuyangZhongzhiShenqingMapper quyangZhongzhiShenqingMapper;
    @Autowired
    private LimsSxtQuyangZhongzhiShenqingZixiangMapper quyangZhongzhiShenqingZixiangMapper;
    @Autowired
    private LimsQingyandanMapper qingyandanMapper;
    @Autowired
    private LimsQingyandanService qingyandanService;
    @Autowired
    private LimsSxtRenwuQingyandanZhuangtaiTongbuService renwuQingyandanZhuangtaiTongbuService;
    @Autowired
    private LimsSxtQuyangJihuaRenwuService renwuService;
 
    @Override
    public void runTodayJihua(boolean dryRun) {
        shengchengDangriQingyandan(LocalDate.now(), dryRun);
    }
 
    @Override
    @AuditLog(action = "PLAN_RUN", label = "执行水系统当天计划", bizType = "SXT",
            bizCodeExpr = "#dryRun ? '' : 'SXT_PLAN_RUN'", targetTable = "lims_sxt_quyang_jihua_renwu")
    public void runDailyJihua(LocalDate date, boolean dryRun) {
        shengchengDangriQingyandan(date, dryRun);
    }
 
    @Override
    @AuditLog(action = "PLAN_RUN", label = "执行指定水系统计划", bizType = "SXT",
            bizCodeExpr = "#jihuaId", bizCodeRequired = false, targetTable = "lims_sxt_quyang_jihua")
    public void runJihua(String jihuaId) {
        UserInfo user = UserProvider.getUser();
        String tenantId = user == null || !StringUtils.hasText(user.getTenantId()) ? "0" : user.getTenantId();
        int generated = renwuService.shengchengDaishengchengQingyandanByJihua(jihuaId, tenantId, LocalDate.now());
        log.info("水系统:手动执行计划 {},生成 {} 张请验单", jihuaId, generated);
    }
 
    private void shengchengDangriQingyandan(LocalDate date, boolean dryRun) {
        if (dryRun) {
            log.info("水系统:dry run 跳过 {} 的待办任务生成", date);
            return;
        }
        int generated = renwuService.shengchengDaishengchengQingyandan(date);
        log.info("水系统:执行 {} 的待办任务,生成 {} 张请验单,dryRun={}", date, generated, dryRun);
    }
 
    @Override
    public void createJihuaQuyangDan(LimsQingyandanEntity qingyandan) {
        qingyandanService.sxtAutoSubmit(qingyandan);
    }
 
    @Override
    public List<LimsQingyandanEntity> chaxunDaiquyangQingyandan(String jianyanLiushuihao, String quyangDidian) {
        UserInfo user = UserProvider.getUser();
        String tenantId = user == null || !StringUtils.hasText(user.getTenantId()) ? "0" : user.getTenantId();
 
        LambdaQueryWrapper<LimsQingyandanEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LimsQingyandanEntity::getTenantId, tenantId)
                .eq(LimsQingyandanEntity::getBizType, "SXT")
                .eq(LimsQingyandanEntity::getQuyangStatus, BizStatus.PENDING_FETCH.getCode())
                .and(item -> item.isNull(LimsQingyandanEntity::getDeleteMark)
                        .or().ne(LimsQingyandanEntity::getDeleteMark, 1));
        if (StringUtils.hasText(jianyanLiushuihao)) {
            wrapper.like(LimsQingyandanEntity::getJianyanLiushuihao, jianyanLiushuihao.trim());
        }
        if (StringUtils.hasText(quyangDidian)) {
            wrapper.like(LimsQingyandanEntity::getQuyangDidian, quyangDidian.trim());
        }
        wrapper.orderByAsc(LimsQingyandanEntity::getCreatorTime)
                .orderByAsc(LimsQingyandanEntity::getId);
        return qingyandanMapper.selectList(wrapper);
    }
 
    @Override
    @Transactional(rollbackFor = Exception.class)
    @AuditLog(action = "FLOW_START", label = "水系统计划流程发起", bizType = "SXT",
            bizCodeExpr = "#flowId", bizCodeRequired = false, targetTable = "lims_sxt_quyang_jihua")
    public void syncFlowStarted(String flowId, String taskId) {
        validateFlowKey(flowId, taskId);
        LimsSxtQuyangJihuaEntity jihua = findJihuaByFlow(flowId, taskId);
        LimsSxtQuyangJihuaZhongzhiEntity zhongzhi = findZhongzhiByFlow(flowId, taskId);
        LimsSxtQuyangZhongzhiShenqingEntity quyangZhongzhiShenqing = findQuyangZhongzhiShenqingByFlow(flowId, taskId);
        ensureSingleBusiness(jihua, zhongzhi, quyangZhongzhiShenqing, flowId, taskId);
 
        if (jihua != null) {
            syncJihuaStarted(jihua, flowId, taskId);
        } else if (zhongzhi != null) {
            syncZhongzhiStarted(zhongzhi, flowId, taskId);
        } else {
            syncQuyangZhongzhiShenqingStarted(quyangZhongzhiShenqing);
        }
    }
 
    @Override
    @Transactional(rollbackFor = Exception.class)
    @AuditLog(action = "FLOW_END", label = "水系统计划流程结束", bizType = "SXT",
            bizCodeExpr = "#flowId", bizCodeRequired = false, targetTable = "lims_sxt_quyang_jihua")
    public void syncFlowEnded(String flowId, String taskId, Integer flowState) {
        syncFlowEndedStatus(flowId, taskId, flowState);
    }
 
    private void syncFlowEndedStatus(String flowId, String taskId, Integer flowState) {
        validateFlowKey(flowId, taskId);
        String targetStatus = LimsSxtFlowStatusResolver.resolveBizStatus(flowState)
                .orElseThrow(() -> new DataException("未知的工作流状态:" + flowState));
        if (!LimsSxtFlowStatusResolver.COMPLETED.equals(targetStatus)) {
            throw new DataException("流程尚未结束,当前状态:" + flowState);
        }
 
        LimsSxtQuyangJihuaEntity jihua = findJihuaByFlow(flowId, taskId);
        LimsSxtQuyangJihuaZhongzhiEntity zhongzhi = findZhongzhiByFlow(flowId, taskId);
        LimsSxtQuyangZhongzhiShenqingEntity quyangZhongzhiShenqing = findQuyangZhongzhiShenqingByFlow(flowId, taskId);
        ensureSingleBusiness(jihua, zhongzhi, quyangZhongzhiShenqing, flowId, taskId);
 
        if (jihua != null) {
            syncJihuaEnded(jihua, flowId, taskId, flowState, targetStatus);
        } else if (zhongzhi != null) {
            syncZhongzhiEnded(zhongzhi, flowId, taskId, flowState, targetStatus);
        } else {
            syncQuyangZhongzhiShenqingEnded(quyangZhongzhiShenqing, flowState, targetStatus);
        }
    }
 
    private void syncJihuaStarted(LimsSxtQuyangJihuaEntity jihua, String flowId, String taskId) {
        if (LimsSxtFlowStatusResolver.COMPLETED.equals(jihua.getBizStatus())) {
            return;
        }
        boolean changed = !Objects.equals(jihua.getBizStatus(), LimsSxtFlowStatusResolver.PROCESSING)
                || !Objects.equals(jihua.getPlanStatus(), LimsSxtPlanStatusResolver.DISABLED);
        if (!changed) {
            return;
        }
        if (!markJihuaStarted(jihua.getId())) {
            return;
        }
    }
 
    private void syncZhongzhiStarted(LimsSxtQuyangJihuaZhongzhiEntity zhongzhi, String flowId, String taskId) {
        if (LimsSxtFlowStatusResolver.COMPLETED.equals(zhongzhi.getBizStatus())) {
            return;
        }
        if (!Objects.equals(zhongzhi.getBizStatus(), LimsSxtFlowStatusResolver.PROCESSING)) {
            if (!markZhongzhiStarted(zhongzhi.getId())) {
                return;
            }
        }
        markApprovedJihuaTerminating(zhongzhi, flowId, taskId);
    }
 
    private void syncJihuaEnded(LimsSxtQuyangJihuaEntity jihua, String flowId, String taskId,
                                Integer flowState, String targetStatus) {
        boolean passed = LimsSxtFlowStatusResolver.isApproved(flowState);
        String targetPlanStatus = passed
                ? LimsSxtPlanStatusResolver.AVAILABLE : LimsSxtPlanStatusResolver.DISABLED;
        boolean changed = !Objects.equals(jihua.getBizStatus(), targetStatus)
                || !Objects.equals(jihua.getPlanStatus(), targetPlanStatus);
        if (changed) {
            updateJihua(jihua.getId(), targetStatus, targetPlanStatus);
        }
        if (passed) {
            eventPublisher.publishEvent(new LimsSxtPlanApprovedEvent(jihua.getId(), jihua.getFTenantId()));
        }
    }
 
    private void syncZhongzhiEnded(LimsSxtQuyangJihuaZhongzhiEntity zhongzhi, String flowId, String taskId,
                                   Integer flowState, String targetStatus) {
        if (!Objects.equals(zhongzhi.getBizStatus(), targetStatus)) {
            updateZhongzhi(zhongzhi.getId(), targetStatus);
        }
        syncApprovedJihuaTerminationStatus(zhongzhi, flowId, taskId, flowState);
    }
 
    private void syncQuyangZhongzhiShenqingStarted(LimsSxtQuyangZhongzhiShenqingEntity shenqing) {
        if (LimsSxtFlowStatusResolver.COMPLETED.equals(shenqing.getBizStatus())
                || Objects.equals(shenqing.getBizStatus(), LimsSxtFlowStatusResolver.PROCESSING)) {
            return;
        }
        if (!markQuyangZhongzhiShenqingStarted(shenqing)) {
            return;
        }
    }
 
    private void syncQuyangZhongzhiShenqingEnded(LimsSxtQuyangZhongzhiShenqingEntity shenqing,
                                                  Integer flowState, String targetStatus) {
        if (!Objects.equals(shenqing.getBizStatus(), targetStatus)) {
            updateQuyangZhongzhiShenqing(shenqing, targetStatus);
        }
        if (LimsSxtFlowStatusResolver.isApproved(flowState)) {
            syncApprovedQuyangTerminationStatus(shenqing);
        }
    }
 
    private void syncApprovedQuyangTerminationStatus(LimsSxtQuyangZhongzhiShenqingEntity shenqing) {
        List<LimsSxtQuyangZhongzhiShenqingZixiangEntity> children = listActiveQuyangZhongzhiChildren(
                shenqing.getId(), shenqing.getFTenantId());
        int updated = 0;
        for (LimsSxtQuyangZhongzhiShenqingZixiangEntity child : children) {
            if (!StringUtils.hasText(child.getQingyandanId())) {
                log.warn("取样终止申请子表未关联请验单,childId={}", child.getId());
                continue;
            }
            if (terminatePendingQingyandan(child)) {
                updated++;
            }
        }
        log.info("取样终止申请审批通过,同步终止 {} 张待取样请验单,terminationId={}", updated, shenqing.getId());
    }
 
    private void markApprovedJihuaTerminating(LimsSxtQuyangJihuaZhongzhiEntity zhongzhi, String flowId,
                                              String taskId) {
        if (!StringUtils.hasText(zhongzhi.getJihuaMingcheng())) {
            log.warn("计划终止申请已发起但未关联原取样计划,terminationId={}", zhongzhi.getId());
            return;
        }
        LimsSxtQuyangJihuaEntity jihua = findApprovedJihua(zhongzhi.getJihuaMingcheng(), zhongzhi.getFTenantId());
        if (jihua == null) {
            log.warn("计划终止申请已发起,但原取样计划不是已审批通过状态,terminationId={}, jihuaId={}",
                    zhongzhi.getId(), zhongzhi.getJihuaMingcheng());
            return;
        }
        if (!Objects.equals(jihua.getPlanStatus(), LimsSxtPlanStatusResolver.AVAILABLE)) {
            return;
        }
        if (!updateJihuaPlanStatus(jihua.getId(), jihua.getPlanStatus(), LimsSxtPlanStatusResolver.TERMINATING)) {
            return;
        }
    }
 
    private void syncApprovedJihuaTerminationStatus(LimsSxtQuyangJihuaZhongzhiEntity zhongzhi, String flowId,
                                                    String taskId, Integer flowState) {
        if (!StringUtils.hasText(zhongzhi.getJihuaMingcheng())) {
            log.warn("计划终止申请已办结但未关联原取样计划,terminationId={}", zhongzhi.getId());
            return;
        }
        LimsSxtQuyangJihuaEntity jihua = findApprovedJihua(zhongzhi.getJihuaMingcheng(), zhongzhi.getFTenantId());
        if (jihua == null) {
            log.warn("计划终止申请已办结,但原取样计划不是已审批通过状态,terminationId={}, jihuaId={}",
                    zhongzhi.getId(), zhongzhi.getJihuaMingcheng());
            return;
        }
        String targetPlanStatus = LimsSxtPlanStatusResolver.resolveTerminationStatus(jihua.getPlanStatus(), flowState);
        if (Objects.equals(jihua.getPlanStatus(), targetPlanStatus)) {
            return;
        }
        if (!updateJihuaPlanStatus(jihua.getId(), jihua.getPlanStatus(), targetPlanStatus)) {
            return;
        }
        boolean passed = LimsSxtFlowStatusResolver.isApproved(flowState);
        if (passed && LimsSxtPlanStatusResolver.TERMINATED.equals(targetPlanStatus)) {
            renwuService.quxiaoWeishengchengRenwuByJihua(
                    jihua.getId(), jihua.getFTenantId(), "plan_terminated");
        }
    }
 
    private void updateJihua(String id, String bizStatus, String planStatus) {
        LambdaUpdateWrapper<LimsSxtQuyangJihuaEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(LimsSxtQuyangJihuaEntity::getId, id)
                .and(item -> item.isNull(LimsSxtQuyangJihuaEntity::getFDeleteMark)
                        .or().ne(LimsSxtQuyangJihuaEntity::getFDeleteMark, 1))
                .set(LimsSxtQuyangJihuaEntity::getBizStatus, bizStatus)
                .set(LimsSxtQuyangJihuaEntity::getPlanStatus, planStatus);
        if (jihuaMapper.update(null, wrapper) != 1) {
            throw new DataException("更新取样计划状态失败");
        }
    }
 
    private boolean updateJihuaPlanStatus(String id, String currentPlanStatus, String targetPlanStatus) {
        LambdaUpdateWrapper<LimsSxtQuyangJihuaEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(LimsSxtQuyangJihuaEntity::getId, id)
                .and(item -> item.isNull(LimsSxtQuyangJihuaEntity::getFDeleteMark)
                        .or().ne(LimsSxtQuyangJihuaEntity::getFDeleteMark, 1))
                .eq(LimsSxtQuyangJihuaEntity::getPlanStatus, currentPlanStatus)
                .set(LimsSxtQuyangJihuaEntity::getPlanStatus, targetPlanStatus);
        return jihuaMapper.update(null, wrapper) == 1;
    }
 
    /**
     * 起始事件只能把未办结记录推进到在办,避免延迟/重放事件覆盖结束事件的最终结果。
     */
    private boolean markJihuaStarted(String id) {
        LambdaUpdateWrapper<LimsSxtQuyangJihuaEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(LimsSxtQuyangJihuaEntity::getId, id)
                .and(item -> item.isNull(LimsSxtQuyangJihuaEntity::getFDeleteMark)
                        .or().ne(LimsSxtQuyangJihuaEntity::getFDeleteMark, 1))
                .and(item -> item.isNull(LimsSxtQuyangJihuaEntity::getBizStatus)
                        .or().ne(LimsSxtQuyangJihuaEntity::getBizStatus, LimsSxtFlowStatusResolver.COMPLETED))
                .set(LimsSxtQuyangJihuaEntity::getBizStatus, LimsSxtFlowStatusResolver.PROCESSING)
                .set(LimsSxtQuyangJihuaEntity::getPlanStatus, LimsSxtPlanStatusResolver.DISABLED);
        return jihuaMapper.update(null, wrapper) > 0;
    }
 
    private void updateZhongzhi(String id, String bizStatus) {
        LambdaUpdateWrapper<LimsSxtQuyangJihuaZhongzhiEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(LimsSxtQuyangJihuaZhongzhiEntity::getId, id)
                .set(LimsSxtQuyangJihuaZhongzhiEntity::getBizStatus, bizStatus);
        if (zhongzhiMapper.update(null, wrapper) != 1) {
            throw new DataException("更新计划终止申请状态失败");
        }
    }
 
    private boolean markZhongzhiStarted(String id) {
        LambdaUpdateWrapper<LimsSxtQuyangJihuaZhongzhiEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(LimsSxtQuyangJihuaZhongzhiEntity::getId, id)
                .and(item -> item.isNull(LimsSxtQuyangJihuaZhongzhiEntity::getBizStatus)
                        .or().ne(LimsSxtQuyangJihuaZhongzhiEntity::getBizStatus, LimsSxtFlowStatusResolver.COMPLETED))
                .set(LimsSxtQuyangJihuaZhongzhiEntity::getBizStatus, LimsSxtFlowStatusResolver.PROCESSING);
        return zhongzhiMapper.update(null, wrapper) > 0;
    }
 
    private void updateQuyangZhongzhiShenqing(LimsSxtQuyangZhongzhiShenqingEntity shenqing, String bizStatus) {
        LambdaUpdateWrapper<LimsSxtQuyangZhongzhiShenqingEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(LimsSxtQuyangZhongzhiShenqingEntity::getId, shenqing.getId())
                .eq(LimsSxtQuyangZhongzhiShenqingEntity::getFTenantId, shenqing.getFTenantId())
                .and(item -> item.isNull(LimsSxtQuyangZhongzhiShenqingEntity::getFDeleteMark)
                        .or().ne(LimsSxtQuyangZhongzhiShenqingEntity::getFDeleteMark, 1))
                .set(LimsSxtQuyangZhongzhiShenqingEntity::getBizStatus, bizStatus);
        if (quyangZhongzhiShenqingMapper.update(null, wrapper) != 1) {
            throw new DataException("更新取样终止申请状态失败");
        }
    }
 
    private boolean markQuyangZhongzhiShenqingStarted(LimsSxtQuyangZhongzhiShenqingEntity shenqing) {
        LambdaUpdateWrapper<LimsSxtQuyangZhongzhiShenqingEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(LimsSxtQuyangZhongzhiShenqingEntity::getId, shenqing.getId())
                .eq(LimsSxtQuyangZhongzhiShenqingEntity::getFTenantId, shenqing.getFTenantId())
                .and(item -> item.isNull(LimsSxtQuyangZhongzhiShenqingEntity::getFDeleteMark)
                        .or().ne(LimsSxtQuyangZhongzhiShenqingEntity::getFDeleteMark, 1))
                .and(item -> item.isNull(LimsSxtQuyangZhongzhiShenqingEntity::getBizStatus)
                        .or().ne(LimsSxtQuyangZhongzhiShenqingEntity::getBizStatus, LimsSxtFlowStatusResolver.COMPLETED))
                .set(LimsSxtQuyangZhongzhiShenqingEntity::getBizStatus, LimsSxtFlowStatusResolver.PROCESSING);
        return quyangZhongzhiShenqingMapper.update(null, wrapper) > 0;
    }
 
    private List<LimsSxtQuyangZhongzhiShenqingZixiangEntity> listActiveQuyangZhongzhiChildren(String shenqingId,
                                                                                                  String tenantId) {
        LambdaQueryWrapper<LimsSxtQuyangZhongzhiShenqingZixiangEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LimsSxtQuyangZhongzhiShenqingZixiangEntity::getFForeignId, shenqingId)
                .eq(LimsSxtQuyangZhongzhiShenqingZixiangEntity::getFTenantId, tenantId)
                .and(item -> item.isNull(LimsSxtQuyangZhongzhiShenqingZixiangEntity::getFDeleteMark)
                        .or().ne(LimsSxtQuyangZhongzhiShenqingZixiangEntity::getFDeleteMark, 1));
        return quyangZhongzhiShenqingZixiangMapper.selectList(wrapper);
    }
 
    private boolean terminatePendingQingyandan(LimsSxtQuyangZhongzhiShenqingZixiangEntity child) {
        LimsQingyandanEntity qingyandan = qingyandanMapper.selectById(child.getQingyandanId());
        if (qingyandan == null) {
            return false;
        }
        LambdaUpdateWrapper<LimsQingyandanEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(LimsQingyandanEntity::getId, child.getQingyandanId())
                .eq(LimsQingyandanEntity::getTenantId, child.getFTenantId())
                .eq(LimsQingyandanEntity::getBizType, "SXT")
                .eq(LimsQingyandanEntity::getQuyangStatus, BizStatus.PENDING_FETCH.getCode())
                .and(item -> item.isNull(LimsQingyandanEntity::getDeleteMark)
                        .or().ne(LimsQingyandanEntity::getDeleteMark, 1))
                .set(LimsQingyandanEntity::getQuyangStatus, BizStatus.DELETED.getCode());
        if (qingyandanMapper.update(null, wrapper) != 1) {
            return false;
        }
        qingyandan.setQuyangStatus(BizStatus.DELETED.getCode());
        renwuQingyandanZhuangtaiTongbuService.tongbu(qingyandan, "quyang_zhongzhi_tongguo");
        return true;
    }
 
    private LimsSxtQuyangJihuaEntity findJihuaByFlow(String flowId, String taskId) {
        LambdaQueryWrapper<LimsSxtQuyangJihuaEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LimsSxtQuyangJihuaEntity::getFFlowId, flowId)
                .eq(LimsSxtQuyangJihuaEntity::getFFlowTaskId, taskId)
                .and(item -> item.isNull(LimsSxtQuyangJihuaEntity::getFDeleteMark)
                        .or().ne(LimsSxtQuyangJihuaEntity::getFDeleteMark, 1));
        List<LimsSxtQuyangJihuaEntity> list = jihuaMapper.selectList(wrapper);
        if (list.size() > 1) {
            throw new DataException("多个取样计划关联同一流程任务:task_id=" + taskId);
        }
        return list.isEmpty() ? null : list.get(0);
    }
 
    private LimsSxtQuyangJihuaZhongzhiEntity findZhongzhiByFlow(String flowId, String taskId) {
        LambdaQueryWrapper<LimsSxtQuyangJihuaZhongzhiEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LimsSxtQuyangJihuaZhongzhiEntity::getFFlowId, flowId)
                .eq(LimsSxtQuyangJihuaZhongzhiEntity::getFFlowTaskId, taskId);
        List<LimsSxtQuyangJihuaZhongzhiEntity> list = zhongzhiMapper.selectList(wrapper);
        if (list.size() > 1) {
            throw new DataException("多个计划终止申请关联同一流程任务:task_id=" + taskId);
        }
        return list.isEmpty() ? null : list.get(0);
    }
 
    private LimsSxtQuyangZhongzhiShenqingEntity findQuyangZhongzhiShenqingByFlow(String flowId, String taskId) {
        LambdaQueryWrapper<LimsSxtQuyangZhongzhiShenqingEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LimsSxtQuyangZhongzhiShenqingEntity::getFFlowId, flowId)
                .eq(LimsSxtQuyangZhongzhiShenqingEntity::getFFlowTaskId, taskId)
                .and(item -> item.isNull(LimsSxtQuyangZhongzhiShenqingEntity::getFDeleteMark)
                        .or().ne(LimsSxtQuyangZhongzhiShenqingEntity::getFDeleteMark, 1));
        List<LimsSxtQuyangZhongzhiShenqingEntity> list = quyangZhongzhiShenqingMapper.selectList(wrapper);
        if (list.size() > 1) {
            throw new DataException("多个取样终止申请关联同一流程任务:task_id=" + taskId);
        }
        return list.isEmpty() ? null : list.get(0);
    }
 
    private LimsSxtQuyangJihuaEntity findApprovedJihua(String jihuaId, String tenantId) {
        LambdaQueryWrapper<LimsSxtQuyangJihuaEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LimsSxtQuyangJihuaEntity::getId, jihuaId)
                .eq(LimsSxtQuyangJihuaEntity::getFFlowState, FLOW_STATE_APPROVED)
                .and(item -> item.isNull(LimsSxtQuyangJihuaEntity::getFDeleteMark)
                        .or().ne(LimsSxtQuyangJihuaEntity::getFDeleteMark, 1));
        if (StringUtils.hasText(tenantId)) {
            wrapper.eq(LimsSxtQuyangJihuaEntity::getFTenantId, tenantId);
        }
        List<LimsSxtQuyangJihuaEntity> list = jihuaMapper.selectList(wrapper);
        if (list.size() > 1) {
            throw new DataException("多个已审批取样计划使用相同主键:jihuaId=" + jihuaId);
        }
        return list.isEmpty() ? null : list.get(0);
    }
 
    private void ensureSingleBusiness(LimsSxtQuyangJihuaEntity jihua,
                                      LimsSxtQuyangJihuaZhongzhiEntity zhongzhi,
                                      LimsSxtQuyangZhongzhiShenqingEntity quyangZhongzhiShenqing,
                                      String flowId, String taskId) {
        int businessCount = (jihua == null ? 0 : 1)
                + (zhongzhi == null ? 0 : 1)
                + (quyangZhongzhiShenqing == null ? 0 : 1);
        if (businessCount == 0) {
            throw new DataException("未找到流程关联的取样计划、计划终止申请或取样终止申请:flow_id=" + flowId + ", task_id=" + taskId);
        }
        if (businessCount > 1) {
            throw new DataException("流程任务关联了多个水系统业务记录:task_id=" + taskId);
        }
    }
 
    private void validateFlowKey(String flowId, String taskId) {
        if (!StringUtils.hasText(flowId)) {
            throw new DataException("流程id(flow_id)不能为空");
        }
        if (!StringUtils.hasText(taskId)) {
            throw new DataException("流程任务id(task_id)不能为空");
        }
    }
 
}