细化接口方法、注释等

This commit is contained in:
wanxiaoli 2025-12-18 17:16:24 +08:00
parent 719f02becd
commit 7499a6e295
17 changed files with 959 additions and 63 deletions

View File

@ -66,6 +66,18 @@
<classifier>plain</classifier>
</dependency>
<!-- Apache POI 用于 Excel 导入 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<!-- Lombok -->
<dependency>

View File

@ -18,6 +18,10 @@ public class CriticalDataController {
/**
* 1. 新增临界数据
* 输入参数请求体中的临界数据对象
* 输出参数布尔值表示是否新增成功
* @param data 临界数据对象
* @return 是否新增成功
*/
@PostMapping
public boolean create(@RequestBody CriticalData data) {
@ -26,6 +30,10 @@ public class CriticalDataController {
/**
* 2. 修改临界数据
* 输入参数请求体中的临界数据对象需包含主键
* 输出参数布尔值表示是否修改成功
* @param data 临界数据对象
* @return 是否修改成功
*/
@PutMapping
public boolean update(@RequestBody CriticalData data) {
@ -34,6 +42,10 @@ public class CriticalDataController {
/**
* 3.1 删除临界数据单条
* 输入参数路径参数临界数据ID
* 输出参数布尔值表示是否删除成功
* @param id 临界数据ID
* @return 是否删除成功
*/
@DeleteMapping("/{id}")
public boolean delete(@PathVariable String id) {
@ -42,6 +54,10 @@ public class CriticalDataController {
/**
* 3.2 删除临界数据多条
* 输入参数请求体中的临界数据ID列表
* 输出参数布尔值表示是否删除成功
* @param ids 临界数据ID列表
* @return 是否删除成功
*/
@DeleteMapping
public boolean deleteBatch(@RequestBody List<String> ids) {
@ -49,23 +65,27 @@ public class CriticalDataController {
}
/**
* 4. 导入临界数据
* 4. 导入临界数据Excel/CSV
* 输入参数表单文件字段 file
* 模板表头device_type, diameter, height, fissile_concentration, isotopic_abundance, keff_value, extra_features
* 采集规则仅采集上述字段表头中多余字段不予采集extra_features 校验为合法 JSON非法或占位符将置为 null
* 输出参数布尔值表示是否导入成功
* @param file Excel/CSV 文件
* @return 是否导入成功
*/
@PostMapping("/import")
public boolean importCriticalData(@RequestParam("file") MultipartFile file) {
return criticalDataService.importCriticalData(file);
}
/**
* 5. 获取设备类型列表去重
*/
@GetMapping("/device-types")
public List<String> listDeviceTypes() {
return criticalDataService.listDeviceTypes();
}
/**
* 6. 根据设备类型获取临界数据列表
* 输入参数查询参数 deviceType设备类型
* 输出参数临界数据列表按创建时间倒序
* @param deviceType 设备类型
* @return 临界数据列表
*/
@GetMapping("/by-device-type")
public List<CriticalData> listByDeviceType(@RequestParam String deviceType) {

View File

@ -18,6 +18,10 @@ public class DeviceController {
/**
* 1. 新增设备
* 输入参数请求体中的设备对象
* 输出参数布尔值表示是否新增成功
* @param device 设备对象
* @return 是否新增成功
*/
@PostMapping
public boolean create(@RequestBody Device device) {
@ -26,6 +30,10 @@ public class DeviceController {
/**
* 2. 编辑设备
* 输入参数请求体中的设备对象需包含主键
* 输出参数布尔值表示是否修改成功
* @param device 设备对象
* @return 是否修改成功
*/
@PutMapping
public boolean update(@RequestBody Device device) {
@ -34,6 +42,10 @@ public class DeviceController {
/**
* 3.1 删除设备单条
* 输入参数路径参数设备ID
* 输出参数布尔值表示是否删除成功
* @param id 设备ID
* @return 是否删除成功
*/
@DeleteMapping("/{id}")
public boolean delete(@PathVariable String id) {
@ -42,6 +54,10 @@ public class DeviceController {
/**
* 3.2 删除设备多条
* 输入参数请求体中的设备ID列表
* 输出参数布尔值表示是否删除成功
* @param ids 设备ID列表
* @return 是否删除成功
*/
@DeleteMapping
public boolean deleteBatch(@RequestBody List<String> ids) {
@ -49,24 +65,43 @@ public class DeviceController {
}
/**
* 4. 导入设备
* Excel / CSV具体解析逻辑在 Service
* 4. 导入设备Excel/CSV
* 输入参数表单文件字段 file
* 模板表头type, code, name, size, volume, flow_rate, pulse_velocity
* 采集规则仅采集上述字段表头中多余字段不予采集size 校验为合法 JSON非法或占位符将置为 null导入记录的 project_id 统一写入 -1
* 输出参数布尔值表示是否导入成功
* @param file Excel/CSV 文件
* @return 是否导入成功
*/
@PostMapping("/import")
public boolean importDevices(@RequestParam("file") MultipartFile file) {
return deviceService.importDevices(file);
}
/**
* 5. 获取设备类型列表去重
* 5. 根据项目ID获取设备列表
* 输入参数查询参数 projectId项目ID
* 输出参数设备列表按创建时间倒序
* @param projectId 项目ID
* @return 设备列表
*/
@GetMapping("/types")
public List<String> listDeviceTypes() {
return deviceService.listDeviceTypes();
@GetMapping("/by-project")
public List<Device> listByProject(@RequestParam String projectId) {
return deviceService.list(
new QueryWrapper<Device>()
.eq("project_id", projectId)
.orderByDesc("created_at")
);
}
/**
* 6. 根据设备类型获取设备列表
* 输入参数查询参数 type设备类型
* 输出参数设备列表按创建时间倒序
* @param type 设备类型
* @return 设备列表
*/
@GetMapping("/by-type")
public List<Device> listByType(@RequestParam String type) {
@ -79,6 +114,11 @@ public class DeviceController {
/**
* 7. 设备查询类型 + 名称
* 输入参数查询参数 type可选name可选
* 输出参数设备列表按创建时间倒序
* @param type 设备类型可选
* @param name 设备名称关键词可选
* @return 设备列表
*/
@GetMapping("/search")
public List<Device> search(@RequestParam(required = false) String type,

View File

@ -5,7 +5,9 @@ import com.yfd.business.css.service.EventService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.util.Map;
import java.util.List;
@RestController
@RequestMapping("/events")
@ -57,4 +59,43 @@ public class EventController {
"data", updatedEvent
));
}
/**
* 根据情景ID获取始发事件列表
* 输入参数查询参数 scenarioId情景ID
* 输出参数事件列表按创建时间倒序
* @param scenarioId 情景ID
* @return 始发事件列表
*/
@GetMapping("/by-scenario")
public List<Event> listByScenario(@RequestParam String scenarioId) {
return eventService.list(
new QueryWrapper<Event>()
.eq("scenario_id", scenarioId)
.orderByDesc("created_at")
);
}
/**
* 删除始发事件
* 输入参数路径参数 eventId事件ID
* 输出参数标准响应结构表示删除结果
* @param eventId 事件ID
* @return 删除结果
*/
@DeleteMapping("/{eventId}")
public ResponseEntity<Map<String, Object>> deleteEvent(@PathVariable String eventId) {
boolean ok = eventService.removeById(eventId);
if (ok) {
return ResponseEntity.ok(Map.of(
"code", 0,
"msg", "删除成功"
));
} else {
return ResponseEntity.badRequest().body(Map.of(
"code", 1,
"msg", "删除失败"
));
}
}
}

View File

@ -18,6 +18,10 @@ public class MaterialController {
/**
* 1. 新增物料
* 输入参数请求体中的物料对象
* 输出参数布尔值表示是否新增成功
* @param material 物料对象
* @return 是否新增成功
*/
@PostMapping
public boolean create(@RequestBody Material material) {
@ -26,6 +30,10 @@ public class MaterialController {
/**
* 2. 编辑物料
* 输入参数请求体中的物料对象需包含主键
* 输出参数布尔值表示是否修改成功
* @param material 物料对象
* @return 是否修改成功
*/
@PutMapping
public boolean update(@RequestBody Material material) {
@ -34,6 +42,10 @@ public class MaterialController {
/**
* 3.1 删除物料单条
* 输入参数路径参数物料ID
* 输出参数布尔值表示是否删除成功
* @param id 物料ID
* @return 是否删除成功
*/
@DeleteMapping("/{id}")
public boolean delete(@PathVariable String id) {
@ -42,6 +54,10 @@ public class MaterialController {
/**
* 3.2 删除物料多条
* 输入参数请求体中的物料ID列表
* 输出参数布尔值表示是否删除成功
* @param ids 物料ID列表
* @return 是否删除成功
*/
@DeleteMapping
public boolean deleteBatch(@RequestBody List<String> ids) {
@ -49,7 +65,13 @@ public class MaterialController {
}
/**
* 4. 导入物料
* 4. 导入物料Excel/CSV
* 输入参数表单文件字段 file
* 模板表头name, u_concentration, uo2_density, u_enrichment, pu_concentration, puo2_density, pu_isotope, hno3_acidity, h2c2o4_concentration, organic_ratio, moisture_content, custom_attrs
* 采集规则仅采集上述字段表头中多余字段不予采集custom_attrs 校验为合法 JSON非法或占位符-/将置为 null导入记录的 project_id 统一写入 -1
* 输出参数布尔值表示是否导入成功
* @param file Excel/CSV 文件
* @return 是否导入成功
*/
@PostMapping("/import")
public boolean importMaterials(@RequestParam("file") MultipartFile file) {
@ -58,6 +80,10 @@ public class MaterialController {
/**
* 5. 根据物料名称搜索
* 输入参数查询参数 name物料名称关键词
* 输出参数物料列表按创建时间倒序
* @param name 物料名称关键词
* @return 物料列表
*/
@GetMapping("/search")
public List<Material> search(@RequestParam String name) {

View File

@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/projects")
@ -20,6 +21,10 @@ public class ProjectController {
/**
* 1. 新增项目
* 输入参数请求体中的项目对象
* 输出参数布尔值表示是否新增成功
* @param project 项目对象
* @return 是否新增成功
*/
@PostMapping
public boolean create(@RequestBody Project project) {
@ -28,6 +33,10 @@ public class ProjectController {
/**
* 2. 修改项目
* 输入参数请求体中的项目对象需包含主键
* 输出参数布尔值表示是否修改成功
* @param project 项目对象
* @return 是否修改成功
*/
@PutMapping
public boolean update(@RequestBody Project project) {
@ -36,6 +45,10 @@ public class ProjectController {
/**
* 3.1 删除项目单条
* 输入参数路径参数项目ID
* 输出参数布尔值表示是否删除成功
* @param id 项目ID
* @return 是否删除成功
*/
@DeleteMapping("/{id}")
public boolean delete(@PathVariable String id) {
@ -44,6 +57,10 @@ public class ProjectController {
/**
* 3.2 删除项目多条
* 输入参数请求体中的项目ID列表
* 输出参数布尔值表示是否删除成功
* @param ids 项目ID列表
* @return 是否删除成功
*/
@DeleteMapping
public boolean deleteBatch(@RequestBody List<String> ids) {
@ -52,6 +69,10 @@ public class ProjectController {
/**
* 4. 导出所有项目
* 输入参数
* 导出描述返回所有项目的 JSON 数组作为附件 `projects.json`
* 输出参数附件字节流application/octet-stream
* @return 附件响应文件名为 projects.json
*/
@GetMapping("/export/all")
public ResponseEntity<byte[]> exportAll() {
@ -66,6 +87,17 @@ public class ProjectController {
/**
* 5. 导出项目工程
* 输入参数路径参数项目ID
* 导出描述返回 ZIP 附件 `project_{id}.zip`包含以下 JSON 文件
* - project.json项目对象
* - devices.json设备列表
* - materials.json物料列表
* - scenarios.json情景列表
* - events.json事件列表
* - scenario-results.json情景结果列表
* 输出参数附件字节流application/octet-stream
* @param id 项目ID
* @return 附件响应文件名为 project_{id}.zip
*/
@GetMapping("/{id}/export")
public ResponseEntity<byte[]> exportProject(@PathVariable String id) {
@ -80,6 +112,10 @@ public class ProjectController {
/**
* 6. 根据项目名称搜索
* 输入参数查询参数 name项目名称关键词
* 输出参数项目列表
* @param name 项目名称关键词
* @return 匹配的项目列表
*/
@GetMapping("/search")
public List<Project> searchByName(@RequestParam String name) {
@ -89,4 +125,47 @@ public class ProjectController {
.orderByDesc("created_at")
);
}
/**
* 7. 根据项目ID获取项目信息
* 输入参数路径参数项目ID
* 输出参数项目对象不存在时为空
* @param id 项目ID
* @return 项目对象
*/
@GetMapping("/{id}")
public Project getById(@PathVariable String id) {
return projectService.getById(id);
}
/**
* 8. 更新指定项目的拓扑结构
* 输入参数路径参数项目ID请求体 Map 中的 topology 字段
* 输出参数标准响应结构包含更新后的项目主键与拓扑字段
* @param id 项目ID
* @param requestBody 请求体需包含 topology 字段
* @return 标准响应结构
*/
@PutMapping("/{id}/topology")
public ResponseEntity<Map<String, Object>> updateTopology(@PathVariable String id,
@RequestBody Map<String, Object> requestBody) {
Object topology = requestBody.get("topology");
if (topology == null) {
return ResponseEntity.badRequest().body(Map.of(
"code", 1,
"msg", "topology不能为空"
));
}
Project updated = new Project();
updated.setProjectId(id);
updated.setTopology(String.valueOf(topology));
projectService.updateById(updated);
return ResponseEntity.ok(Map.of(
"code", 0,
"msg", "修改成功",
"data", updated
));
}
}

View File

@ -17,6 +17,10 @@ public class ScenarioController {
/**
* 1. 新增情景
* 输入参数请求体中的情景对象
* 输出参数布尔值表示是否新增成功
* @param scenario 情景对象
* @return 是否新增成功
*/
@PostMapping
public boolean create(@RequestBody Scenario scenario) {
@ -25,6 +29,10 @@ public class ScenarioController {
/**
* 2. 修改情景
* 输入参数请求体中的情景对象需包含主键
* 输出参数布尔值表示是否修改成功
* @param scenario 情景对象
* @return 是否修改成功
*/
@PutMapping
public boolean update(@RequestBody Scenario scenario) {
@ -32,7 +40,11 @@ public class ScenarioController {
}
/**
* 3. 删除情景
* 3.1 删除情景单条
* 输入参数路径参数情景ID
* 输出参数布尔值表示是否删除成功
* @param id 情景ID
* @return 是否删除成功
*/
@DeleteMapping("/{id}")
public boolean delete(@PathVariable String id) {
@ -41,6 +53,10 @@ public class ScenarioController {
/**
* 3.2 删除情景多条
* 输入参数请求体中的情景ID列表
* 输出参数布尔值表示是否删除成功
* @param ids 情景ID列表
* @return 是否删除成功
*/
@DeleteMapping
public boolean deleteBatch(@RequestBody List<String> ids) {
@ -49,6 +65,10 @@ public class ScenarioController {
/**
* 4. 根据情景名称搜索
* 输入参数查询参数 name情景名称关键词
* 输出参数情景列表按创建时间倒序
* @param name 情景名称关键词
* @return 情景列表
*/
@GetMapping("/search")
public List<Scenario> searchByName(@RequestParam String name) {
@ -58,4 +78,32 @@ public class ScenarioController {
.orderByDesc("created_at")
);
}
/**
* 5. 根据情景ID获取情景记录
* 输入参数路径参数情景ID
* 输出参数情景对象不存在时为空
* @param id 情景ID
* @return 情景对象
*/
@GetMapping("/{id}")
public Scenario getById(@PathVariable String id) {
return scenarioService.getById(id);
}
/**
* 6. 根据项目ID查询情景列表
* 输入参数查询参数 projectId项目ID
* 输出参数情景列表按创建时间倒序
* @param projectId 项目ID
* @return 情景列表
*/
@GetMapping("/by-project")
public List<Scenario> listByProject(@RequestParam String projectId) {
return scenarioService.list(
new QueryWrapper<Scenario>()
.eq("project_id", projectId)
.orderByDesc("created_at")
);
}
}

View File

@ -5,6 +5,8 @@ import com.yfd.business.css.service.ScenarioResultService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.util.List;
import java.util.Map;
@RestController
@ -30,4 +32,23 @@ public class ScenarioResultController {
"data", savedResult
));
}
/**
* 根据情景ID与设备ID获取情景结果列表
* 输入参数查询参数 scenarioId情景IDdeviceId设备ID
* 输出参数情景结果列表按时间点 step 排序
* @param scenarioId 情景ID
* @param deviceId 设备ID
* @return 情景结果列表
*/
@GetMapping("/by-scenario")
public List<ScenarioResult> listByScenario(@RequestParam String scenarioId,
@RequestParam String deviceId) {
return scenarioResultService.list(
new QueryWrapper<ScenarioResult>()
.eq("scenario_id", scenarioId)
.eq("device_id", deviceId)
.orderByAsc("step")
);
}
}

View File

@ -8,6 +8,7 @@ import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.math.BigDecimal;
@Data
@TableName("critical_data")
@ -21,11 +22,23 @@ public class CriticalData implements Serializable {
@TableField("device_type")
private String deviceType;
@TableField("attr_state")
private String attrState; // JSON
@TableField("diameter")
private BigDecimal diameter;
@TableField("height")
private BigDecimal height;
@TableField("fissile_concentration")
private BigDecimal fissileConcentration;
@TableField("isotopic_abundance")
private BigDecimal isotopicAbundance;
@TableField("extra_features")
private String extraFeatures;
@TableField("keff_value")
private Double keffValue;
private BigDecimal keffValue;
@TableField("created_at")
private LocalDateTime createdAt;

View File

@ -8,6 +8,7 @@ import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.math.BigDecimal;
@Data
@TableName("material")
@ -25,34 +26,34 @@ public class Material implements Serializable {
private String name;
@TableField("u_concentration")
private Double uConcentration;
private BigDecimal uConcentration;
@TableField("uo2_density")
private Double uo2Density;
private BigDecimal uo2Density;
@TableField("u_enrichment")
private Double uEnrichment;
private BigDecimal uEnrichment;
@TableField("pu_concentration")
private Double puConcentration;
private BigDecimal puConcentration;
@TableField("puo2_density")
private Double puo2Density;
private BigDecimal puo2Density;
@TableField("pu_isotope")
private Double puIsotope;
private BigDecimal puIsotope;
@TableField("hno3_acidity")
private Double hno3Acidity;
private BigDecimal hno3Acidity;
@TableField("h2c2o4_concentration")
private Double h2c2o4Concentration;
private BigDecimal h2c2o4Concentration;
@TableField("organic_ratio")
private Double organicRatio;
private BigDecimal organicRatio;
@TableField("moisture_content")
private Double moistureContent;
private BigDecimal moistureContent;
@TableField("custom_attrs")
private String customAttrs; // JSON

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@TableName("scenario_result")
@ -25,5 +26,5 @@ public class ScenarioResult implements Serializable {
private String attrState; // JSON
@TableField("keff_value")
private Double keffValue;
private BigDecimal keffValue;
}

View File

@ -11,8 +11,5 @@ public interface CriticalDataService extends IService<CriticalData> {
*/
boolean importCriticalData(MultipartFile file);
/**
* 获取设备类型列表去重
*/
List<String> listDeviceTypes();
}

View File

@ -3,15 +3,9 @@ package com.yfd.business.css.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yfd.business.css.domain.Device;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
public interface DeviceService extends IService<Device> {
/**
* 导入设备
*/
boolean importDevices(MultipartFile file);
/**
* 获取设备类型列表去重
*/
List<String> listDeviceTypes();
}

View File

@ -7,25 +7,193 @@ import com.yfd.business.css.service.CriticalDataService;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.annotation.Resource;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@Service
public class CriticalDataServiceImpl
extends ServiceImpl<CriticalDataMapper, CriticalData>
implements CriticalDataService {
@Resource
private ObjectMapper objectMapper;
@Override
public boolean importCriticalData(MultipartFile file) {
try {
String name = file.getOriginalFilename();
if (name == null) return false;
String lower = name.toLowerCase();
if (lower.endsWith(".xlsx")) {
return importExcel(new XSSFWorkbook(file.getInputStream()));
} else if (lower.endsWith(".xls")) {
return importExcel(new HSSFWorkbook(file.getInputStream()));
} else if (lower.endsWith(".csv")) {
return importCsv(file.getInputStream());
} else {
return false;
}
} catch (Exception e) {
return false;
}
}
@Override
public List<String> listDeviceTypes() {
return this.list()
.stream()
.map(CriticalData::getDeviceType)
.filter(s -> s != null && !s.isEmpty())
.distinct()
.collect(Collectors.toList());
private boolean importExcel(Workbook workbook) {
try (Workbook wb = workbook) {
Sheet sheet = wb.getSheetAt(0);
if (sheet == null) return false;
Row headerRow = sheet.getRow(0);
if (headerRow == null) return false;
Map<String, Integer> idx = new HashMap<>();
for (int i = 0; i < headerRow.getLastCellNum(); i++) {
Cell c = headerRow.getCell(i);
if (c == null) continue;
String key = c.getStringCellValue();
if (key != null) idx.put(key.trim().toLowerCase(), i);
}
String[] keys = new String[]{
"device_type","diameter","height",
"fissile_concentration","isotopic_abundance",
"keff_value","extra_features"
};
for (String k : keys) if (!idx.containsKey(k)) return false;
List<CriticalData> list = new ArrayList<>();
for (int r = 1; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) continue;
CriticalData cd = new CriticalData();
cd.setDeviceType(cleanString(getString(row, idx.get("device_type"))));
cd.setDiameter(cleanDecimal(getDecimal(row, idx.get("diameter"))));
cd.setHeight(cleanDecimal(getDecimal(row, idx.get("height"))));
cd.setFissileConcentration(cleanDecimal(getDecimal(row, idx.get("fissile_concentration"))));
cd.setIsotopicAbundance(cleanDecimal(getDecimal(row, idx.get("isotopic_abundance"))));
cd.setKeffValue(cleanDecimal(getDecimal(row, idx.get("keff_value"))));
cd.setExtraFeatures(validateJson(cleanString(getString(row, idx.get("extra_features")))));
cd.setCreatedAt(LocalDateTime.now());
cd.setUpdatedAt(LocalDateTime.now());
if (isEmpty(cd)) continue;
list.add(cd);
}
if (list.isEmpty()) return false;
return this.saveBatch(list);
} catch (Exception e) {
return false;
}
}
private boolean importCsv(InputStream is) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
String header = br.readLine();
if (header == null) return false;
String[] hs = header.split(",");
Map<String, Integer> idx = new HashMap<>();
for (int i = 0; i < hs.length; i++) idx.put(hs[i].trim().toLowerCase(), i);
String[] keys = new String[]{
"device_type","diameter","height",
"fissile_concentration","isotopic_abundance",
"keff_value","extra_features"
};
for (String k : keys) if (!idx.containsKey(k)) return false;
List<CriticalData> list = new ArrayList<>();
String line;
while ((line = br.readLine()) != null) {
String[] cols = line.split(",");
CriticalData cd = new CriticalData();
cd.setDeviceType(cleanString(get(cols, idx.get("device_type"))));
cd.setDiameter(cleanDecimal(parseDecimal(get(cols, idx.get("diameter")))));
cd.setHeight(cleanDecimal(parseDecimal(get(cols, idx.get("height")))));
cd.setFissileConcentration(cleanDecimal(parseDecimal(get(cols, idx.get("fissile_concentration")))));
cd.setIsotopicAbundance(cleanDecimal(parseDecimal(get(cols, idx.get("isotopic_abundance")))));
cd.setKeffValue(cleanDecimal(parseDecimal(get(cols, idx.get("keff_value")))));
cd.setExtraFeatures(validateJson(cleanString(get(cols, idx.get("extra_features")))));
cd.setCreatedAt(LocalDateTime.now());
cd.setUpdatedAt(LocalDateTime.now());
if (isEmpty(cd)) continue;
list.add(cd);
}
if (list.isEmpty()) return false;
return this.saveBatch(list);
} catch (Exception e) {
return false;
}
}
private String getString(Row row, int i) {
Cell c = row.getCell(i);
if (c == null) return null;
switch (c.getCellType()) {
case STRING: return c.getStringCellValue();
case NUMERIC: return String.valueOf(c.getNumericCellValue());
case BOOLEAN: return String.valueOf(c.getBooleanCellValue());
default: return null;
}
}
private BigDecimal getDecimal(Row row, int i) {
Cell c = row.getCell(i);
if (c == null) return null;
switch (c.getCellType()) {
case NUMERIC: return BigDecimal.valueOf(c.getNumericCellValue());
case STRING:
try { return new BigDecimal(c.getStringCellValue().trim()); } catch (Exception e) { return null; }
default: return null;
}
}
private String get(String[] cols, int i) {
if (i < 0 || i >= cols.length) return null;
String v = cols[i];
if (v == null) return null;
return v.trim();
}
private BigDecimal parseDecimal(String s) {
if (s == null || s.isEmpty()) return null;
try { return new BigDecimal(s.trim()); } catch (Exception e) { return null; }
}
private boolean isPlaceholder(String s) {
if (s == null) return false;
String t = s.trim();
return "-".equals(t) || "".equals(t) || "/".equals(t) || "".equals(t);
}
private String cleanString(String s) {
return isPlaceholder(s) ? null : s;
}
private BigDecimal cleanDecimal(BigDecimal d) {
return d;
}
private boolean isEmpty(CriticalData cd) {
return cd.getDeviceType() == null || cd.getDeviceType().isEmpty();
}
private String validateJson(String s) {
if (s == null || s.isEmpty()) return null;
try {
JsonNode node = objectMapper.readTree(s);
return objectMapper.writeValueAsString(node);
} catch (Exception e) {
return null;
}
}
}

View File

@ -7,25 +7,172 @@ import com.yfd.business.css.service.DeviceService;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.annotation.Resource;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@Service
public class DeviceServiceImpl
extends ServiceImpl<DeviceMapper, Device>
implements DeviceService {
@Resource
private ObjectMapper objectMapper;
@Override
public boolean importDevices(MultipartFile file) {
try {
String name = file.getOriginalFilename();
if (name == null) return false;
String lower = name.toLowerCase();
if (lower.endsWith(".xlsx")) {
return importExcel(new XSSFWorkbook(file.getInputStream()));
} else if (lower.endsWith(".xls")) {
return importExcel(new HSSFWorkbook(file.getInputStream()));
} else if (lower.endsWith(".csv")) {
return importCsv(file.getInputStream());
} else {
return false;
}
} catch (Exception e) {
return false;
}
}
@Override
public List<String> listDeviceTypes() {
return this.list()
.stream()
.map(Device::getType)
.filter(s -> s != null && !s.isEmpty())
.distinct()
.collect(Collectors.toList());
private boolean importExcel(Workbook workbook) {
try (Workbook wb = workbook) {
Sheet sheet = wb.getSheetAt(0);
if (sheet == null) return false;
Row headerRow = sheet.getRow(0);
if (headerRow == null) return false;
Map<String, Integer> idx = new HashMap<>();
for (int i = 0; i < headerRow.getLastCellNum(); i++) {
Cell c = headerRow.getCell(i);
if (c == null) continue;
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"};
for (String k : keys) {
if (!idx.containsKey(k)) return false;
}
List<Device> devices = new ArrayList<>();
for (int r = 1; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) continue;
Device d = new Device();
d.setType(getString(row, idx.get("type")));
d.setCode(getString(row, idx.get("code")));
d.setName(getString(row, idx.get("name")));
d.setSize(validateJson(getString(row, idx.get("size"))));
d.setVolume(getDouble(row, idx.get("volume")));
d.setFlowRate(getDouble(row, idx.get("flow_rate")));
d.setPulseVelocity(getDouble(row, idx.get("pulse_velocity")));
d.setProjectId("-1");
d.setCreatedAt(LocalDateTime.now());
d.setUpdatedAt(LocalDateTime.now());
if (isEmpty(d)) continue;
devices.add(d);
}
if (devices.isEmpty()) return false;
return this.saveBatch(devices);
} catch (Exception e) {
return false;
}
}
private boolean importCsv(InputStream is) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
String header = br.readLine();
if (header == null) return false;
String[] hs = header.split(",");
Map<String, Integer> idx = new HashMap<>();
for (int i = 0; i < hs.length; i++) idx.put(hs[i].trim().toLowerCase(), i);
String[] keys = new String[]{"type","code","name","size","volume","flow_rate","pulse_velocity"};
for (String k : keys) if (!idx.containsKey(k)) return false;
List<Device> devices = new ArrayList<>();
String line;
while ((line = br.readLine()) != null) {
String[] cols = line.split(",");
Device d = new Device();
d.setType(get(cols, idx.get("type")));
d.setCode(get(cols, idx.get("code")));
d.setName(get(cols, idx.get("name")));
d.setSize(validateJson(get(cols, idx.get("size"))));
d.setVolume(parseDouble(get(cols, idx.get("volume"))));
d.setFlowRate(parseDouble(get(cols, idx.get("flow_rate"))));
d.setPulseVelocity(parseDouble(get(cols, idx.get("pulse_velocity"))));
d.setProjectId("-1");
d.setCreatedAt(LocalDateTime.now());
d.setUpdatedAt(LocalDateTime.now());
if (isEmpty(d)) continue;
devices.add(d);
}
if (devices.isEmpty()) return false;
return this.saveBatch(devices);
} catch (Exception e) {
return false;
}
}
private String getString(Row row, int i) {
Cell c = row.getCell(i);
if (c == null) return null;
switch (c.getCellType()) {
case STRING: return c.getStringCellValue();
case NUMERIC: return String.valueOf(c.getNumericCellValue());
case BOOLEAN: return String.valueOf(c.getBooleanCellValue());
default: return null;
}
}
private Double getDouble(Row row, int i) {
Cell c = row.getCell(i);
if (c == null) return null;
switch (c.getCellType()) {
case NUMERIC: return c.getNumericCellValue();
case STRING:
try { return Double.parseDouble(c.getStringCellValue()); } catch (Exception e) { return null; }
default: return null;
}
}
private String get(String[] cols, int i) {
if (i < 0 || i >= cols.length) return null;
String v = cols[i];
if (v == null) return null;
return v.trim();
}
private Double parseDouble(String s) {
if (s == null || s.isEmpty()) return null;
try { return Double.parseDouble(s); } catch (Exception e) { return null; }
}
private boolean isEmpty(Device d) {
return (d.getCode() == null || d.getCode().isEmpty()) && (d.getName() == null || d.getName().isEmpty());
}
private String validateJson(String s) {
if (s == null || s.isEmpty()) return null;
try {
JsonNode node = objectMapper.readTree(s);
return objectMapper.writeValueAsString(node);
} catch (Exception e) {
return null;
}
}
}

View File

@ -7,12 +7,207 @@ import com.yfd.business.css.service.MaterialService;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.annotation.Resource;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class MaterialServiceImpl
extends ServiceImpl<MaterialMapper, Material>
implements MaterialService {
@Resource
private ObjectMapper objectMapper;
@Override
public boolean importMaterials(MultipartFile file) {
try {
String name = file.getOriginalFilename();
if (name == null) return false;
String lower = name.toLowerCase();
if (lower.endsWith(".xlsx")) {
return importExcel(new XSSFWorkbook(file.getInputStream()));
} else if (lower.endsWith(".xls")) {
return importExcel(new HSSFWorkbook(file.getInputStream()));
} else if (lower.endsWith(".csv")) {
return importCsv(file.getInputStream());
} else {
return false;
}
} catch (Exception e) {
return false;
}
}
private boolean importExcel(Workbook workbook) {
try (Workbook wb = workbook) {
Sheet sheet = wb.getSheetAt(0);
if (sheet == null) return false;
Row headerRow = sheet.getRow(0);
if (headerRow == null) return false;
Map<String, Integer> idx = new HashMap<>();
for (int i = 0; i < headerRow.getLastCellNum(); i++) {
Cell c = headerRow.getCell(i);
if (c == null) continue;
String key = c.getStringCellValue();
if (key != null) idx.put(key.trim().toLowerCase(), i);
}
String[] keys = new String[]{
"name","u_concentration","uo2_density","u_enrichment",
"pu_concentration","puo2_density","pu_isotope",
"hno3_acidity","h2c2o4_concentration","organic_ratio",
"moisture_content","custom_attrs"
};
for (String k : keys) {
if (!idx.containsKey(k)) return false;
}
List<Material> list = new ArrayList<>();
for (int r = 1; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) continue;
Material m = new Material();
m.setName(cleanString(getString(row, idx.get("name"))));
m.setUConcentration(cleanDecimal(getDecimal(row, idx.get("u_concentration"))));
m.setUo2Density(cleanDecimal(getDecimal(row, idx.get("uo2_density"))));
m.setUEnrichment(cleanDecimal(getDecimal(row, idx.get("u_enrichment"))));
m.setPuConcentration(cleanDecimal(getDecimal(row, idx.get("pu_concentration"))));
m.setPuo2Density(cleanDecimal(getDecimal(row, idx.get("puo2_density"))));
m.setPuIsotope(cleanDecimal(getDecimal(row, idx.get("pu_isotope"))));
m.setHno3Acidity(cleanDecimal(getDecimal(row, idx.get("hno3_acidity"))));
m.setH2c2o4Concentration(cleanDecimal(getDecimal(row, idx.get("h2c2o4_concentration"))));
m.setOrganicRatio(cleanDecimal(getDecimal(row, idx.get("organic_ratio"))));
m.setMoistureContent(cleanDecimal(getDecimal(row, idx.get("moisture_content"))));
m.setCustomAttrs(validateJson(cleanString(getString(row, idx.get("custom_attrs")))));
m.setProjectId("-1");
m.setCreatedAt(LocalDateTime.now());
m.setUpdatedAt(LocalDateTime.now());
if (isEmpty(m)) continue;
list.add(m);
}
if (list.isEmpty()) return false;
return this.saveBatch(list);
} catch (Exception e) {
return false;
}
}
private boolean importCsv(InputStream is) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
String header = br.readLine();
if (header == null) return false;
String[] hs = header.split(",");
Map<String, Integer> idx = new HashMap<>();
for (int i = 0; i < hs.length; i++) idx.put(hs[i].trim().toLowerCase(), i);
String[] keys = new String[]{
"name","u_concentration","uo2_density","u_enrichment",
"pu_concentration","puo2_density","pu_isotope",
"hno3_acidity","h2c2o4_concentration","organic_ratio",
"moisture_content","custom_attrs"
};
for (String k : keys) if (!idx.containsKey(k)) return false;
List<Material> list = new ArrayList<>();
String line;
while ((line = br.readLine()) != null) {
String[] cols = line.split(",");
Material m = new Material();
m.setName(cleanString(get(cols, idx.get("name"))));
m.setUConcentration(cleanDecimal(parseDecimal(get(cols, idx.get("u_concentration")))));
m.setUo2Density(cleanDecimal(parseDecimal(get(cols, idx.get("uo2_density")))));
m.setUEnrichment(cleanDecimal(parseDecimal(get(cols, idx.get("u_enrichment")))));
m.setPuConcentration(cleanDecimal(parseDecimal(get(cols, idx.get("pu_concentration")))));
m.setPuo2Density(cleanDecimal(parseDecimal(get(cols, idx.get("puo2_density")))));
m.setPuIsotope(cleanDecimal(parseDecimal(get(cols, idx.get("pu_isotope")))));
m.setHno3Acidity(cleanDecimal(parseDecimal(get(cols, idx.get("hno3_acidity")))));
m.setH2c2o4Concentration(cleanDecimal(parseDecimal(get(cols, idx.get("h2c2o4_concentration")))));
m.setOrganicRatio(cleanDecimal(parseDecimal(get(cols, idx.get("organic_ratio")))));
m.setMoistureContent(cleanDecimal(parseDecimal(get(cols, idx.get("moisture_content")))));
m.setCustomAttrs(validateJson(cleanString(get(cols, idx.get("custom_attrs")))));
m.setProjectId("-1");
m.setCreatedAt(LocalDateTime.now());
m.setUpdatedAt(LocalDateTime.now());
if (isEmpty(m)) continue;
list.add(m);
}
if (list.isEmpty()) return false;
return this.saveBatch(list);
} catch (Exception e) {
return false;
}
}
private String getString(Row row, int i) {
Cell c = row.getCell(i);
if (c == null) return null;
switch (c.getCellType()) {
case STRING: return c.getStringCellValue();
case NUMERIC: return String.valueOf(c.getNumericCellValue());
case BOOLEAN: return String.valueOf(c.getBooleanCellValue());
default: return null;
}
}
private BigDecimal getDecimal(Row row, int i) {
Cell c = row.getCell(i);
if (c == null) return null;
switch (c.getCellType()) {
case NUMERIC: return BigDecimal.valueOf(c.getNumericCellValue());
case STRING:
try { return new BigDecimal(c.getStringCellValue().trim()); } catch (Exception e) { return null; }
default: return null;
}
}
private String get(String[] cols, int i) {
if (i < 0 || i >= cols.length) return null;
String v = cols[i];
if (v == null) return null;
return v.trim();
}
private BigDecimal parseDecimal(String s) {
if (s == null || s.isEmpty()) return null;
try { return new BigDecimal(s.trim()); } catch (Exception e) { return null; }
}
private boolean isEmpty(Material m) {
return m.getName() == null || m.getName().isEmpty();
}
private boolean isPlaceholder(String s) {
if (s == null) return false;
String t = s.trim();
return "-".equals(t) || "".equals(t) || "/".equals(t) || "".equals(t);
}
private String cleanString(String s) {
return isPlaceholder(s) ? null : s;
}
private BigDecimal cleanDecimal(BigDecimal d) {
return d;
}
private String validateJson(String s) {
if (s == null || s.isEmpty()) return null;
try {
JsonNode node = objectMapper.readTree(s);
return objectMapper.writeValueAsString(node);
} catch (Exception e) {
return null;
}
}
}

View File

@ -5,18 +5,111 @@ import com.yfd.business.css.domain.Project;
import com.yfd.business.css.mapper.ProjectMapper;
import com.yfd.business.css.service.ProjectService;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yfd.business.css.domain.Device;
import com.yfd.business.css.domain.Material;
import com.yfd.business.css.domain.Scenario;
import com.yfd.business.css.domain.Event;
import com.yfd.business.css.domain.ScenarioResult;
import com.yfd.business.css.service.DeviceService;
import com.yfd.business.css.service.MaterialService;
import com.yfd.business.css.service.ScenarioService;
import com.yfd.business.css.service.EventService;
import com.yfd.business.css.service.ScenarioResultService;
import java.util.List;
import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;
import java.io.ByteArrayOutputStream;
@Service
public class ProjectServiceImpl
extends ServiceImpl<ProjectMapper, Project>
implements ProjectService {
@Resource
private ObjectMapper objectMapper;
@Resource
private DeviceService deviceService;
@Resource
private MaterialService materialService;
@Resource
private ScenarioService scenarioService;
@Resource
private EventService eventService;
@Resource
private ScenarioResultService scenarioResultService;
@Override
public byte[] exportAllProjects() {
return new byte[0];
try {
List<Project> projects = this.list();
return objectMapper.writeValueAsBytes(projects);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public byte[] exportProjectEngineering(String projectId) {
return new byte[0];
try {
Project project = this.getById(projectId);
if (project == null) {
throw new IllegalArgumentException("项目不存在: " + projectId);
}
List<Device> devices = deviceService.list(
new QueryWrapper<Device>().eq("project_id", projectId)
);
List<Material> materials = materialService.list(
new QueryWrapper<Material>().eq("project_id", projectId)
);
List<Scenario> scenarios = scenarioService.list(
new QueryWrapper<Scenario>().eq("project_id", projectId)
);
List<String> scenarioIds = scenarios.stream().map(Scenario::getScenarioId).toList();
List<Event> events = scenarioIds.isEmpty()
? List.of()
: eventService.list(new QueryWrapper<Event>().in("scenario_id", scenarioIds));
List<ScenarioResult> results = scenarioIds.isEmpty()
? List.of()
: scenarioResultService.list(new QueryWrapper<ScenarioResult>().in("scenario_id", scenarioIds));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
// 项目信息
zos.putNextEntry(new ZipEntry("project.json"));
zos.write(objectMapper.writeValueAsBytes(project));
zos.closeEntry();
// 设备
zos.putNextEntry(new ZipEntry("devices.json"));
zos.write(objectMapper.writeValueAsBytes(devices));
zos.closeEntry();
// 物料
zos.putNextEntry(new ZipEntry("materials.json"));
zos.write(objectMapper.writeValueAsBytes(materials));
zos.closeEntry();
// 情景
zos.putNextEntry(new ZipEntry("scenarios.json"));
zos.write(objectMapper.writeValueAsBytes(scenarios));
zos.closeEntry();
// 事件
zos.putNextEntry(new ZipEntry("events.json"));
zos.write(objectMapper.writeValueAsBytes(events));
zos.closeEntry();
// 结果
zos.putNextEntry(new ZipEntry("scenario-results.json"));
zos.write(objectMapper.writeValueAsBytes(results));
zos.closeEntry();
}
return baos.toByteArray();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}