package jnpf.limsService.impl; import jnpf.audit.sdk.annotation.AuditLog; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import jnpf.base.service.SuperServiceImpl; import jnpf.exception.DataException; import jnpf.limsEntity.BizEnabledEnum; import jnpf.limsEntity.ZhiliangBiaozhunGuanliEntity; import jnpf.limsEntity.ZhiliangBiaozhunGuanliZixiangEntity; import jnpf.limsMapper.ZhiliangBiaozhunGuanliMapper; import jnpf.limsService.ZhiliangBiaozhunGuanliService; import jnpf.limsService.ZhiliangBiaozhunGuanliZixiangService; import jnpf.util.RandomUtil; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; @Service public class ZhiliangBiaozhunGuanliServiceImpl extends SuperServiceImpl implements ZhiliangBiaozhunGuanliService { @Autowired private ZhiliangBiaozhunGuanliZixiangService zhiliangBiaozhunGuanliZixiangService; /** copyProperties 时统一忽略的系统/主键字段 */ private static final String[] MAIN_IGNORE = { "id", "tenantId", "creatorUserId", "creatorTime", "lastModifyUserId", "lastModifyTime", "deleteMark", "deleteTime", "deleteUserId" }; private static final String[] ZIXIANG_IGNORE = { "id", "foreignId", "tenantId", "deleteMark", "deleteTime", "deleteUserId" }; @Override public ZhiliangBiaozhunGuanliEntity findByYangpinId(String yangpinId) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("yangpin_id", yangpinId); wrapper.orderByDesc("f_id"); wrapper.last("LIMIT 1"); return this.getOne(wrapper); } @Override @Transactional(rollbackFor = Exception.class) @AuditLog(action = "VERSION_UPGRADE", label = "质量标准版本升级", bizType = "ZHILIANG_BIAOZHUN", bizCodeExpr = "#result ?: ''", targetTable = "zhiliang_biaozhun_guanli", targetIdExpr = "#result", diff = true, entityClass = ZhiliangBiaozhunGuanliEntity.class) public String versionUpgrade(Map formData) { if (formData == null || formData.isEmpty()) { throw new DataException("入参不能为空"); } // 路由复用:id 为空 = 新建场景(实际写入由 jnpf 在线表单完成),直接放行 // id 有值 = 版本升级场景,需要为旧数据生成历史快照 // // 版本升级流程:jnpf 表单的 beforeSubmit 钩子先调用本接口, // 我们把当前 DB 中的源记录整份复制成一条 biz_enabled=disabled 的历史快照行; // 接口返回成功后 jnpf 才执行它原生的 UPDATE,把源行(同一 f_id)更新成表单里 // 用户填写的"新版本数据"。 // 因此:本方法 INSERT 的那条新行 = 历史快照(旧数据); // 源行(被 jnpf 后续 UPDATE)= 升级后的当前活跃版本。 String sourceId = asString(formData.get("id")); if (sourceId == null || sourceId.isEmpty()) { return null; } // 1. 用 id 查源主表(当前在 DB 里的旧数据) ZhiliangBiaozhunGuanliEntity source = this.getById(sourceId); if (source == null) { throw new DataException("未找到对应的质量标准记录(id=" + sourceId + ")"); } // 1.1 校验前端传入的 biaozhun_banben 在 (yangpin_id, biaozhun_bianhao) 维度下是否唯一 validateBiaozhunBanbenUnique(formData, source); // 2. 查源记录的子表项 List sourceZixiangList = zhiliangBiaozhunGuanliZixiangService.listByForeignId(sourceId); // 3. 把源主表整体复制成"历史快照"行:新 f_id、biz_enabled=disabled String historicalSnapshotId = RandomUtil.uuId(); ZhiliangBiaozhunGuanliEntity historicalSnapshot = new ZhiliangBiaozhunGuanliEntity(); BeanUtils.copyProperties(source, historicalSnapshot, MAIN_IGNORE); historicalSnapshot.setId(historicalSnapshotId); historicalSnapshot.setBizEnabled(BizEnabledEnum.DISABLED.getCode()); // 兜底:如果 MetaObjectHandler 没生效,至少 creatorTime 有值 historicalSnapshot.setCreatorTime(new Date()); this.save(historicalSnapshot); // 4. 复制源子表项到历史快照下:每条新 f_id,f_foreign_id 指向快照主表 f_id if (sourceZixiangList != null && !sourceZixiangList.isEmpty()) { List historicalZixiangList = new ArrayList<>(sourceZixiangList.size()); for (ZhiliangBiaozhunGuanliZixiangEntity src : sourceZixiangList) { ZhiliangBiaozhunGuanliZixiangEntity copy = new ZhiliangBiaozhunGuanliZixiangEntity(); BeanUtils.copyProperties(src, copy, ZIXIANG_IGNORE); copy.setId(RandomUtil.uuId()); copy.setForeignId(historicalSnapshotId); historicalZixiangList.add(copy); } zhiliangBiaozhunGuanliZixiangService.saveBatch(historicalZixiangList); } return historicalSnapshotId; } private static String asString(Object v) { return v == null ? null : v.toString(); } /** * 仅做前端拦截用:校验 formData 里的 biaozhun_banben 在同一 * (yangpin_id, biaozhun_bianhao) 维度下是否已存在 —— 不同标准编号下的版本号是隔离的, * 不应跨标准互相判重。yangpin_id 与 biaozhun_bianhao 都取自 DB 源记录, * 避免前端篡改。任一值缺失则跳过校验。 */ private void validateBiaozhunBanbenUnique(Map formData, ZhiliangBiaozhunGuanliEntity source) { String biaozhunBanben = asString(formData.get("biaozhun_banben")); if (biaozhunBanben == null || biaozhunBanben.isEmpty()) { return; } String yangpinId = source.getYangpinId(); String biaozhunBianhao = source.getBiaozhunBianhao(); if (yangpinId == null || yangpinId.isEmpty() || biaozhunBianhao == null || biaozhunBianhao.isEmpty()) { return; } QueryWrapper w = new QueryWrapper<>(); w.eq("yangpin_id", yangpinId); w.eq("biaozhun_bianhao", biaozhunBianhao); w.eq("biaozhun_banben", biaozhunBanben); w.and(q -> q.isNull("f_delete_mark").or().ne("f_delete_mark", 1)); // 排除源记录自身:upgrade 场景下新记录是从源复制的,源不应与自己冲突 if (source.getId() != null && !source.getId().isEmpty()) { w.ne("f_id", source.getId()); } if (this.count(w) > 0) { throw new DataException("该样品的标准编号【" + biaozhunBianhao + "】下已存在版本号【" + biaozhunBanben + "】,请使用其他版本号"); } } }