package com.battery.business.service.report; import javax.sql.DataSource; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import com.alibaba.fastjson2.JSON; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; /** 化成身份与版本使用独立表,复用对比报表的事务内存储规则。 */ @Repository public class FormationReportRepository extends ReportVersionRepository { private final JdbcTemplate jdbc; public FormationReportRepository(DataSource dataSource) { super(dataSource, "formation", "化成"); jdbc = new JdbcTemplate(dataSource); } public record SourceSummary(Summary report, int versionNo, List> sources) { } public record CycleCurve(Map selection, Map curve) { } /** 列表只从保存版本投影来源名称,禁止传输或反序列化测量快照。 */ public List listSources() { return jdbc.query(""" SELECT r.report_id,r.report_name,r.creator_name,r.created_at_utc,v.version_no, JSON_EXTRACT(v.snapshot_json,'$.items[*].source') FROM t_formation_report r JOIN t_formation_report_version v ON v.report_id=r.report_id AND v.version_no=(SELECT MAX(v2.version_no) FROM t_formation_report_version v2 WHERE v2.report_id=r.report_id) ORDER BY r.created_at_utc DESC,r.report_id DESC """, (rs,index)->new SourceSummary(new Summary(rs.getLong(1),rs.getString(2),rs.getString(3),rs.getTimestamp(4).getTime()), rs.getInt(5), parseSources(rs.getString(6)))); } /** 首屏投影不含任何循环曲线,旧快照也按相同路径读取,不重写历史 JSON。 */ public Version page(long id, Integer versionNo) { return getProjection(id, versionNo, objectProjection("v.snapshot_json", List.of("name","description","cycleNos","settings","calculatedValues","units","displayUnits","curveMode"))); } /** 版本号由首屏查询固定,后续字段查询不会混入并发产生的新版本。 */ public List> pageItems(long id, int versionNo) { String path = "CONCAT('$.items[',j.item_index-1,']')"; String fields = List.of("source","cycleNos","cycles","metrics","businessFields","chargeSteps").stream() .map(key->"'"+key+"',JSON_EXTRACT(v.snapshot_json,CONCAT("+path+",'."+key+"'))").collect(Collectors.joining(",")); return jdbc.query("SELECT JSON_OBJECT("+fields+") FROM t_formation_report_version v " + "JOIN JSON_TABLE(v.snapshot_json,'$.items[*]' COLUMNS(item_index FOR ORDINALITY)) j ON TRUE " + "WHERE v.report_id=? AND v.version_no=? ORDER BY j.item_index", (rs,index)->JSON.parseObject(rs.getString(1)),id,versionNo); } /** 只投影指定保存循环,保留采样的原始顺序、空值、单位和独立来源身份。 */ public List cycleCurves(long id, int versionNo, int cycleNo) { return jdbc.query(""" SELECT JSON_EXTRACT(v.snapshot_json,CONCAT('$.items[',j.item_index-1,'].source.selection')), JSON_EXTRACT(v.snapshot_json,CONCAT('$.items[',j.item_index-1,'].curves."',?,'"')) FROM t_formation_report_version v JOIN JSON_TABLE(v.snapshot_json,'$.items[*]' COLUMNS(item_index FOR ORDINALITY)) j ON TRUE WHERE v.report_id=? AND v.version_no=? ORDER BY j.item_index """, (rs,index)->new CycleCurve(JSON.parseObject(rs.getString(1)),JSON.parseObject(rs.getString(2))),cycleNo,id,versionNo); } private static String objectProjection(String document, List keys) { return "JSON_OBJECT("+keys.stream().map(key->"'"+key+"',JSON_EXTRACT("+document+",'$."+key+"')").collect(Collectors.joining(","))+")"; } private static List> parseSources(String json) { if (json == null) return List.of(); return JSON.parseArray(json).stream().map(value->(Map)JSON.toJSON(value)).toList(); } }