|
@@ -1,21 +1,35 @@
|
|
|
package cn.iocoder.yudao.module.museums.service.specimeninfo;
|
|
|
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
|
|
+import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
|
|
+import cn.iocoder.yudao.module.museums.dal.dataobject.specimenoutbound.SpecimenOutboundDO;
|
|
|
+import cn.iocoder.yudao.module.museums.dal.mysql.specimenoutbound.SpecimenOutboundMapper;
|
|
|
+import org.apache.tomcat.util.http.fileupload.FileUtils;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import javax.annotation.Resource;
|
|
|
-import org.springframework.validation.annotation.Validated;
|
|
|
+
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.validation.annotation.Validated;
|
|
|
|
|
|
+import java.io.*;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.nio.file.Files;
|
|
|
import java.util.*;
|
|
|
+import java.util.zip.ZipEntry;
|
|
|
+import java.util.zip.ZipInputStream;
|
|
|
+
|
|
|
import cn.iocoder.yudao.module.museums.controller.admin.specimeninfo.vo.*;
|
|
|
import cn.iocoder.yudao.module.museums.dal.dataobject.specimeninfo.SpecimenInfoDO;
|
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
|
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
|
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
|
|
|
import cn.iocoder.yudao.module.museums.dal.mysql.specimeninfo.SpecimenInfoMapper;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
import static cn.iocoder.yudao.module.museums.enums.ErrorCodeConstants.*;
|
|
|
+import cn.hutool.core.io.IoUtil;
|
|
|
|
|
|
|
|
|
* 标本管理 Service 实现类
|
|
@@ -28,6 +42,18 @@ public class SpecimenInfoServiceImpl implements SpecimenInfoService {
|
|
|
|
|
|
@Resource
|
|
|
private SpecimenInfoMapper specimenInfoMapper;
|
|
|
+ @Resource
|
|
|
+ private SpecimenOutboundMapper specimenOutboundMapper;
|
|
|
+ @Resource
|
|
|
+ private FileApi fileApi;
|
|
|
+
|
|
|
+ public SpecimenInfoServiceImpl(SpecimenInfoMapper specimenInfoMapper, FileApi fileApi) {
|
|
|
+ this.specimenInfoMapper = specimenInfoMapper;
|
|
|
+ this.fileApi = fileApi;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private SpecimenInfoDO specimenInfoDO;
|
|
|
|
|
|
@Override
|
|
|
public Integer createSpecimenInfo(SpecimenInfoSaveReqVO createReqVO) {
|
|
@@ -71,4 +97,246 @@ public class SpecimenInfoServiceImpl implements SpecimenInfoService {
|
|
|
return specimenInfoMapper.selectPage(pageReqVO);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入
|
|
|
+ public SpecimenImportRespVO importSpecimenList(List<SpecimenImportExcelVO> importSpecimens, boolean isUpdateSupport) {
|
|
|
+
|
|
|
+ if (CollUtil.isEmpty(importSpecimens)) {
|
|
|
+ throw exception(SPECIMEN_INFO_LIST_IS_EMPTY);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ SpecimenImportRespVO respVO = SpecimenImportRespVO.builder()
|
|
|
+ .createSpecimenNumbers(new ArrayList<>())
|
|
|
+ .updateSpecimenNumbers(new ArrayList<>())
|
|
|
+ .failureSpecimenNumbers(new LinkedHashMap<>()).build();
|
|
|
+
|
|
|
+ importSpecimens.forEach(importSpecimen -> {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ SpecimenInfoDO existSpecimen = specimenInfoMapper.selectBySpecimenNumber(importSpecimen.getSpecimenNumber());
|
|
|
+
|
|
|
+
|
|
|
+ if (!isValidImageName(importSpecimen.getImageName())) {
|
|
|
+ respVO.getFailureSpecimenNumbers().put(importSpecimen.getSpecimenNumber(), "图片名称格式不正确");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (existSpecimen == null) {
|
|
|
+
|
|
|
+ SpecimenInfoDO newSpecimen = BeanUtils.toBean(importSpecimen, SpecimenInfoDO.class);
|
|
|
+ specimenInfoMapper.insert(newSpecimen);
|
|
|
+ respVO.getCreateSpecimenNumbers().add(importSpecimen.getSpecimenNumber());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ SpecimenInfoDO updateSpecimen = BeanUtils.toBean(importSpecimen, SpecimenInfoDO.class);
|
|
|
+ updateSpecimen.setId(existSpecimen.getId());
|
|
|
+ specimenInfoMapper.updateById(updateSpecimen);
|
|
|
+ respVO.getUpdateSpecimenNumbers().add(importSpecimen.getSpecimenNumber());
|
|
|
+ });
|
|
|
+
|
|
|
+ return respVO;
|
|
|
+ }
|
|
|
+ private boolean isValidImageName(String imageName) {
|
|
|
+ return imageName != null && imageName.matches(".*\\.(jpg|jpeg|png|gif)$");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入
|
|
|
+ public String importSpecimenImages(MultipartFile file) throws Exception {
|
|
|
+
|
|
|
+ if (!file.getOriginalFilename().endsWith(".zip")) {
|
|
|
+ throw exception(UPLOADED_FOLDER_CANNOT_EMPTY);
|
|
|
+ }
|
|
|
+
|
|
|
+ File tempDir = Files.createTempDirectory("specimen_images").toFile();
|
|
|
+ try (ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream())) {
|
|
|
+ ZipEntry entry;
|
|
|
+ while ((entry = zipInputStream.getNextEntry()) != null) {
|
|
|
+ if (!entry.isDirectory()) {
|
|
|
+ File newFile = new File(tempDir, entry.getName());
|
|
|
+
|
|
|
+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFile))) {
|
|
|
+ byte[] buffer = new byte[1024];
|
|
|
+ int len;
|
|
|
+ while ((len = zipInputStream.read(buffer)) > 0) {
|
|
|
+ bos.write(buffer, 0, len);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ File[] imageFiles = tempDir.listFiles();
|
|
|
+ if (imageFiles != null) {
|
|
|
+ for (File imageFile : imageFiles) {
|
|
|
+ String imageName = imageFile.getName();
|
|
|
+ if (!isValidImageName(imageName)) {
|
|
|
+
|
|
|
+ System.err.println("无效的图片格式: " + imageName);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ SpecimenInfoDO specimenInfo = specimenInfoMapper.selectByImageName(imageName);
|
|
|
+ if (specimenInfo != null) {
|
|
|
+
|
|
|
+ String imagePath = fileApi.createFile(Files.readAllBytes(imageFile.toPath()));
|
|
|
+
|
|
|
+ specimenInfo.setImagePath(imagePath);
|
|
|
+ specimenInfoMapper.updateById(specimenInfo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ FileUtils.deleteDirectory(tempDir);
|
|
|
+ return "标本图片导入成功";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class) // 事务管理
|
|
|
+ public void importSpecimenData(MultipartFile excelFile, MultipartFile imageFile, boolean updateSupport) throws Exception {
|
|
|
+
|
|
|
+ List<SpecimenImportExcelVO> list = ExcelUtils.read(excelFile, SpecimenImportExcelVO.class);
|
|
|
+ SpecimenImportRespVO importRespVO = importSpecimenList(list, updateSupport);
|
|
|
+
|
|
|
+
|
|
|
+ String imageImportResult = importSpecimenImages(imageFile);
|
|
|
+
|
|
|
+
|
|
|
+ System.out.println("标本导入结果: " + importRespVO);
|
|
|
+ System.out.println("标本图片导入结果: " + imageImportResult);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<SpecimenInfoDO> getEntryStatistics(int year) {
|
|
|
+ return specimenInfoMapper.selectEntryStatisticsByYear(year);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<SpecimenInfoDO> getSpecimenTypeStatistics(int specimen_type) {
|
|
|
+ return specimenInfoMapper.selectSpecimenTypeStatistics(specimen_type);
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public List<Map<String, Object>> getAllSpecimenTypeStatistics() {
|
|
|
+ return specimenInfoMapper.selectAllSpecimenTypeStatistics();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<Integer, Map<String, Integer>> getYearlySpecimenStatistics() {
|
|
|
+
|
|
|
+ Map<Integer, Map<String, Integer>> yearlyDataMap = new HashMap<>();
|
|
|
+
|
|
|
+
|
|
|
+ List<SpecimenInfoDO> inStockRecords = specimenInfoMapper.getInStockRecords();
|
|
|
+ if (inStockRecords == null) {
|
|
|
+ inStockRecords = new ArrayList<>();
|
|
|
+ }
|
|
|
+ for (SpecimenInfoDO record : inStockRecords) {
|
|
|
+ if (record.getCreateTime() != null) {
|
|
|
+ int year = record.getCreateTime().getYear();
|
|
|
+ yearlyDataMap.putIfAbsent(year, new HashMap<>());
|
|
|
+ yearlyDataMap.get(year).put("inStockCount", yearlyDataMap.get(year).getOrDefault("inStockCount", 0) + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ List<SpecimenOutboundDO> outboundRecords = specimenOutboundMapper.getOutboundRecords();
|
|
|
+ if (outboundRecords == null) {
|
|
|
+ outboundRecords = new ArrayList<>();
|
|
|
+ }
|
|
|
+ for (SpecimenOutboundDO record : outboundRecords) {
|
|
|
+ if (record.getOutgoingTime() != null) {
|
|
|
+ int year = record.getOutgoingTime().getYear();
|
|
|
+ yearlyDataMap.putIfAbsent(year, new HashMap<>());
|
|
|
+ yearlyDataMap.get(year).put("outStockCount", yearlyDataMap.get(year).getOrDefault("outStockCount", 0) + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ List<SpecimenOutboundDO> inboundRecords = specimenOutboundMapper.getOutboundRecords();
|
|
|
+ if (inboundRecords == null) {
|
|
|
+ inboundRecords = new ArrayList<>();
|
|
|
+ }
|
|
|
+ for (SpecimenOutboundDO record : inboundRecords) {
|
|
|
+ if (record.getReturnDate() != null) {
|
|
|
+ int year = record.getReturnDate().getYear();
|
|
|
+ yearlyDataMap.putIfAbsent(year, new HashMap<>());
|
|
|
+ yearlyDataMap.get(year).put("returnCount", yearlyDataMap.get(year).getOrDefault("returnCount", 0) + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return yearlyDataMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Map<Integer, Map<String, Integer>> getYearlySpecimenSourceStatistics() {
|
|
|
+
|
|
|
+ Map<Integer, Map<String, Integer>> yearlySourceDataMap = new HashMap<>();
|
|
|
+
|
|
|
+
|
|
|
+ List<SpecimenInfoDO> inStockRecords = specimenInfoMapper.getInStockRecords();
|
|
|
+ if (inStockRecords == null) {
|
|
|
+ inStockRecords = new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ for (SpecimenInfoDO record : inStockRecords) {
|
|
|
+ if (record != null && record.getCreateTime() != null) {
|
|
|
+ int year = record.getCreateTime().getYear();
|
|
|
+ yearlySourceDataMap.putIfAbsent(year, new HashMap<>());
|
|
|
+
|
|
|
+
|
|
|
+ String sourceKey = getSourceKey(record.getSource());
|
|
|
+ yearlySourceDataMap.get(year).put(sourceKey, yearlySourceDataMap.get(year).getOrDefault(sourceKey, 0) + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return yearlySourceDataMap;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private String getSourceKey(Integer source) {
|
|
|
+ if (source == null) return "未知";
|
|
|
+ switch (source) {
|
|
|
+ case 0: return "采购";
|
|
|
+ case 1: return "捐赠";
|
|
|
+ case 2: return "采集";
|
|
|
+ default: return "其他";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<SpecimenInfoDO> getSpecimenSourceStatistics(int year) {
|
|
|
+ return specimenInfoMapper.selectSpecimenSourceStatistics(year);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<SpecimenInfoDO> getSpecimenRecords(Long id) {
|
|
|
+ return specimenInfoMapper.getSpecimenRecords(id);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
}
|