刘光辉
昨天 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
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<ZhiliangBiaozhunGuanliMapper, ZhiliangBiaozhunGuanliEntity>
        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<ZhiliangBiaozhunGuanliEntity> 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<String, Object> 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<ZhiliangBiaozhunGuanliZixiangEntity> 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<ZhiliangBiaozhunGuanliZixiangEntity> 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<String, Object> 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<ZhiliangBiaozhunGuanliEntity> 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 + "】,请使用其他版本号");
        }
    }
}