Kaynağa Gözat

Merge branch 'master' of http://gogs.gisvg.com/YDM/museums

hyy 1 ay önce
ebeveyn
işleme
5040c19658

+ 11 - 1
yudao-module-museums/yudao-module-museums-biz/src/main/java/cn/iocoder/yudao/module/museums/service/specimeninfo/SpecimenInfoService.java

@@ -67,7 +67,8 @@ public interface SpecimenInfoService {
      * @param isUpdateSupport     是否支持更新
      * @return 导入结果
      */
-    SpecimenImportRespVO importSpecimenList(List<SpecimenImportExcelVO> importSpecimens, boolean isUpdateSupport);
+    SpecimenImportRespVO importSpecimenListAndImages(List<SpecimenImportExcelVO> importSpecimens,MultipartFile file,
+                                            Integer updateType, boolean isUpdateSupport);
     /**
      * 批量导入标本图片
      *
@@ -76,6 +77,15 @@ public interface SpecimenInfoService {
      */
     SpecimenImportRespVO importSpecimenImages(MultipartFile file) throws Exception;
 
+     /**
+     * 批量标本数据同时导入图片
+     *
+     * @param importSpecimens     导入标本列表
+     * @param isUpdateSupport     是否支持更新
+     * @return 导入结果
+     */
+    SpecimenImportRespVO importSpecimenList(List<SpecimenImportExcelVO> importSpecimens, boolean isUpdateSupport);
+
     //工作台
     /**
      * 统计本年标本每月入库数量

+ 188 - 1
yudao-module-museums/yudao-module-museums-biz/src/main/java/cn/iocoder/yudao/module/museums/service/specimeninfo/SpecimenInfoServiceImpl.java

@@ -31,7 +31,12 @@ 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 static cn.iocoder.yudao.module.museums.enums.social.LogRecordConstants.*;
-
+import org.apache.tomcat.util.http.fileupload.FileUtils;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.nio.file.Files;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
 /**
  * 标本管理 Service 实现类
  *
@@ -297,6 +302,188 @@ public class SpecimenInfoServiceImpl implements SpecimenInfoService {
         return respVO;
     }
 
+
+    @Override
+    public SpecimenImportRespVO importSpecimenListAndImages(List<SpecimenImportExcelVO> importSpecimens,
+                                                            MultipartFile file, Integer updateType, boolean isUpdateSupport) {
+
+        File tempDir;
+        try {
+            tempDir = Files.createTempDirectory("specimen_images").toFile();
+        } catch (IOException e) {
+            throw new RuntimeException("创建临时目录失败", e);
+        }
+
+        if(file != null && !file.isEmpty()) {
+            if (!file.getOriginalFilename().endsWith(".zip")) {
+                throw exception(PHOTO_GROUP_MUST_BE_COMPRESSED_PACKAGE);
+            }
+            try (ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream())) {
+                ZipEntry entry;
+                while ((entry = zipInputStream.getNextEntry()) != null) {
+                    if (!entry.isDirectory()) {
+                        String fileName = entry.getName().toLowerCase();
+                        if (isSupportedImage(fileName)) {
+                            File newFile = new File(tempDir, fileName);
+                            try (FileOutputStream fos = new FileOutputStream(newFile);
+                                 BufferedOutputStream bos = new BufferedOutputStream(fos)) {
+                                byte[] buffer = new byte[4096];
+                                int length;
+                                while ((length = zipInputStream.read(buffer)) != -1) {
+                                    bos.write(buffer, 0, length);
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                throw exception(PHOTO_GROUP_PACKAGE_FAILED_DECOMPRESS);
+            }
+        }
+
+        // 1.1 参数校验
+        if (CollUtil.isEmpty(importSpecimens)) {
+            throw exception(SPECIMEN_INFO_LIST_IS_EMPTY);
+        }
+
+        // 2. 遍历,逐个创建或更新
+        SpecimenImportRespVO respVO = SpecimenImportRespVO.builder()
+                .createSpecimenNumbers(new ArrayList<>())
+                .updateSpecimenNumbers(new ArrayList<>())
+                .failureSpecimenNumbers(new LinkedHashMap<>())
+                .createdSpecimenIds(new ArrayList<>())
+                .createSpecimenImages(new ArrayList<>())  // 成功导入的图片
+                .failureSpecimenImages(new LinkedHashMap<>())  // 失败的图片及错误信息
+                .build();
+
+        List<Long> successfulSpecimenIds = new ArrayList<>(); // 用于存储成功导入的标本ID
+        List<Long> updatedSpecimenIds = new ArrayList<>(); // 用于存储成功更新的标本ID
+
+        importSpecimens.forEach(importSpecimen -> {
+            // 2.1. 判断是否存在
+            SpecimenInfoDO existSpecimen = specimenInfoMapper.selectBySpecimenNumber(importSpecimen.getSpecimenNumber());
+
+            if (!isValidImageName(importSpecimen.getImageName())) {
+                respVO.getFailureSpecimenNumbers().put(importSpecimen.getSpecimenNumber(), "图片名称格式不正确");
+                return;
+            }
+
+            String[] names = importSpecimen.getImageName().split("、");
+            List<String> imagePathSet = new ArrayList<>();
+            for (String importName : names) {
+                File imageFile = findImageInTempDir(tempDir, importName);
+                if (imageFile != null) {
+                    try {
+                        byte[] imageBytes = Files.readAllBytes(imageFile.toPath());
+                        String imagePath = fileApi.createFile(imageBytes);
+                        imagePathSet.add(imagePath);
+                        respVO.getCreateSpecimenImages().add(importName);
+                        // 处理上传后的路径,如存储到数据库或更新状态等
+                    } catch (IOException e) {
+                        respVO.getFailureSpecimenImages().put(importName, "图片上传失败");
+                    }
+                } else {
+                    respVO.getFailureSpecimenImages().put(importSpecimen.getSpecimenNumber(), "图片不存在压缩包中");
+                }
+                if (existSpecimen != null &&  imagePathSet.isEmpty()) {
+                    imagePathSet = BeanUtils.toBean(existSpecimen, SpecimenInfoRespVO.class).getImagePath();
+                }
+            }
+
+            // 处理文献资料字段,如果为空就赋值 <br>
+            if (importSpecimen.getDescription() == null || importSpecimen.getDescription().trim().isEmpty()) {
+                importSpecimen.setDescription("<p><br></p>");
+            }
+
+            if (existSpecimen == null) {
+                // 2.2.1 不存在则插入
+                SpecimenInfoDO newSpecimen = BeanUtils.toBean(importSpecimen, SpecimenInfoDO.class);
+                newSpecimen.setImagePath(imagePathSet.toString());
+                if (updateType != -1) {
+                    newSpecimen.setSpecimenType(updateType);
+                }
+                specimenInfoMapper.insert(newSpecimen);
+                respVO.getCreateSpecimenNumbers().add(importSpecimen.getSpecimenNumber());
+                respVO.getCreatedSpecimenIds().add(newSpecimen.getId()); // 添加新标本 ID
+                successfulSpecimenIds.add(newSpecimen.getId()); // 记录成功导入的ID
+                return;
+            }
+
+            // 2.2.2 如果存在,判断是否允许更新
+            if (!isUpdateSupport) {
+                respVO.getFailureSpecimenNumbers().put(importSpecimen.getSpecimenNumber(), "标本编号已存在,且不支持更新");
+                return;
+            }
+            // 更新逻辑
+            SpecimenInfoDO updateSpecimen = BeanUtils.toBean(importSpecimen, SpecimenInfoDO.class);
+
+
+            updateSpecimen.setImagePath(imagePathSet.toString());
+            if (updateType != -1) {
+                updateSpecimen.setSpecimenType(updateType);
+            }
+            updateSpecimen.setId(existSpecimen.getId()); // 设置 ID 进行更新
+            specimenInfoMapper.updateById(updateSpecimen);
+            respVO.getUpdateSpecimenNumbers().add(importSpecimen.getSpecimenNumber());
+            updatedSpecimenIds.add(updateSpecimen.getId()); // 记录成功更新的ID
+        });
+
+        // 合并成功导入和更新的标本ID
+        successfulSpecimenIds.addAll(updatedSpecimenIds);
+
+        // 记录成功导入和更新的标本ID到日志上下文
+        LogRecordContext.putVariable("extra", successfulSpecimenIds);
+
+        return respVO;
+
+    }
+     private boolean isSupportedImage(String fileName) {
+        return fileName.endsWith(".jpg") || fileName.endsWith(".png") || fileName.endsWith(".gif") ||
+               fileName.endsWith(".bmp") || fileName.endsWith(".tiff") || fileName.endsWith(".jpeg") ||
+               fileName.endsWith(".psd") || fileName.endsWith(".raw") || fileName.endsWith(".svg");
+    }
+
+    private File findImageInTempDir(File tempDir, String imageName) {
+        File[] files = tempDir.listFiles((dir, name) -> name.equalsIgnoreCase(imageName));
+        return files != null && files.length > 0 ? files[0] : null;
+    }
+    //公共方法
+//    public List<Integer> processZipFile(ZipInputStream zipInputStream, Integer groupId) throws Exception {
+//        List<Integer> photoIds = new ArrayList<>();
+//        ZipEntry entry;
+//        while ((entry = zipInputStream.getNextEntry()) != null) {
+//            if (!entry.isDirectory()) {
+//                String fileName = entry.getName().toLowerCase();  // 转为小写
+//                // 检查文件类型,只处理图片(转换为小写后进行检查)
+//                if (fileName.endsWith(".jpg") || fileName.endsWith(".png") || fileName.endsWith(".gif")
+//                        || fileName.endsWith(".bmp") || fileName.endsWith(".tiff")|| fileName.endsWith(".jpeg")
+//                        || fileName.endsWith(".psd") || fileName.endsWith(".raw") || fileName.endsWith(".svg")) {
+//
+//                    // 使用 ByteArrayOutputStream 读取文件内容
+//                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+//                    byte[] buffer = new byte[1024];
+//                    int length;
+//                    while ((length = zipInputStream.read(buffer)) != -1) {
+//                        bos.write(buffer, 0, length);
+//                    }
+//                    byte[] imageBytes = bos.toByteArray();
+//
+//                    // 上传图片并获取 URL
+//                    String imagePath = fileApi.createFile(imageBytes);
+//
+//                    // 创建照片信息记录
+//                    PhotosDO photoRecord = new PhotosDO();
+//                    photoRecord.setGroupId(groupId);
+//                    photoRecord.setPhotoUrl(imagePath);
+//                    photosMapper.insert(photoRecord);
+//
+//                    // 获取并存储新插入的照片 ID
+//                    photoIds.add(photoRecord.getId());
+//                }
+//            }
+//        }
+//        return photoIds;
+//    }
     //工作台
     //根据入库的登记情况统计本年标本入库信息
     @Override

+ 5 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java

@@ -67,7 +67,11 @@ public class UserSaveReqVO {
     // ========== 仅【创建】时,需要传递的字段 ==========
 
     @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
-    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+    @Length(min = 8, message = "密码长度至少为 8 位")
+    @Pattern(
+            regexp = "^(?:(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)|(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?])|(?=.*[a-z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?])|(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?])).{8,}$",
+            message = "密码至少包含大写字母、小写字母、数字和特殊字符中的任意三种"
+    )
     private String password;
 
     @AssertTrue(message = "密码不能为空")

+ 5 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java

@@ -6,6 +6,7 @@ import org.hibernate.validator.constraints.Length;
 
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
 
 @Schema(description = "管理后台 - 用户更新密码 Request VO")
 @Data
@@ -18,6 +19,10 @@ public class UserUpdatePasswordReqVO {
     @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
     @NotEmpty(message = "密码不能为空")
     @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+    @Pattern(
+            regexp = "^(?:(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)|(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?])|(?=.*[a-z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?])|(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?])).{8,}$",
+            message = "密码至少包含大写字母、小写字母、数字和特殊字符中的任意三种"
+    )
     private String password;
 
 }