Merge branch 'develop-business-css' of http://121.37.111.42:3000/ThbTech/JavaProjectRepo into develop-business-css

This commit is contained in:
limengnan 2025-12-25 09:22:15 +08:00
commit 4aca24a8c1
12 changed files with 222 additions and 123 deletions

View File

@ -1,23 +1,30 @@
package com.yfd.business.css.config;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import jakarta.annotation.Resource;
import javax.sql.DataSource;
@Configuration
public class MybatisConfig {
@Resource
private MybatisPlusInterceptor mybatisPlusInterceptor;
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:/mapper/**/*.xml"));
factoryBean.setPlugins(new Interceptor[]{ mybatisPlusInterceptor });
return factoryBean.getObject();
}

View File

@ -66,20 +66,13 @@ public class AlgorithmController {
public Page<Algorithm> searchAlgorithms(@RequestParam(required = false) String name,
@RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Algorithm> qw = new QueryWrapper<>();
if (name != null && !name.isEmpty()) {
qw.like("name", name);
}
qw.orderByDesc("updated_at").last("LIMIT " + offset + "," + pageSize);
java.util.List<Algorithm> records = algorithmService.list(qw);
long total = (name != null && !name.isEmpty())
? algorithmService.count(new QueryWrapper<Algorithm>().like("name", name))
: algorithmService.count();
qw.orderByDesc("updated_at");
Page<Algorithm> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records);
page.setTotal(total);
return page;
return algorithmService.page(page, qw);
}
private String currentUsername() {

View File

@ -76,8 +76,9 @@ public class CriticalDataController {
* @return 是否导入成功
*/
@PostMapping("/import")
public boolean importCriticalData(@RequestParam("file") MultipartFile file) {
return criticalDataService.importCriticalData(file);
public boolean importCriticalData(@RequestParam("file") MultipartFile file,
@RequestParam String deviceType) {
return criticalDataService.importCriticalData(file, deviceType);
}
@ -95,16 +96,10 @@ public class CriticalDataController {
public Page<CriticalData> listByDeviceType(@RequestParam String deviceType,
@RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
Page<CriticalData> page = new Page<>(pageNum, pageSize, true);
QueryWrapper<CriticalData> qw = new QueryWrapper<CriticalData>()
.eq("device_type", deviceType)
.orderByDesc("created_at")
.last("LIMIT " + offset + "," + pageSize);
java.util.List<CriticalData> records = criticalDataService.list(qw);
long total = criticalDataService.count(new QueryWrapper<CriticalData>().eq("device_type", deviceType));
Page<CriticalData> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records);
page.setTotal(total);
return page;
.orderByDesc("created_at");
return criticalDataService.page(page, qw);
}
}

View File

@ -85,8 +85,9 @@ public class DeviceController {
* @return 是否导入成功
*/
@PostMapping("/import")
public boolean importDevices(@RequestParam("file") MultipartFile file) {
return deviceService.importDevices(file);
public boolean importDevices(@RequestParam("file") MultipartFile file,
@RequestParam String deviceType) {
return deviceService.importDevices(file, deviceType);
}
@ -124,7 +125,6 @@ public class DeviceController {
@RequestParam(required = false) String name,
@RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Device> qw = new QueryWrapper<>();
if (type != null && !type.isEmpty()) {
qw.eq("type", type);
@ -132,20 +132,9 @@ public class DeviceController {
if (name != null && !name.isEmpty()) {
qw.like("name", name);
}
qw.orderByDesc("created_at").last("LIMIT " + offset + "," + pageSize);
java.util.List<Device> records = deviceService.list(qw);
QueryWrapper<Device> countQw = new QueryWrapper<>();
if (type != null && !type.isEmpty()) {
countQw.eq("type", type);
}
if (name != null && !name.isEmpty()) {
countQw.like("name", name);
}
long total = deviceService.count(countQw);
qw.orderByDesc("created_at");
Page<Device> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records);
page.setTotal(total);
return page;
return deviceService.page(page, qw);
}
private String currentUsername() {

View File

@ -102,37 +102,23 @@ public class MaterialController {
public Page<Material> search(@RequestParam(required = false) String name,
@RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Material> qw = new QueryWrapper<Material>().orderByDesc("created_at");
if (name != null && !name.isEmpty()) {
qw.like("name", name);
}
qw.last("LIMIT " + offset + "," + pageSize);
java.util.List<Material> records = materialService.list(qw);
long total = (name != null && !name.isEmpty())
? materialService.count(new QueryWrapper<Material>().like("name", name))
: materialService.count();
Page<Material> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records);
page.setTotal(total);
return page;
return materialService.page(page, qw);
}
@GetMapping("/by-project")
public Page<Material> pageByProject(@RequestParam String projectId,
@RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Material> qw = new QueryWrapper<Material>()
.eq("project_id", projectId)
.orderByDesc("created_at")
.last("LIMIT " + offset + "," + pageSize);
java.util.List<Material> records = materialService.list(qw);
long total = materialService.count(new QueryWrapper<Material>().eq("project_id", projectId));
.orderByDesc("created_at");
Page<Material> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records);
page.setTotal(total);
return page;
return materialService.page(page, qw);
}
private String currentUsername() {

View File

@ -156,23 +156,12 @@ public class ProjectController {
public Page<Project> search(@RequestParam(required = false) @Parameter(description = "项目名称关键词,可为空") String name,
@RequestParam(defaultValue = "1") @Parameter(description = "页码默认1") long pageNum,
@RequestParam(defaultValue = "20") @Parameter(description = "每页条数默认20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Project> qw = new QueryWrapper<Project>().orderByDesc("created_at");
if (name != null && !name.isEmpty()) {
qw.like("name", name);
}
qw.last("LIMIT " + offset + "," + pageSize);
java.util.List<Project> records = projectService.list(qw);
long total;
if (name != null && !name.isEmpty()) {
total = projectService.count(new QueryWrapper<Project>().like("name", name));
} else {
total = projectService.count();
}
Page<Project> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records);
page.setTotal(total);
return page;
return projectService.page(page, qw);
}
/**

View File

@ -103,20 +103,12 @@ public class ScenarioController {
@RequestParam(required = false) String name,
@RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Scenario> qw = new QueryWrapper<Scenario>().eq("project_id", projectId).orderByDesc("created_at");
if (name != null && !name.isEmpty()) {
qw.like("name", name);
}
qw.last("LIMIT " + offset + "," + pageSize);
java.util.List<Scenario> records = scenarioService.list(qw);
long total = (name != null && !name.isEmpty())
? scenarioService.count(new QueryWrapper<Scenario>().eq("project_id", projectId).like("name", name))
: scenarioService.count(new QueryWrapper<Scenario>().eq("project_id", projectId));
Page<Scenario> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records);
page.setTotal(total);
return page;
return scenarioService.page(page, qw);
}
private String currentUsername() {

View File

@ -9,7 +9,7 @@ public interface CriticalDataService extends IService<CriticalData> {
/**
* 导入临界数据
*/
boolean importCriticalData(MultipartFile file);
boolean importCriticalData(MultipartFile file, String deviceType);
}

View File

@ -7,5 +7,5 @@ public interface DeviceService extends IService<Device> {
/**
* 导入设备
*/
boolean importDevices(MultipartFile file);
boolean importDevices(MultipartFile file, String deviceType);
}

View File

@ -40,7 +40,7 @@ public class CriticalDataServiceImpl
@Resource
private IUserService userService;
@Override
public boolean importCriticalData(MultipartFile file) {
public boolean importCriticalData(MultipartFile file, String deviceType) {
try {
String name = file.getOriginalFilename();
log.info("critical-data import start name={} size={} contentType={}", name, file.getSize(), file.getContentType());
@ -48,10 +48,10 @@ public class CriticalDataServiceImpl
String lower = name.toLowerCase();
if (lower.endsWith(".xlsx")) {
log.info("critical-data detected type=xlsx");
return importExcel(new XSSFWorkbook(file.getInputStream()));
return importExcel(new XSSFWorkbook(file.getInputStream()), deviceType);
} else if (lower.endsWith(".xls")) {
log.info("critical-data detected type=xls");
return importExcel(new HSSFWorkbook(file.getInputStream()));
return importExcel(new HSSFWorkbook(file.getInputStream()), deviceType);
} else {
log.warn("critical-data unsupported file type name={}", name);
return false;
@ -62,7 +62,7 @@ public class CriticalDataServiceImpl
}
}
private boolean importExcel(Workbook workbook) {
private boolean importExcel(Workbook workbook, String deviceType) {
try (Workbook wb = workbook) {
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateAll();
@ -86,7 +86,7 @@ public class CriticalDataServiceImpl
}
log.info("critical-data excel header keys={}", idx.keySet());
String[] keys = new String[]{
"device_type","diameter","height",
"diameter","height",
"fissile_concentration","isotopic_abundance",
"keff_value","extra_features"
};
@ -101,7 +101,7 @@ public class CriticalDataServiceImpl
Row row = sheet.getRow(r);
if (row == null) continue;
CriticalData cd = new CriticalData();
cd.setDeviceType(cleanString(getString(row, idx.get("device_type"))));
cd.setDeviceType(deviceType);
cd.setDiameter(cleanDecimal(getDecimalFlexible(row, idx.get("diameter"), evaluator, formatter)));
cd.setHeight(cleanDecimal(getDecimalFlexible(row, idx.get("height"), evaluator, formatter)));
cd.setFissileConcentration(cleanDecimal(getDecimalFlexible(row, idx.get("fissile_concentration"), evaluator, formatter)));

View File

@ -35,15 +35,15 @@ public class DeviceServiceImpl
@Resource
private IUserService userService;
@Override
public boolean importDevices(MultipartFile file) {
public boolean importDevices(MultipartFile file, String deviceType) {
try {
String name = file.getOriginalFilename();
if (name == null) return false;
String lower = name.toLowerCase();
if (lower.endsWith(".xlsx")) {
return importExcel(new XSSFWorkbook(file.getInputStream()));
return importExcel(new XSSFWorkbook(file.getInputStream()), deviceType);
} else if (lower.endsWith(".xls")) {
return importExcel(new HSSFWorkbook(file.getInputStream()));
return importExcel(new HSSFWorkbook(file.getInputStream()), deviceType);
} else {
return false;
}
@ -52,7 +52,7 @@ public class DeviceServiceImpl
}
}
private boolean importExcel(Workbook workbook) {
private boolean importExcel(Workbook workbook, String deviceType) {
try (Workbook wb = workbook) {
Sheet sheet = wb.getSheetAt(0);
if (sheet == null) return false;
@ -65,7 +65,7 @@ public class DeviceServiceImpl
String key = c.getStringCellValue();
if (key != null) idx.put(key.trim().toLowerCase(), i);
}
String[] keys = new String[]{"type","code","name","size","volume","flow_rate","pulse_velocity"};
String[] keys = new String[]{"code","name","size","volume","flow_rate","pulse_velocity"};
for (String k : keys) {
if (!idx.containsKey(k)) return false;
}
@ -74,7 +74,7 @@ public class DeviceServiceImpl
Row row = sheet.getRow(r);
if (row == null) continue;
Device d = new Device();
d.setType(getString(row, idx.get("type")));
d.setType(deviceType);
d.setCode(getString(row, idx.get("code")));
d.setName(getString(row, idx.get("name")));
d.setSize(validateJson(getString(row, idx.get("size"))));

View File

@ -190,17 +190,29 @@ public class ProjectServiceImpl
src.put("delayMs", delayMs);
srcList.add(src);
}
}
}
Map<String, Object> plan = new HashMap<>();
plan.put("target", Map.of("entityType","device","entityId",deviceId,"property",propName));
plan.put("bias", prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0);
plan.put("sources", srcList);
plans.add(plan);
}
});
}
// materials can be array or single object; also some payloads use "material" key
Map<String, Object> plan = new HashMap<>();
plan.put("target", Map.of("entityType","device","entityId",deviceId,"property",propName));
plan.put("bias", prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0);
plan.put("sources", srcList);
StringBuilder sb = new StringBuilder();
double b = prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0;
sb.append(b);
for (Map<String, Object> s : srcList) {
double coef = s.get("coefficient") instanceof Number ? ((Number) s.get("coefficient")).doubleValue() : 1.0;
String seId = String.valueOf(s.get("entityId"));
String seProp = String.valueOf(s.get("property"));
long dms = s.get("delayMs") instanceof Number ? ((Number) s.get("delayMs")).longValue() : 0L;
long ds = dms / 1000L;
sb.append(" + ").append(coef).append("*").append(seId).append(".").append(seProp).append("@").append(ds).append("s");
}
plan.put("formula", sb.toString());
plans.add(plan);
}
});
}
// materials can be array or single object; also some payloads use "material" key
JsonNode mats = dn.path("materials");
if (mats.isMissingNode() || mats.isNull()) {
mats = dn.path("material");
@ -298,16 +310,28 @@ public class ProjectServiceImpl
src.put("delayMs", delayMs);
srcList.add(src);
}
}
}
Map<String, Object> plan = new HashMap<>();
plan.put("target", Map.of("entityType","material","entityId",mid,"property",propName));
plan.put("bias", prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0);
plan.put("sources", srcList);
plans.add(plan);
}
});
}
Map<String, Object> plan = new HashMap<>();
plan.put("target", Map.of("entityType","material","entityId",mid,"property",propName));
plan.put("bias", prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0);
plan.put("sources", srcList);
StringBuilder sb = new StringBuilder();
double b = prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0;
sb.append(b);
for (Map<String, Object> s : srcList) {
double coef = s.get("coefficient") instanceof Number ? ((Number) s.get("coefficient")).doubleValue() : 1.0;
String seId = String.valueOf(s.get("entityId"));
String seProp = String.valueOf(s.get("property"));
long dms = s.get("delayMs") instanceof Number ? ((Number) s.get("delayMs")).longValue() : 0L;
long ds = dms / 1000L;
sb.append(" + ").append(coef).append("*").append(seId).append(".").append(seProp).append("@").append(ds).append("s");
}
plan.put("formula", sb.toString());
plans.add(plan);
}
});
}
}
private static String optText(JsonNode n, String field) {
@ -497,10 +521,6 @@ public class ProjectServiceImpl
return out;
}
JsonNode root = objectMapper.readTree(p.getTopology());
long start = getLongParam(params, "startTime", 0L);
long end = getLongParam(params, "endTime", 60L);
long stepSec = getLongParam(params, "step", 1L);
if (end < start) end = start;
List<Device> devs = parseDeviceOrder(projectId);
if (devs.isEmpty()) {
issues.add("devices为空");
@ -508,27 +528,42 @@ public class ProjectServiceImpl
out.put("frames", frames);
return out;
}
Map<String, String> devToMat = buildDeviceMaterialMap(root, issues);
Map<String, Map<String, Double>> devStatic = new HashMap<>();
Map<String, Map<String, Object>> devInfluence = new HashMap<>();
parseDeviceStaticsAndInfluences(root, devStatic, devInfluence, issues);
Map<String, Map<String, Double>> matStatic = new HashMap<>();
Map<String, Map<String, Object>> matInfluence = new HashMap<>();
parseMaterialStaticsAndInfluences(root, matStatic, matInfluence, issues);
Map<String, Map<String, Double>> matStaticDb = buildMaterialStaticFromDb(devToMat);
List<Event> events = eventService.list(
new QueryWrapper<Event>()
.select("event_id","scenario_id","device_id","material_id","attr_changes","trigger_time","created_at","modifier")
.eq("scenario_id", scenarioId)
);
Map<String, Object> valueProviders = buildValueProviders(events, issues);
List<Long> timePoints = collectTimePoints(valueProviders);
if (timePoints.isEmpty()) {
issues.add("事件为空或未提供时间点,无法生成帧");
out.put("generated", Map.of("events", events.size(), "snapshots", 0));
out.put("frames", frames);
return out;
}
Map<String, String> devTypeMap = buildDeviceTypeMap(root);
int snapshots = 0;
for (long t = start; t <= end; t += stepSec) {
int stepIndex = (int) ((t - start) / stepSec);
for (int idx = 0; idx < timePoints.size(); idx++) {
long t = timePoints.get(idx);
int stepIndex = idx;
Map<String, Map<String, Object>> deviceStates = new HashMap<>();
for (Device d : devs) {
String did = d.getDeviceId();
String dtype = devTypeMap.getOrDefault(did, d.getType());
Map<String, Object> state = new HashMap<>();
Map<String, Double> s = devStatic.getOrDefault(did, Map.of());
for (Map.Entry<String, Double> e : s.entrySet()) state.put(e.getKey(), e.getValue());
if (dtype != null && !dtype.isEmpty()) {
state.put("deviceType", dtype);
}
Map<String, Double> sdev = devStatic.getOrDefault(did, Map.of());
for (Map.Entry<String, Double> e : sdev.entrySet()) state.put(e.getKey(), e.getValue());
Map<String, Object> infl = devInfluence.getOrDefault(did, Map.of());
for (Map.Entry<String, Object> e : infl.entrySet()) {
String prop = e.getKey();
@ -552,12 +587,17 @@ public class ProjectServiceImpl
}
state.put(prop, sum + bias);
}
Map<String, Object> materialsState = new HashMap<>();
for (Map.Entry<String, Map<String, Double>> me : matStatic.entrySet()) {
String mid = me.getKey();
Map<String, Object> mstate = new HashMap<>();
for (Map.Entry<String, Double> e : me.getValue().entrySet()) mstate.put(e.getKey(), e.getValue());
Map<String, Object> minfl = matInfluence.getOrDefault(mid, Map.of());
String boundMid = devToMat.get(did);
if (boundMid != null) {
Map<String, Double> smatDb = matStaticDb.getOrDefault(boundMid, Map.of());
for (Map.Entry<String, Double> e : smatDb.entrySet()) {
state.put(e.getKey(), e.getValue());
}
Map<String, Double> smatTopo = matStatic.getOrDefault(boundMid, Map.of());
for (Map.Entry<String, Double> e : smatTopo.entrySet()) {
state.put(e.getKey(), e.getValue());
}
Map<String, Object> minfl = matInfluence.getOrDefault(boundMid, Map.of());
for (Map.Entry<String, Object> e : minfl.entrySet()) {
String prop = e.getKey();
@SuppressWarnings("unchecked")
@ -578,11 +618,13 @@ public class ProjectServiceImpl
sum += coef * val;
}
}
mstate.put(prop, sum + bias);
state.put(prop, sum + bias);
}
materialsState.put(mid, mstate);
}
state.put("materials", materialsState);
// apply material event overrides
overrideWithEvents(state, "material", boundMid, t, valueProviders);
}
// apply device event overrides
overrideWithEvents(state, "device", did, t, valueProviders);
deviceStates.put(did, state);
}
snapshots += devs.size();
@ -834,6 +876,112 @@ public class ProjectServiceImpl
return s.getOrDefault(property, 0.0);
}
private List<Long> collectTimePoints(Map<String, Object> providers) {
Set<Long> ts = new HashSet<>();
for (Object v : providers.values()) {
@SuppressWarnings("unchecked")
Map<String, Object> pv = (Map<String, Object>) v;
@SuppressWarnings("unchecked")
List<Map<String, Object>> schedule = (List<Map<String, Object>>) pv.get("schedule");
if (schedule == null) continue;
for (Map<String, Object> s : schedule) {
String type = String.valueOf(s.get("type"));
if ("step-set".equals(type)) {
long t = (long) toDouble(s.get("time"));
ts.add(t);
} else if ("ramp".equals(type)) {
long st = (long) toDouble(s.get("startTime"));
long et = (long) toDouble(s.get("endTime"));
ts.add(st);
ts.add(et);
}
}
}
List<Long> list = new ArrayList<>(ts);
list.sort(Comparator.naturalOrder());
return list;
}
private Map<String, String> buildDeviceMaterialMap(JsonNode root, List<String> issues) {
Map<String, String> map = new HashMap<>();
JsonNode devicesNode = root.path("devices");
if (!devicesNode.isArray()) return map;
for (JsonNode dn : devicesNode) {
String did = optText(dn, "deviceId");
if (did == null || did.isEmpty()) continue;
JsonNode mats = dn.path("materials");
if (mats.isMissingNode() || mats.isNull()) mats = dn.path("material");
if (mats.isArray()) {
for (JsonNode mn : mats) {
String mid = optText(mn, "materialId");
if (mid != null && !mid.isEmpty()) {
map.put(did, mid);
break;
}
}
} else if (mats.isObject()) {
String mid = optText(mats, "materialId");
if (mid != null && !mid.isEmpty()) {
map.put(did, mid);
} else {
issues.add("材料缺少materialId: device=" + did);
}
}
}
return map;
}
private Map<String, String> buildDeviceTypeMap(JsonNode root) {
Map<String, String> map = new HashMap<>();
JsonNode devicesNode = root.path("devices");
if (!devicesNode.isArray()) return map;
for (JsonNode dn : devicesNode) {
String did = optText(dn, "deviceId");
String tp = optText(dn, "type");
if (did != null && !did.isEmpty() && tp != null && !tp.isEmpty()) {
map.put(did, tp);
}
}
return map;
}
private void overrideWithEvents(Map<String, Object> state, String entityType, String entityId, long t, Map<String, Object> providers) {
String prefix = entityType + ":" + entityId + ":";
for (String key : providers.keySet()) {
if (!key.startsWith(prefix)) continue;
String prop = key.substring(prefix.length());
double val = readValue(entityType, entityId, prop, t, Map.of(), Map.of(), providers);
state.put(prop, val);
}
}
private Map<String, Map<String, Double>> buildMaterialStaticFromDb(Map<String, String> devToMat) {
Map<String, Map<String, Double>> out = new HashMap<>();
if (devToMat == null || devToMat.isEmpty()) return out;
Set<String> mids = new HashSet<>(devToMat.values());
if (mids.isEmpty()) return out;
List<Material> mats = materialService.list(new QueryWrapper<Material>().in("material_id", mids));
Map<String, Material> mm = new HashMap<>();
for (Material m : mats) mm.put(m.getMaterialId(), m);
for (String mid : mids) {
Material m = mm.get(mid);
if (m == null) continue;
Map<String, Double> s = new HashMap<>();
if (m.getUConcentration() != null) s.put("u_concentration", toDouble(m.getUConcentration()));
if (m.getUo2Density() != null) s.put("uo2_density", toDouble(m.getUo2Density()));
if (m.getUEnrichment() != null) s.put("u_enrichment", toDouble(m.getUEnrichment()));
if (m.getPuConcentration() != null) s.put("pu_concentration", toDouble(m.getPuConcentration()));
if (m.getPuo2Density() != null) s.put("puo2_density", toDouble(m.getPuo2Density()));
if (m.getPuIsotope() != null) s.put("pu_isotope", toDouble(m.getPuIsotope()));
if (m.getHno3Acidity() != null) s.put("hno3_acidity", toDouble(m.getHno3Acidity()));
if (m.getH2c2o4Concentration() != null) s.put("h2c2o4_concentration", toDouble(m.getH2c2o4Concentration()));
if (m.getOrganicRatio() != null) s.put("organic_ratio", toDouble(m.getOrganicRatio()));
if (m.getMoistureContent() != null) s.put("moisture_content", toDouble(m.getMoistureContent()));
out.put(mid, s);
}
return out;
}
private long getLongParam(Map<String, Object> params, String key, long def) {
if (params == null) return def;
Object v = params.get(key);