yeg
2026-09-09 b837cc1f88e6fa14442f66eecb59c45464dd7bb3
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
package com.battery.business.report;
 
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson2.JSON;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import com.ruoyi.common.exception.ServiceException;
 
/** 报表身份和版本存储;重读只访问报表表,不联查来源测试。 */
public abstract class ReportVersionRepository
{
    private final JdbcTemplate jdbc;
    private final String type;
    private final String title;
 
    protected ReportVersionRepository(DataSource dataSource, String type, String title)
    { jdbc = new JdbcTemplate(dataSource); this.type = type; this.title = title; }
 
    // 类型仅由两个仓储构造器指定,SQL 表名不接受请求输入;两类共用已验证的版本和删除规则。
    private String sql(String statement) { return statement.replace("t_compare_report", "t_" + type + "_report"); }
 
    public record Summary(long id, String name, String creator, long createdAt) { }
    /** 仅供需要从快照提取列表展示字段的化成服务使用;普通报表列表不返回快照。 */
    public record SummarySnapshot(long id, String name, String creator, long createdAt, int versionNo, String snapshotJson) { }
    public record Version(Summary report, int versionNo, String snapshotJson, String operatorName, long versionCreatedAt, String changeSummary) { }
    public record History(int versionNo, String operatorName, long createdAt, String changeSummary) { }
 
    /** 查询所有已保存报表摘要,允许授权用户跨创建人查看。 */
    public List<Summary> list()
    {
        return jdbc.query(sql("select report_id, report_name, creator_name, created_at_utc from t_compare_report order by created_at_utc desc, report_id desc"),
                (rs, index) -> new Summary(rs.getLong(1), rs.getString(2), rs.getString(3), rs.getTimestamp(4).getTime()));
    }
 
    public List<SummarySnapshot> listWithSnapshots()
    {
        return jdbc.query(sql("select r.report_id, r.report_name, r.creator_name, r.created_at_utc, v.version_no, v.snapshot_json "
                + "from t_compare_report r join t_compare_report_version v on v.report_id=r.report_id "
                + "and v.version_no=(select max(v2.version_no) from t_compare_report_version v2 where v2.report_id=r.report_id) "
                + "order by r.created_at_utc desc, r.report_id desc"),
                (rs, index) -> new SummarySnapshot(rs.getLong(1), rs.getString(2), rs.getString(3), rs.getTimestamp(4).getTime(), rs.getInt(5), rs.getString(6)));
    }
 
    /** 重读最新完整版本;来源不存在不影响结果。 */
    public Version get(long id)
    { return get(id, null); }
 
    /** 读取指定历史版,版本号为空时读取最新版本。 */
    public Version get(long id, Integer versionNo)
    {
        var rows = jdbc.query(sql("select r.report_id, r.report_name, r.creator_name, r.created_at_utc, v.version_no, v.snapshot_json, v.operator_name, v.created_at_utc, v.change_summary "
                + "from t_compare_report r join t_compare_report_version v on v.report_id=r.report_id where r.report_id=? "
                + (versionNo == null ? "and v.version_no=(select max(v2.version_no) from t_compare_report_version v2 where v2.report_id=r.report_id)" : "and v.version_no=?")),
                (rs, index) -> new Version(new Summary(rs.getLong(1), rs.getString(2), rs.getString(3), rs.getTimestamp(4).getTime()),
                        rs.getInt(5), rs.getString(6), rs.getString(7), rs.getTimestamp(8).getTime(), rs.getString(9)),
                versionNo == null ? new Object[] { id } : new Object[] { id, versionNo });
        if (rows.isEmpty()) throw new ServiceException(title + "报表或版本不存在");
        return rows.get(0);
    }
 
    /** 历史摘要只来自正式版本,不返回测量快照。 */
    public List<History> history(long id)
    {
        return jdbc.query(sql("select version_no,operator_name,created_at_utc,change_summary from t_compare_report_version where report_id=? order by version_no desc"),
                (rs, index) -> new History(rs.getInt(1), rs.getString(2), rs.getTimestamp(3).getTime(), rs.getString(4)), id);
    }
 
    /** 业务事务内锁定报表,版本号分配与旧基准拒绝共用此锁。 */
    public void lock(long id)
    {
        if (jdbc.queryForList(sql("select report_id from t_compare_report where report_id=? for update"), Long.class, id).isEmpty())
            throw new ServiceException(title + "报表不存在");
    }
 
    /** 仅在业务事务和报表锁内调用;物理清理全部版本,同步保留最小操作日志。 */
    public void delete(long id, long operatorId, String operatorName)
    {
        String name = jdbc.queryForObject(sql("select report_name from t_compare_report where report_id=?"), String.class, id);
        jdbc.update(sql("delete from t_compare_report_version where report_id=?"), id);
        jdbc.update(sql("delete from t_compare_report where report_id=?"), id);
        // 不走异步 @Log,日志失败必须回滚删除;只保留身份,不读取或记录测量快照。
        jdbc.update("insert into sys_oper_log(title,business_type,method,request_method,operator_type,oper_name,oper_url,oper_param,status,oper_time) values(?,3,?,'DELETE',1,?,?,?,0,?)",
                title + "报表删除", type + "-report.delete", operatorName, "/system/" + type + "-report/" + id,
                JSON.toJSONString(Map.of("reportId", id, "reportName", name, "operatorId", operatorId)), new Timestamp(System.currentTimeMillis()));
    }
 
    /** 在业务事务和报表锁内追加版本并更新列表名称;调用前须校验最新基准并分配版本号。 */
    public void append(long id, int versionNo, String name, String snapshot, long operatorId, String operatorName, String summary)
    {
        jdbc.update(sql("insert into t_compare_report_version(report_id,version_no,snapshot_json,operator_id,operator_name,created_at_utc,change_summary) values(?,?,?,?,?,?,?)"),
                id, versionNo, snapshot, operatorId, operatorName, new Timestamp(System.currentTimeMillis()), summary);
        jdbc.update(sql("update t_compare_report set report_name=? where report_id=?"), name, id);
    }
 
    /** 由业务事务调用,身份与首个版本任一写入失败必须一起回滚。 */
    public long insert(String name, String snapshot, long operatorId, String operatorName, long createdAt)
    {
        var keys = new GeneratedKeyHolder();
        jdbc.update(connection -> {
            var statement = connection.prepareStatement(sql("insert into t_compare_report(report_name,creator_id,creator_name,created_at_utc) values(?,?,?,?)"), java.sql.Statement.RETURN_GENERATED_KEYS);
            statement.setString(1, name); statement.setLong(2, operatorId); statement.setString(3, operatorName);
            statement.setTimestamp(4, new Timestamp(createdAt));
            return statement;
        }, keys);
        long id = keys.getKey().longValue();
        jdbc.update(sql("insert into t_compare_report_version(report_id,version_no,snapshot_json,operator_id,operator_name,created_at_utc,change_summary) values(?,1,?,?,?,?,?)"),
                id, snapshot, operatorId, operatorName, new Timestamp(createdAt), "首次保存" + title + "报表");
        return id;
    }
}