package jnpf.limsService.impl; import jnpf.audit.sdk.annotation.AuditLog; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import jnpf.base.UserInfo; import jnpf.exception.DataException; import jnpf.limsEntity.JianyanXiangmuGuanliEntity; import jnpf.limsEntity.LimsQingyandanEntity; import jnpf.limsEntity.LimsSxtQingyanXiangmuEntity; import jnpf.limsEntity.LimsSxtQingyanXiangmuZixiangEntity; import jnpf.limsEntity.LimsSxtQuyangJihuaEntity; import jnpf.limsEntity.LimsSxtQuyangJihuaRenwuJianyanXiangKuaizhaoEntity; import jnpf.limsEntity.LimsSxtQuyangJihuaRenwuPeizhiKuaizhaoEntity; import jnpf.limsEntity.LimsSxtQuyangJihuaRenwuRenyuanKuaizhaoEntity; import jnpf.limsEntity.LimsSxtQuyangJihuaRenwuShijianEntity; import jnpf.limsEntity.LimsSxtQuyangJihuaRenwuEntity; import jnpf.limsEntity.LimsSxtQuyangJihuaZixiangEntity; import jnpf.limsEntity.LimsSxtQuyangdianEntity; import jnpf.limsEntity.LimsWaterSamplingMessageEvent; import jnpf.limsEntity.RenwuTihuanJieguo; import jnpf.limsEntity.RenwuTihuanPeizhiMingling; import jnpf.limsEntity.YangpinGuanliEntity; import jnpf.limsEntity.ZhiliangBiaozhunGuanliEntity; import jnpf.limsEntity.ZhiliangBiaozhunGuanliZixiangEntity; import jnpf.limsMapper.JianyanXiangmuGuanliMapper; import jnpf.limsMapper.LimsSxtQingyanXiangmuMapper; import jnpf.limsMapper.LimsSxtQingyanXiangmuZixiangMapper; import jnpf.limsMapper.LimsSxtQuyangJihuaMapper; import jnpf.limsMapper.LimsSxtQuyangJihuaRenwuJianyanXiangKuaizhaoMapper; import jnpf.limsMapper.LimsSxtQuyangJihuaRenwuMapper; import jnpf.limsMapper.LimsSxtQuyangJihuaRenwuPeizhiKuaizhaoMapper; import jnpf.limsMapper.LimsSxtQuyangJihuaRenwuRenyuanKuaizhaoMapper; import jnpf.limsMapper.LimsSxtQuyangJihuaRenwuShijianMapper; import jnpf.limsMapper.LimsSxtQuyangJihuaZixiangMapper; import jnpf.limsMapper.LimsSxtQuyangdianMapper; import jnpf.limsMapper.YangpinGuanliMapper; import jnpf.limsMapper.ZhiliangBiaozhunGuanliMapper; import jnpf.limsModel.LimsSxtQuyangdianKuaizhaoModel; import jnpf.limsService.LimsQingyandanService; import jnpf.limsService.LimsSxtQuyangJihuaRenwuService; import jnpf.limsService.ZhiliangBiaozhunGuanliZixiangService; import jnpf.limsService.support.LimsSxtQuyangJihuaRenwuPlanner; import jnpf.limsService.support.LimsSxtPlanStatusResolver; import jnpf.limsService.support.LimsSxtQuyangJihuaRenwuStatus; import jnpf.util.RandomUtil; import jnpf.util.UserProvider; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.time.LocalDate; import java.time.ZoneId; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; /** * 新 Renwu 仅使用冻结快照生成请验单;不读写旧 Task 表。 */ @Slf4j @Service public class LimsSxtQuyangJihuaRenwuServiceImpl implements LimsSxtQuyangJihuaRenwuService { private static final Integer FLOW_STATE_APPROVED = 2; private static final String SXT_BIZ_TYPE = "SXT"; @Autowired private LimsSxtQuyangJihuaMapper jihuaMapper; @Autowired private LimsSxtQuyangJihuaZixiangMapper jihuaZixiangMapper; @Autowired private LimsSxtQuyangJihuaRenwuMapper renwuMapper; @Autowired private LimsSxtQuyangJihuaRenwuPeizhiKuaizhaoMapper peizhiKuaizhaoMapper; @Autowired private LimsSxtQuyangJihuaRenwuJianyanXiangKuaizhaoMapper jianyanKuaizhaoMapper; @Autowired private LimsSxtQuyangJihuaRenwuRenyuanKuaizhaoMapper renyuanKuaizhaoMapper; @Autowired private LimsSxtQuyangJihuaRenwuShijianMapper shijianMapper; @Autowired private LimsSxtQuyangdianMapper quyangdianMapper; @Autowired private YangpinGuanliMapper yangpinGuanliMapper; @Autowired private ZhiliangBiaozhunGuanliMapper zhiliangBiaozhunMapper; @Autowired private ZhiliangBiaozhunGuanliZixiangService zhiliangBiaozhunZixiangService; @Autowired private LimsSxtQingyanXiangmuMapper qingyanPeizhiMapper; @Autowired private LimsSxtQingyanXiangmuZixiangMapper qingyanPeizhiZixiangMapper; @Autowired private JianyanXiangmuGuanliMapper jianyanXiangmuMapper; @Autowired private LimsQingyandanService qingyandanService; @Autowired private ApplicationEventPublisher eventPublisher; @Scheduled(cron = "${lims.sxt.jihua.cron:0 0 1 * * ?}") public void dingShiShengchengQingyandan() { try { int shuliang = shengchengDaishengchengQingyandan(LocalDate.now()); if (shuliang > 0) { log.info("水系统Renwu到期生成请验单完成,数量={}", shuliang); } } catch (Exception e) { log.error("水系统Renwu到期生成请验单失败", e); } } @Scheduled(cron = "${lims.sxt.jihua.snapshot-compensation-cron:0 5/15 * * * ?}") public void dingShiBuchangRenwu() { try { buchangYishenpiJihuaRenwu(); } catch (Exception e) { log.error("水系统Renwu快照补偿失败", e); } } @Override @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) public int wuhuaYishenpiJihua(String jihuaId, String tenantId) { LimsSxtQuyangJihuaEntity jihua = chaxunYishenpiKeZhixingJihua(jihuaId, tenantId); return wuhuaYishenpiJihuaCong(jihuaId, toLocalDate(jihua.getKaishiShijian(), "计划开始时间不能为空"), tenantId); } @Override @Transactional(rollbackFor = Exception.class) public int wuhuaYishenpiJihuaCong(String jihuaId, LocalDate qishiRiqi, String tenantId) { if (qishiRiqi == null) { throw new DataException("任务生成起始日期不能为空"); } tenantId = normalizeTenantId(tenantId); LimsSxtQuyangJihuaEntity jihua = chaxunYishenpiKeZhixingJihua(jihuaId, tenantId); LocalDate kaishi = toLocalDate(jihua.getKaishiShijian(), "计划开始时间不能为空"); LocalDate jieshu = toLocalDate(jihua.getJieshuShijian(), "计划结束时间不能为空"); LocalDate youxiaoQishi = qishiRiqi.isBefore(kaishi) ? kaishi : qishiRiqi; List zixiangList = chaxunYouxiaoJihuaZixiang(jihua.getId(), tenantId); int chenggongShuliang = 0; for (LocalDate jihuaRiqi : executionDates(kaishi, jieshu, jihua.getJihuaZhouqiLeixing(), parseInterval(jihua.getJihuaZhouqiJiange()))) { if (jihuaRiqi.isBefore(youxiaoQishi)) { continue; } for (LimsSxtQuyangJihuaZixiangEntity zixiang : zixiangList) { chenggongShuliang += wuhuaDanTiaoRenwu(jihua, zixiang, jihuaRiqi, tenantId); } } return chenggongShuliang; } @Override @Transactional(rollbackFor = Exception.class) public int shengchengDaishengchengQingyandan(LocalDate jihuaRiqi) { requireJihuaRiqi(jihuaRiqi); return shengchengYisuodingRenwu(renwuMapper.lockPendingRenwu(jihuaRiqi)); } @Override @Transactional(rollbackFor = Exception.class) public int shengchengDaishengchengQingyandanByJihua(String jihuaId, String tenantId, LocalDate jihuaRiqi) { requireText(jihuaId, "取样计划ID不能为空"); requireJihuaRiqi(jihuaRiqi); return shengchengYisuodingRenwu(renwuMapper.lockPendingRenwuForJihua( jihuaId, normalizeTenantId(tenantId), jihuaRiqi)); } @Override @AuditLog(action = "PLAN_COMPENSATE", label = "补偿水系统已审批计划任务", bizType = "SXT", bizCodeExpr = "#result > 0 ? 'SXT_PLAN_COMPENSATE' : ''", targetTable = "lims_sxt_quyang_jihua_renwu") public int buchangYishenpiJihuaRenwu() { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("f_flow_state", FLOW_STATE_APPROVED) .eq("plan_status", LimsSxtPlanStatusResolver.AVAILABLE) .and(item -> item.eq("f_delete_mark", 0).or().isNull("f_delete_mark")); int chenggongShuliang = 0; for (LimsSxtQuyangJihuaEntity jihua : jihuaMapper.selectList(wrapper)) { try { chenggongShuliang += wuhuaYishenpiJihua(jihua.getId(), jihua.getFTenantId()); shengchengDaishengchengQingyandanByJihua(jihua.getId(), jihua.getFTenantId(), LocalDate.now()); } catch (Exception e) { log.error("水系统Renwu补偿失败,jihuaId={}, tenantId={}", jihua.getId(), jihua.getFTenantId(), e); } } return chenggongShuliang; } @Override @Transactional(rollbackFor = Exception.class) public int quxiaoWeishengchengRenwuByJihua(String jihuaId, String tenantId, String yuanyinDaima) { requireText(jihuaId, "取样计划ID不能为空"); List renwuList = renwuMapper.lockWeishengchengRenwuByJihua( jihuaId, normalizeTenantId(tenantId)); int shuliang = 0; for (LimsSxtQuyangJihuaRenwuEntity renwu : renwuList) { if (quxiaoRenwu(renwu, yuanyinDaima)) { shuliang++; } } return shuliang; } @Override @Transactional(rollbackFor = Exception.class) public void quxiaoWeishengchengWeilaiRenwu(String renwuId, String tenantId, String yuanyinDaima) { requireText(renwuId, "任务ID不能为空"); LimsSxtQuyangJihuaRenwuEntity renwu = renwuMapper.lockRenwuForQuxiao(renwuId, normalizeTenantId(tenantId)); if (renwu == null) { throw new DataException("计划任务不存在"); } LocalDate jihuaRiqi = toLocalDate(renwu.getJihuaRiqi(), "任务计划日期不能为空"); if (!jihuaRiqi.isAfter(LocalDate.now())) { throw new DataException("仅未来未生成请验单的任务允许取消"); } if (!quxiaoRenwu(renwu, yuanyinDaima)) { throw new DataException("任务状态已变化,取消失败"); } } @Override public RenwuTihuanJieguo tihuanWeishengchengRenwuPeizhi(String renwuId, RenwuTihuanPeizhiMingling mingling) { throw new DataException("受控配置替代功能未启用"); } private int wuhuaDanTiaoRenwu(LimsSxtQuyangJihuaEntity jihua, LimsSxtQuyangJihuaZixiangEntity zixiang, LocalDate jihuaRiqi, String tenantId) { LimsSxtQuyangdianKuaizhaoModel quyangdian = chaxunWeiyiYouxiaoQuyangdian(jihua, zixiang, tenantId); YangpinGuanliEntity yangpin = chaxunYouxiaoYangpin(jihua.getYangpinLeixing(), tenantId); ZhiliangBiaozhunGuanliEntity biaozhun = chaxunYouxiaoBiaozhun(zixiang.getZhiliangBiaozhun(), tenantId, yangpin.getId()); LimsSxtQingyanXiangmuEntity qingyanPeizhi = chaxunYouxiaoQingyanPeizhi( zixiang.getJianyanXiangmu(), tenantId, yangpin.getId(), biaozhun.getId()); List peizhiZixiang = chaxunYouxiaoPeizhiZixiang(qingyanPeizhi.getId(), tenantId); Map jianyanXiangmuMap = chaxunYouxiaoJianyanXiangmuMap(peizhiZixiang, tenantId); List jianyanKuaizhao = buildJianyanKuaizhao( tenantId, qingyanPeizhi, peizhiZixiang, biaozhun, jianyanXiangmuMap); if (jianyanKuaizhao.isEmpty()) { throw new DataException("请验配置与质量标准没有匹配的检验项目"); } Date now = new Date(); LimsSxtQuyangJihuaRenwuEntity renwu = buildRenwu(jihua, zixiang, jihuaRiqi, quyangdian, now); renwu.setKuaizhaoHaxi(haxi(JSON.toJSONString(jihua) + JSON.toJSONString(zixiang) + JSON.toJSONString(quyangdian) + JSON.toJSONString(yangpin) + JSON.toJSONString(biaozhun) + JSON.toJSONString(jianyanXiangmuMap.values()) + JSON.toJSONString(jianyanKuaizhao))); if (renwuMapper.insertIgnoreUnique(renwu) != 1) { return 0; } baocunPeizhiKuaizhao(renwu, "quyangdian", quyangdian.getId(), null, quyangdian, now); baocunPeizhiKuaizhao(renwu, "yangpin", yangpin.getId(), null, yangpin, now); baocunPeizhiKuaizhao(renwu, "zhiliang_biaozhun", biaozhun.getId(), biaozhun.getBiaozhunBanben(), biaozhun, now); baocunPeizhiKuaizhao(renwu, "qingyan_peizhi", qingyanPeizhi.getId(), null, qingyanPeizhi, now); for (LimsSxtQingyanXiangmuZixiangEntity item : peizhiZixiang) { baocunPeizhiKuaizhao(renwu, "qingyan_peizhi_zixiang", item.getId(), null, item, now); } for (JianyanXiangmuGuanliEntity xiangmu : jianyanXiangmuMap.values()) { baocunPeizhiKuaizhao(renwu, "jianyan_xiangmu", xiangmu.getId(), null, xiangmu, now); } baocunRenyuanKuaizhao(renwu, "qingyan_shenqingren", jihua.getZhidingren(), null, now); if (hasText(quyangdian.getFuzeGangwei())) { baocunRenyuanKuaizhao(renwu, "quyang_fuze_gangwei", null, quyangdian.getFuzeGangwei(), now); } for (LimsSxtQuyangJihuaRenwuJianyanXiangKuaizhaoEntity item : jianyanKuaizhao) { item.setRenwuId(renwu.getId()); item.setFCreatorUserId(renwu.getFCreatorUserId()); jianyanKuaizhaoMapper.insert(item); } baocunShijian(renwu, "renwu_chuangjian", null, null, null, null); return 1; } private int shengchengYisuodingRenwu(List renwuList) { int chenggongShuliang = 0; for (LimsSxtQuyangJihuaRenwuEntity renwu : renwuList) { if (renwuMapper.markGenerating(renwu.getId()) != 1) { continue; } try { boolean chayi = jiaoyanDangqianPeizhi(renwu); List jianyanKuaizhao = chaxunJianyanKuaizhao(renwu.getId(), renwu.getFTenantId()); LimsQingyandanEntity qingyandan = qingyandanService.sxtRenwuKuaizhaoAutoSubmit( buildQingyandanFromRenwuKuaizhao(renwu), jianyanKuaizhao); if (renwuMapper.markGenerated(renwu.getId(), qingyandan.getId(), qingyandan.getJianyanLiushuihao(), qingyandan.getBizStatus()) != 1) { throw new DataException("任务状态已变化,不能标记已生成:" + renwu.getId()); } baocunShijian(renwu, "qingyandan_chuangjian", null, null, qingyandan.getId(), null); eventPublisher.publishEvent(new LimsWaterSamplingMessageEvent( renwu.getFTenantId(), renwu.getId(), qingyandan.getId(), qingyandan.getJianyanLiushuihao(), renwu.getFuzeGangwei(), renwu.getJihuaMingcheng(), renwu.getYangpinMingcheng(), qingyandan.getQuyangDidian(), renwu.getJihuaRiqi())); if (chayi) { baocunShijian(renwu, "peizhi_chayi_faxian", "config_changed", "当前配置与冻结快照不同", null, null); } chenggongShuliang++; } catch (PeizhiWuxiaoException e) { if (renwuMapper.markBlockedPeizhiWuxiao(renwu.getId(), e.yuanyinDaima, e.getMessage()) == 1) { baocunShijian(renwu, "peizhi_wuxiao_zuduan", e.yuanyinDaima, e.getMessage(), null, null); } } catch (Exception e) { renwuMapper.resetPending(renwu.getId()); throw e; } } return chenggongShuliang; } private boolean jiaoyanDangqianPeizhi(LimsSxtQuyangJihuaRenwuEntity renwu) { boolean chayi = false; LimsSxtQuyangdianEntity quyangdian = quyangdianMapper.selectById(renwu.getQuyangdianId()); if (quyangdian == null || isDeleted(quyangdian.getDeleteMark()) || !"enabled".equals(quyangdian.getBizEnabled())) { throw new PeizhiWuxiaoException("config_disabled", "取样点已删除或停用"); } YangpinGuanliEntity yangpin = yangpinGuanliMapper.selectById(renwu.getYangpinLeixing()); if (!isYouxiaoYangpin(yangpin, renwu.getFTenantId())) { throw new PeizhiWuxiaoException("config_disabled", "样品已删除或停用"); } ZhiliangBiaozhunGuanliEntity biaozhun = zhiliangBiaozhunMapper.selectById(renwu.getZhiliangBiaozhun()); if (!isYouxiaoBiaozhun(biaozhun, renwu.getFTenantId(), renwu.getYangpinLeixing())) { throw new PeizhiWuxiaoException("config_disabled", "质量标准已删除、停用或与样品不匹配"); } LimsSxtQingyanXiangmuEntity peizhi = qingyanPeizhiMapper.selectById(renwu.getJianyanXiangmu()); if (!isYouxiaoPeizhi(peizhi, renwu.getFTenantId(), renwu.getYangpinLeixing(), renwu.getZhiliangBiaozhun())) { throw new PeizhiWuxiaoException("config_soft_deleted", "请验配置已删除或与任务样品、质量标准不匹配"); } for (LimsSxtQuyangJihuaRenwuJianyanXiangKuaizhaoEntity item : chaxunJianyanKuaizhao(renwu.getId(), renwu.getFTenantId())) { LimsSxtQingyanXiangmuZixiangEntity peizhiZixiang = qingyanPeizhiZixiangMapper.selectById(item.getLaiyuanQingyanPeizhiZixiangId()); ZhiliangBiaozhunGuanliZixiangEntity biaozhunZixiang = zhiliangBiaozhunZixiangService.getById(item.getLaiyuanZhiliangBiaozhunZixiangId()); JianyanXiangmuGuanliEntity jianyanXiangmu = jianyanXiangmuMapper.selectById(item.getJianyanXiangmuId()); if (!isYouxiaoPeizhiZixiang(peizhiZixiang, renwu.getFTenantId()) || !isYouxiaoBiaozhunZixiang(biaozhunZixiang, renwu.getFTenantId()) || !isYouxiaoJianyanXiangmu(jianyanXiangmu, renwu.getFTenantId())) { throw new PeizhiWuxiaoException("config_soft_deleted", "检验项目、请验配置子项或质量标准子项已删除"); } } for (LimsSxtQuyangJihuaRenwuPeizhiKuaizhaoEntity kuaizhao : chaxunPeizhiKuaizhao(renwu.getId(), renwu.getFTenantId())) { Object dangqian = dangqianPeizhi( kuaizhao.getPeizhiLeixing(), kuaizhao.getLaiyuanPeizhiId(), renwu.getFTenantId()); if (dangqian != null && !peizhiKuaizhaoYizhi(kuaizhao, dangqian)) { chayi = true; } } LambdaUpdateWrapper update = new LambdaUpdateWrapper<>(); update.eq(LimsSxtQuyangJihuaRenwuEntity::getId, renwu.getId()) .set(LimsSxtQuyangJihuaRenwuEntity::getPeizhiJiaoyanShijian, new Date()) .set(LimsSxtQuyangJihuaRenwuEntity::getPeizhiJiaoyanZhuangtai, chayi ? "drift_detected" : "valid") .set(LimsSxtQuyangJihuaRenwuEntity::getPeizhiChayiBiaozhi, chayi ? 1 : 0); renwuMapper.update(null, update); return chayi; } //配置快照一致 private boolean peizhiKuaizhaoYizhi(LimsSxtQuyangJihuaRenwuPeizhiKuaizhaoEntity kuaizhao, Object dangqian) { if ("quyangdian".equals(kuaizhao.getPeizhiLeixing())) { LimsSxtQuyangdianKuaizhaoModel dongjie = JSON.parseObject( kuaizhao.getKuaizhaoShuju(), LimsSxtQuyangdianKuaizhaoModel.class); return haxi(JSON.toJSONString(dongjie)).equals(haxi(JSON.toJSONString(dangqian))); } return haxi(JSON.toJSONString(dangqian)).equals(kuaizhao.getKuaizhaoHaxi()); } //根据类型查询当前配置 private Object dangqianPeizhi(String leixing, String id, String tenantId) { if ("quyangdian".equals(leixing)) return quyangdianMapper.selectForTaskSnapshot(tenantId, id); if ("yangpin".equals(leixing)) return yangpinGuanliMapper.selectById(id); if ("zhiliang_biaozhun".equals(leixing)) return zhiliangBiaozhunMapper.selectById(id); if ("qingyan_peizhi".equals(leixing)) return qingyanPeizhiMapper.selectById(id); if ("qingyan_peizhi_zixiang".equals(leixing)) return qingyanPeizhiZixiangMapper.selectById(id); if ("jianyan_xiangmu".equals(leixing)) return jianyanXiangmuMapper.selectById(id); return null; } //从任务快照生成请验单 private LimsQingyandanEntity buildQingyandanFromRenwuKuaizhao(LimsSxtQuyangJihuaRenwuEntity renwu) { YangpinGuanliEntity yangpin = JSON.parseObject(chaxunPeizhiKuaizhaoShuju(renwu, "yangpin"), YangpinGuanliEntity.class); ZhiliangBiaozhunGuanliEntity biaozhun = JSON.parseObject(chaxunPeizhiKuaizhaoShuju(renwu, "zhiliang_biaozhun"), ZhiliangBiaozhunGuanliEntity.class); LimsQingyandanEntity qingyandan = new LimsQingyandanEntity(); qingyandan.setTenantId(renwu.getFTenantId()); qingyandan.setLaiyuanRenwuId(renwu.getId()); qingyandan.setRenwuKuaizhaoHaxi(renwu.getKuaizhaoHaxi()); qingyandan.setChanpinId(renwu.getYangpinLeixing()); qingyandan.setChanpinMingcheng(yangpin.getMingcheng()); qingyandan.setChanpinBianma(yangpin.getBianma()); qingyandan.setGuige(yangpin.getGuige()); qingyandan.setBaozhuangGuige(yangpin.getBaozhuangGuige()); qingyandan.setChucunTiaojian(yangpin.getCucunTiaojian()); qingyandan.setWeiyiShibiemaEnabled(yangpin.getWeiyiShibiemaEnabled()); qingyandan.setJianyanBiaozhun(renwu.getZhiliangBiaozhun()); qingyandan.setJianyanLiang(hasText(renwu.getQuyangliang()) ? renwu.getQuyangliang() : biaozhun.getJianceLiang()); qingyandan.setQuyangDanwei(hasText(renwu.getQuyangliangDanwei()) ? renwu.getQuyangliangDanwei() : biaozhun.getQuyangDanwei()); qingyandan.setJianceLiuyangLiang(biaozhun.getJianceLiuyangLiang()); qingyandan.setLiuyangLiang(biaozhun.getLiuyangLiang()); qingyandan.setLiuyangDanwei(biaozhun.getLiuyangKaochaDanwei()); qingyandan.setShifouShixiaoxingYaoqiu(biaozhun.getShifouShixiaoxingYaoqiu()); qingyandan.setBizType(SXT_BIZ_TYPE); qingyandan.setJianceFangshi("quanjian"); qingyandan.setQuyangDidian(renwu.getQuyangdianBianhao() + "-" + renwu.getSuoshuChejian() + "-" + renwu.getQuyangDidian()); qingyandan.setQingyanRen(renwu.getZhidingren()); qingyandan.setQingyanRiqi(renwu.getJihuaRiqi()); qingyandan.setCreatorUserId(renwu.getZhidingren()); List biaozhunZixiangIds = new ArrayList<>(); for (LimsSxtQuyangJihuaRenwuJianyanXiangKuaizhaoEntity item : chaxunJianyanKuaizhao(renwu.getId(), renwu.getFTenantId())) { biaozhunZixiangIds.add(item.getLaiyuanZhiliangBiaozhunZixiangId()); } qingyandan.setJianceXiangmu(JSON.toJSONString(biaozhunZixiangIds)); return qingyandan; } private LimsSxtQuyangJihuaRenwuEntity buildRenwu(LimsSxtQuyangJihuaEntity jihua, LimsSxtQuyangJihuaZixiangEntity zixiang, LocalDate jihuaRiqi, LimsSxtQuyangdianKuaizhaoModel quyangdian, Date now) { LimsSxtQuyangJihuaRenwuEntity renwu = new LimsSxtQuyangJihuaRenwuEntity(); renwu.setId(RandomUtil.uuId()); renwu.setFTenantId(normalizeTenantId(jihua.getFTenantId())); renwu.setJihuaId(jihua.getId()); renwu.setJihuaZixiangId(zixiang.getId()); renwu.setJihuaRiqi(Date.from(jihuaRiqi.atStartOfDay(ZoneId.systemDefault()).toInstant())); renwu.setRenwuZhuangtai(LimsSxtQuyangJihuaRenwuStatus.PENDING); renwu.setJihuaMingcheng(jihua.getJihuaMingcheng()); renwu.setYangpinLeixing(jihua.getYangpinLeixing()); renwu.setYangpinLeixingMingcheng(jihua.getYangpinLeixingMingcheng()); renwu.setYangpinMingcheng(jihua.getYangpinMingcheng()); renwu.setSuoshuChejian(quyangdian.getChejianMingchengName()); renwu.setSuoshuChejianId(quyangdian.getChejianMingcheng()); renwu.setQuyangDidian(zixiang.getQuyangDidian()); renwu.setQuyangdianBianhao(zixiang.getQuyangdianBianhao()); renwu.setFuzeGangwei(quyangdian.getFuzeGangwei()); renwu.setQuyangliang(zixiang.getQuyangliang()); renwu.setQuyangliangDanwei(zixiang.getQuyangliangDanwei()); renwu.setZhiliangBiaozhun(zixiang.getZhiliangBiaozhun()); renwu.setJianyanXiangmu(zixiang.getJianyanXiangmu()); renwu.setJianyanXiangmuMingcheng(zixiang.getJianyanXiangmuMingcheng()); renwu.setZhidingren(jihua.getZhidingren()); renwu.setFDeleteMark(0); renwu.setFCreatorTime(now); renwu.setFCreatorUserId(jihua.getFCreatorUserId()); renwu.setRenwuShiliId(UUID.nameUUIDFromBytes((renwu.getFTenantId() + "|" + jihua.getId() + "|" + zixiang.getId() + "|" + jihuaRiqi).getBytes(StandardCharsets.UTF_8)).toString()); renwu.setRenwuXiudingXuhao(1); renwu.setQuyangdianId(quyangdian.getId()); renwu.setKuaizhaoJiegouBanben("1.0"); renwu.setKuaizhaoShengchengShijian(now); renwu.setPeizhiChayiBiaozhi(0); renwu.setZhuangtaiBiangengShijian(now); return renwu; } private List buildJianyanKuaizhao( String tenantId, LimsSxtQingyanXiangmuEntity peizhi, List peizhiZixiang, ZhiliangBiaozhunGuanliEntity biaozhun, Map xiangmuMap) { Map biaozhunMap = new HashMap<>(); for (ZhiliangBiaozhunGuanliZixiangEntity item : zhiliangBiaozhunZixiangService.listByForeignId(biaozhun.getId())) { if (isYouxiaoBiaozhunZixiang(item, tenantId)) biaozhunMap.put(item.getJianyanXiangmuBianma(), item); } List result = new ArrayList<>(); for (LimsSxtQingyanXiangmuZixiangEntity peizhiItem : peizhiZixiang) { JianyanXiangmuGuanliEntity xiangmu = xiangmuMap.get(peizhiItem.getXiangmuId()); ZhiliangBiaozhunGuanliZixiangEntity biaozhunItem = biaozhunMap.get(xiangmu.getBianma()); if (biaozhunItem == null) { throw new DataException("请验配置检验项目未维护质量标准:" + xiangmu.getBianma()); } result.add(buildJianyanKuaizhaoItem(tenantId, peizhi, peizhiItem, biaozhun, xiangmu, biaozhunItem)); } return result; } private Map chaxunYouxiaoJianyanXiangmuMap( List peizhiZixiang, String tenantId) { Set xiangmuBianhaoSet = new LinkedHashSet<>(); for (LimsSxtQingyanXiangmuZixiangEntity item : peizhiZixiang) { if (!hasText(item.getXiangmuId())) { throw new DataException("请验配置子项未维护质量标准子项ID:" + item.getId()); } if (!hasText(item.getXiangmuBianhao())) { throw new DataException("请验配置子项未维护检验项目编码:" + item.getId()); } xiangmuBianhaoSet.add(item.getXiangmuBianhao()); } QueryWrapper wrapper = new QueryWrapper<>(); wrapper.in("bianma", xiangmuBianhaoSet) .eq("f_tenant_id", tenantId) .and(item -> item.eq("f_delete_mark", 0) .or() .isNull("f_delete_mark")); Map anBianmaChaxunJieguo = new HashMap<>(); for (JianyanXiangmuGuanliEntity item : jianyanXiangmuMapper.selectList(wrapper)) { anBianmaChaxunJieguo.put(item.getBianma(), item); } Map xiangmuMap = new LinkedHashMap<>(); for (LimsSxtQingyanXiangmuZixiangEntity item : peizhiZixiang) { JianyanXiangmuGuanliEntity xiangmu = anBianmaChaxunJieguo.get(item.getXiangmuBianhao()); if (xiangmu == null) { throw new DataException( "请验配置引用的检验项目不存在或已删除,项目编码:" + item.getXiangmuBianhao()); } // key 仍使用质量标准子项 ID,兼容后续快照构建逻辑 xiangmuMap.put(item.getXiangmuId(), xiangmu); } return xiangmuMap; } private LimsSxtQuyangJihuaRenwuJianyanXiangKuaizhaoEntity buildJianyanKuaizhaoItem( String tenantId, LimsSxtQingyanXiangmuEntity peizhi, LimsSxtQingyanXiangmuZixiangEntity peizhiItem, ZhiliangBiaozhunGuanliEntity biaozhun, JianyanXiangmuGuanliEntity xiangmu, ZhiliangBiaozhunGuanliZixiangEntity biaozhunItem) { LimsSxtQuyangJihuaRenwuJianyanXiangKuaizhaoEntity kuaizhao = new LimsSxtQuyangJihuaRenwuJianyanXiangKuaizhaoEntity(); kuaizhao.setId(RandomUtil.uuId()); kuaizhao.setFTenantId(tenantId); kuaizhao.setLaiyuanQingyanPeizhiId(peizhi.getId()); kuaizhao.setLaiyuanQingyanPeizhiZixiangId(peizhiItem.getId()); kuaizhao.setLaiyuanZhiliangBiaozhunId(biaozhun.getId()); kuaizhao.setLaiyuanZhiliangBiaozhunZixiangId(biaozhunItem.getId()); kuaizhao.setJianyanXiangmuId(xiangmu.getId()); kuaizhao.setJianyanXiangmuBianma(biaozhunItem.getJianyanXiangmuBianma()); kuaizhao.setJianyanXiangmuMingcheng(biaozhunItem.getJianyanXiangmuMingcheng()); kuaizhao.setJianyanXiangmuShifouFenxileixing(xiangmu.getShifouFenxileixing()); kuaizhao.setJianyanXiangmuFenxiLeixing(xiangmu.getFenxiLeixing()); kuaizhao.setJianyanXiangmuJieguoLeixing(xiangmu.getJieguoLeixing()); kuaizhao.setJianyanXiangmuMorenDanwei(xiangmu.getMorenDanwei()); kuaizhao.setLilunShangxian(xiangmu.getLilunShangxian()); kuaizhao.setLilunXiaxian(xiangmu.getLilunXiaxian()); kuaizhao.setJianyanXiangmuBeizhu(xiangmu.getNote()); kuaizhao.setBiaozhunBianhao(biaozhun.getBiaozhunBianhao()); kuaizhao.setBiaozhunBanben(biaozhun.getBiaozhunBanben()); kuaizhao.setBiaozhunGuiding(biaozhunItem.getBiaozhunGuiding()); kuaizhao.setBiaozhunShangxian(biaozhunItem.getBiaozhunShangxian()); kuaizhao.setBiaozhunXiaxian(biaozhunItem.getBiaozhunXiaxian()); kuaizhao.setJianyanDanwei(biaozhunItem.getJianyanDanwei()); kuaizhao.setShujuLeixing(biaozhunItem.getShujuLeixing()); kuaizhao.setFenxiLeixing(biaozhunItem.getFenxiLeixing()); kuaizhao.setJianyanLiang(biaozhunItem.getJianyanLiang()); kuaizhao.setJianchaBanzu(biaozhunItem.getJianchaBanzu()); kuaizhao.setKuaizhaoHaxi(haxi(JSON.toJSONString(kuaizhao))); kuaizhao.setFDeleteMark(0); kuaizhao.setFCreatorTime(new Date()); return kuaizhao; } private void baocunPeizhiKuaizhao(LimsSxtQuyangJihuaRenwuEntity renwu, String leixing, String laiyuanId, String banben, Object shuju, Date now) { String json = JSON.toJSONString(shuju); LimsSxtQuyangJihuaRenwuPeizhiKuaizhaoEntity kuaizhao = new LimsSxtQuyangJihuaRenwuPeizhiKuaizhaoEntity(); kuaizhao.setId(RandomUtil.uuId()); kuaizhao.setFTenantId(renwu.getFTenantId()); kuaizhao.setRenwuId(renwu.getId()); kuaizhao.setPeizhiLeixing(leixing); kuaizhao.setLaiyuanPeizhiId(laiyuanId); kuaizhao.setLaiyuanPeizhiBanben(banben); kuaizhao.setKuaizhaoShuju(json); kuaizhao.setKuaizhaoHaxi(haxi(json)); kuaizhao.setFCreatorTime(now); kuaizhao.setFCreatorUserId(renwu.getFCreatorUserId()); peizhiKuaizhaoMapper.insertKuaizhao(kuaizhao); } private void baocunRenyuanKuaizhao(LimsSxtQuyangJihuaRenwuEntity renwu, String guanxiLeixing, String yonghuId, String gangweiId, Date now) { LimsSxtQuyangJihuaRenwuRenyuanKuaizhaoEntity kuaizhao = new LimsSxtQuyangJihuaRenwuRenyuanKuaizhaoEntity(); kuaizhao.setId(RandomUtil.uuId()); kuaizhao.setFTenantId(renwu.getFTenantId()); kuaizhao.setRenwuId(renwu.getId()); kuaizhao.setGuanxiLeixing(guanxiLeixing); kuaizhao.setYonghuId(yonghuId); kuaizhao.setGangweiId(gangweiId); kuaizhao.setKuaizhaoHaxi(haxi(guanxiLeixing + "|" + yonghuId + "|" + gangweiId)); kuaizhao.setFCreatorTime(now); kuaizhao.setFCreatorUserId(renwu.getFCreatorUserId()); renyuanKuaizhaoMapper.insert(kuaizhao); } private void baocunShijian(LimsSxtQuyangJihuaRenwuEntity renwu, String leixing, String yuanyinDaima, String yuanyin, String qingyandanId, String shouquanId) { LimsSxtQuyangJihuaRenwuShijianEntity shijian = new LimsSxtQuyangJihuaRenwuShijianEntity(); shijian.setId(RandomUtil.uuId()); shijian.setFTenantId(renwu.getFTenantId()); shijian.setRenwuId(renwu.getId()); shijian.setRenwuShiliId(renwu.getRenwuShiliId()); shijian.setShijianLeixing(leixing); shijian.setFashengShijian(new Date()); UserInfo user = UserProvider.getUser(); shijian.setCaozuoRenId(user == null ? null : user.getUserId()); shijian.setCaozuoLeixing(user == null ? "system" : "user"); shijian.setYuanyinDaima(yuanyinDaima); shijian.setYuanyin(yuanyin); shijian.setGuanlianQingyandanId(qingyandanId); shijian.setXinKuaizhaoHaxi(renwu.getKuaizhaoHaxi()); shijian.setShouquanId(shouquanId); shijian.setShijianShuju("{}"); shijianMapper.insertShijian(shijian); } private boolean quxiaoRenwu(LimsSxtQuyangJihuaRenwuEntity renwu, String yuanyinDaima) { UserInfo user = UserProvider.getUser(); int updated = renwuMapper.quxiaoDangeRenwu(renwu.getId(), user == null ? "0" : user.getUserId(), yuanyinDaima); if (updated == 1) { baocunShijian(renwu, "renwu_quxiao", yuanyinDaima, null, null, null); return true; } return false; } private List chaxunJianyanKuaizhao(String renwuId, String tenantId) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("renwu_id", renwuId).eq("f_tenant_id", tenantId) .and(item -> item.eq("f_delete_mark", 0).or().isNull("f_delete_mark")); return jianyanKuaizhaoMapper.selectList(wrapper); } private List chaxunPeizhiKuaizhao(String renwuId, String tenantId) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("renwu_id", renwuId).eq("f_tenant_id", tenantId); return peizhiKuaizhaoMapper.selectList(wrapper); } private String chaxunPeizhiKuaizhaoShuju(LimsSxtQuyangJihuaRenwuEntity renwu, String leixing) { for (LimsSxtQuyangJihuaRenwuPeizhiKuaizhaoEntity kuaizhao : chaxunPeizhiKuaizhao(renwu.getId(), renwu.getFTenantId())) { if (leixing.equals(kuaizhao.getPeizhiLeixing())) return kuaizhao.getKuaizhaoShuju(); } throw new DataException("任务缺少" + leixing + "配置快照:" + renwu.getId()); } private LimsSxtQuyangJihuaEntity chaxunYishenpiKeZhixingJihua(String jihuaId, String tenantId) { requireText(jihuaId, "取样计划ID不能为空"); QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("f_id", jihuaId).eq("f_tenant_id", normalizeTenantId(tenantId)) .eq("f_flow_state", FLOW_STATE_APPROVED).eq("plan_status", LimsSxtPlanStatusResolver.AVAILABLE) .and(item -> item.eq("f_delete_mark", 0).or().isNull("f_delete_mark")); LimsSxtQuyangJihuaEntity jihua = jihuaMapper.selectOne(wrapper); if (jihua == null) throw new DataException("取样计划不存在、未审批通过或不可执行"); return jihua; } private List chaxunYouxiaoJihuaZixiang(String jihuaId, String tenantId) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("f_foreign_id", jihuaId).eq("f_tenant_id", tenantId) .and(item -> item.eq("f_delete_mark", 0).or().isNull("f_delete_mark")); List list = jihuaZixiangMapper.selectList(wrapper); if (list.isEmpty()) throw new DataException("取样计划未配置有效子项"); return list; } private LimsSxtQuyangdianKuaizhaoModel chaxunWeiyiYouxiaoQuyangdian( LimsSxtQuyangJihuaEntity jihua, LimsSxtQuyangJihuaZixiangEntity zixiang, String tenantId) { if (!hasText(zixiang.getQuyangdianId())) { throw new DataException("取样计划子项未保存取样点ID:" + zixiang.getId()); } LimsSxtQuyangdianKuaizhaoModel quyangdian = quyangdianMapper.selectForTaskSnapshot( tenantId, zixiang.getQuyangdianId()); if (quyangdian == null) { throw new DataException("水系统取样点不存在、已删除或已停用:" + zixiang.getQuyangdianId()); } if (!Objects.equals(jihua.getYangpinLeixing(), quyangdian.getLeiXing())) { throw new DataException("取样点样品类型与计划不一致:" + zixiang.getQuyangdianId()); } if (!Objects.equals(zixiang.getSuoshuChejian(), quyangdian.getChejianMingchengName())) { throw new DataException("取样点所属车间与计划不一致:" + zixiang.getQuyangdianId()); } return quyangdian; } private YangpinGuanliEntity chaxunYouxiaoYangpin(String yangpinId, String tenantId) { YangpinGuanliEntity yangpin = yangpinGuanliMapper.selectById(yangpinId); if (!isYouxiaoYangpin(yangpin, tenantId)) throw new DataException("样品不存在、已删除或已停用"); return yangpin; } private ZhiliangBiaozhunGuanliEntity chaxunYouxiaoBiaozhun(String biaozhunId, String tenantId, String yangpinId) { ZhiliangBiaozhunGuanliEntity biaozhun = zhiliangBiaozhunMapper.selectById(biaozhunId); if (!isYouxiaoBiaozhun(biaozhun, tenantId, yangpinId)) throw new DataException("质量标准不存在、已删除、已停用或与样品不匹配"); return biaozhun; } private LimsSxtQingyanXiangmuEntity chaxunYouxiaoQingyanPeizhi(String peizhiId, String tenantId, String yangpinId, String biaozhunId) { LimsSxtQingyanXiangmuEntity peizhi = qingyanPeizhiMapper.selectById(peizhiId); if (!isYouxiaoPeizhi(peizhi, tenantId, yangpinId, biaozhunId)) { throw new DataException("水系统请验配置不存在、已删除或与样品、质量标准不匹配"); } return peizhi; } private List chaxunYouxiaoPeizhiZixiang(String peizhiId, String tenantId) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("f_foreign_id", peizhiId).eq("f_tenant_id", tenantId) .and(item -> item.eq("f_delete_mark", 0).or().isNull("f_delete_mark")); List list = qingyanPeizhiZixiangMapper.selectList(wrapper); if (list.isEmpty()) throw new DataException("水系统请验配置未配置有效检验项目"); return list; } private boolean isYouxiaoYangpin(YangpinGuanliEntity item, String tenantId) { return item != null && tenantId.equals(item.getFTenantId()) && !isDeleted(item.getFDeleteMark()) && "enabled".equals(item.getBizEnabled()); } private boolean isYouxiaoBiaozhun(ZhiliangBiaozhunGuanliEntity item, String tenantId, String yangpinId) { return item != null && tenantId.equals(item.getTenantId()) && !isDeleted(item.getDeleteMark()) && "enabled".equals(item.getBizEnabled()) && yangpinId.equals(item.getYangpinId()); } private boolean isYouxiaoPeizhi(LimsSxtQingyanXiangmuEntity item, String tenantId, String yangpinId, String biaozhunId) { return item != null && tenantId.equals(item.getFTenantId()) && !isDeleted(item.getFDeleteMark()) && yangpinId.equals(item.getYangpinLeixing()) && biaozhunId.equals(item.getZhiliangBiaozhun()); } private boolean isYouxiaoPeizhiZixiang(LimsSxtQingyanXiangmuZixiangEntity item, String tenantId) { return item != null && tenantId.equals(item.getFTenantId()) && !isDeleted(item.getFDeleteMark()); } private boolean isYouxiaoBiaozhunZixiang(ZhiliangBiaozhunGuanliZixiangEntity item, String tenantId) { return item != null && tenantId.equals(item.getTenantId()) && !isDeleted(item.getDeleteMark()); } private boolean isYouxiaoJianyanXiangmu(JianyanXiangmuGuanliEntity item, String tenantId) { return item != null && tenantId.equals(item.getTenantId()) && !isDeleted(item.getDeleteMark()); } private boolean isDeleted(Integer deleteMark) { return deleteMark != null && deleteMark == 1; } private boolean hasText(String value) { return StringUtils.hasText(value); } private int parseInterval(String value) { try { int result = Integer.parseInt(value == null ? "" : value.trim()); if (result < 0) throw new NumberFormatException(); return result; } catch (NumberFormatException e) { throw new DataException("取样计划周期间隔必须为非负整数"); } } private List executionDates(LocalDate start, LocalDate end, String leixing, int jiange) { try { return LimsSxtQuyangJihuaRenwuPlanner.executionDates(start, end, leixing, jiange); } catch (IllegalArgumentException e) { throw new DataException(e.getMessage()); } } private String normalizeTenantId(String tenantId) { return hasText(tenantId) ? tenantId : "0"; } private void requireText(String value, String message) { if (!hasText(value)) throw new DataException(message); } private void requireJihuaRiqi(LocalDate jihuaRiqi) { if (jihuaRiqi == null) throw new DataException("任务执行日期不能为空"); } private LocalDate toLocalDate(Date value, String message) { if (value == null) throw new DataException(message); return value.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); } private String haxi(String value) { try { byte[] bytes = MessageDigest.getInstance("SHA-256").digest(value.getBytes(StandardCharsets.UTF_8)); StringBuilder result = new StringBuilder(bytes.length * 2); for (byte b : bytes) result.append(String.format("%02x", b)); return result.toString(); } catch (Exception e) { throw new IllegalStateException("无法计算快照哈希", e); } } private static class PeizhiWuxiaoException extends RuntimeException { private final String yuanyinDaima; private PeizhiWuxiaoException(String yuanyinDaima, String message) { super(message); this.yuanyinDaima = yuanyinDaima; } } }