package com.battery.business.report; import java.math.BigDecimal; import java.math.MathContext; import java.util.*; import com.alibaba.fastjson2.*; import com.battery.business.domain.*; import com.battery.business.service.*; import com.ruoyi.common.exception.ServiceException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** 拼接报表按来源顺序平移循环号;统计只读取检测完成测试的当前有效循环记录。 */ @Service public class SpliceReportService { private final ITTestService tests; private final ITestDataQueryService data; private final SpliceReportRepository reports; public SpliceReportService(ITTestService t, ITestDataQueryService d, SpliceReportRepository r){tests=t;data=d;reports=r;} public record Candidate(SpliceReportRequest.Selection selection,String name,String type,long groupId,String group,String batteryNo,long startedAt){} public record Matrix(List> segments,List cycleNos){} public List sources(){ TTest q=new TTest(); q.setTestStatus("COMPLETED"); List out=new ArrayList<>(); for(TTest s:tests.selectTestList(q)){TTest t=tests.selectTestById(s.getTestId()); for(TTestBattery b:t.getBatteries()) out.add(candidate(t,b));} return out; } public Matrix preview(List selections){ if(selections==null||selections.size()<2||new HashSet<>(selections).size()!=selections.size()) throw new ServiceException("请选择至少两个不重复的检测完成测试电池"); List> segments=new ArrayList<>(); Set groups=new HashSet<>(); Set batteries=new HashSet<>(); int offset=0; long group=0; for(var sel:selections){TTest t=tests.selectTestById(sel.testId()); if(t==null||!"COMPLETED".equals(t.getTestStatus())) throw new ServiceException("仅可选择检测完成测试"); TTestBattery b=t.getBatteries().stream().filter(x->sel.testBatteryId().equals(x.getTestBatteryId())).findFirst().orElseThrow(()->new ServiceException("电池不属于所选测试")); if(group==0)group=b.getGroupId(); if(group!=b.getGroupId()) throw new ServiceException("拼接测试必须属于同一电池组"); groups.add(b.getGroupId()); batteries.add(b.getBatteryNo()); List> rows=data.cycles(sel.testId()).stream().filter(r->Objects.equals(r.get("batteryNo"),b.getBatteryNo())).toList(); List nos=rows.stream().map(this::cycle).filter(x->x>0).distinct().sorted().toList(); Map seg=new LinkedHashMap<>(); seg.put("source",candidate(t,b)); seg.put("offset",offset); seg.put("cycles",rows); seg.put("originalCycleNos",nos); seg.put("statistics",statistics(rows,nos)); segments.add(seg); offset += nos.size(); } if(batteries.size()==0) throw new ServiceException("未找到共同参与电池"); return new Matrix(segments,java.util.stream.IntStream.range(1,offset+1).boxed().toList()); } private Map statistics(List> rows,List nos){Map out=new LinkedHashMap<>(); for(int no:nos){var m=rows.stream().filter(r->cycle(r)==no).toList(); Map v=new LinkedHashMap<>(); if(m.size()==1){Map row=m.get(0); for(String k:List.of("chargeEnergy","dischargeEnergy","chargeCapacity","dischargeCapacity")) if(row.get(k)!=null)v.put(k,row.get(k)); Number c=num(v.get("chargeEnergy")),d=num(v.get("dischargeEnergy")); if(c!=null&&d!=null&&c.doubleValue()>0)v.put("energyEfficiency",BigDecimal.valueOf(d.doubleValue()).multiply(BigDecimal.valueOf(100)).divide(BigDecimal.valueOf(c.doubleValue()),MathContext.DECIMAL128));} out.put(Integer.toString(no),v);} return out;} private List> dataStepRows(Map row){return List.of();} private Number num(Object x){return x instanceof Number?n((Number)x):null;} private Number n(Number x){return x;} @Transactional public Map create(SpliceReportRequest r,long uid,String user){Matrix m=preview(r.selections()); Map snap=new LinkedHashMap<>(); snap.put("name",r.name().trim());snap.put("description",r.description());snap.put("selections",r.selections());snap.put("segments",m.segments());snap.put("cycleNos",m.cycleNos());snap.put("settings",r.settings()); return get(reports.insert(r.name().trim(),JSON.toJSONString(snap),uid,user,System.currentTimeMillis()),null);} public Map get(long id,Integer version){var v=reports.get(id,version);Map x=JSON.parseObject(v.snapshotJson());x.put("id",id);x.put("versionNo",v.versionNo());x.put("creator",v.report().creator());x.put("createdAt",v.report().createdAt());x.put("operatorName",v.operatorName());x.put("versionCreatedAt",v.versionCreatedAt());x.put("changeSummary",v.changeSummary());return x;} @Transactional public Map edit(long id,SpliceReportRequest.Edit r,long uid,String user){reports.lock(id);var v=reports.get(id);if(v.versionNo()!=r.baseVersionNo())throw new ServiceException("报表已有新版本,请重新打开后编辑");var x=JSON.parseObject(v.snapshotJson());x.put("name",r.name().trim());x.put("description",r.description());x.put("settings",r.settings());reports.append(id,r.baseVersionNo()+1,r.name().trim(),x.toJSONString(),uid,user,r.changeSummary().trim());return get(id,null);} @Transactional public Map refresh(long id,int base,String summary,long uid,String user){reports.lock(id);var v=reports.get(id);if(v.versionNo()!=base)throw new ServiceException("报表已有新版本,请重新打开后编辑");var x=JSON.parseObject(v.snapshotJson());var selections=JSON.parseArray(x.getString("selections"),SpliceReportRequest.Selection.class);Matrix m=preview(selections);x.put("segments",m.segments());x.put("cycleNos",m.cycleNos());reports.append(id,base+1,x.getString("name"),x.toJSONString(),uid,user,"按最新来源生成:"+summary);return get(id,null);} public void delete(long id,long uid,String user){reports.lock(id);reports.delete(id,uid,user);} private Candidate candidate(TTest t,TTestBattery b){return new Candidate(new SpliceReportRequest.Selection(t.getTestId(),b.getTestBatteryId()),t.getTestName(),t.getTestTypeName(),b.getGroupId(),b.getGroupName(),b.getBatteryNo(),t.getStartedAtUtc()==null?0:t.getStartedAtUtc().getTime());} private int cycle(Map r){return r.get("cycleNo") instanceof Number n?n.intValue():0;} }