diff --git a/backend/src/main/java/com/yfd/platform/annotation/UserIdField.java b/backend/src/main/java/com/yfd/platform/annotation/UserIdField.java new file mode 100644 index 0000000..43721a2 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/annotation/UserIdField.java @@ -0,0 +1,14 @@ +package com.yfd.platform.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 标记字段为用户ID字段,用于用户名称自动填充 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface UserIdField { +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/annotation/UserNameField.java b/backend/src/main/java/com/yfd/platform/annotation/UserNameField.java new file mode 100644 index 0000000..583d9b8 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/annotation/UserNameField.java @@ -0,0 +1,14 @@ +package com.yfd.platform.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 标记字段为用户名字段,用于接收自动填充的用户名称 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface UserNameField { +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/common/utils/UserNameFillHelper.java b/backend/src/main/java/com/yfd/platform/common/utils/UserNameFillHelper.java new file mode 100644 index 0000000..86157c4 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/common/utils/UserNameFillHelper.java @@ -0,0 +1,190 @@ +package com.yfd.platform.common.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.yfd.platform.annotation.UserIdField; +import com.yfd.platform.annotation.UserNameField; +import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.mapper.SysUserMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Field; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 用户名称自动填充工具类 + *

+ * 使用方式: + * 1. 在实体类的用户ID字段上添加 @UserIdField 注解 + * 2. 在对应需要填充用户名的字段上添加 @UserNameField 注解 + * 3. 调用 {@link #fillUserNames(List)} 方法自动填充 + * + *

+ * 示例:
+ * {@code
+ * @Data
+ * public class MyEntity {
+ *     @UserIdField
+ *     private String userId;
+ *
+ *     @UserNameField
+ *     private String userName;
+ * }
+ *
+ * // 使用
+ * List list = ...;
+ * userNameFillHelper.fillUserNames(list);
+ * }
+ * 
+ */ +@Component +public class UserNameFillHelper { + + @Resource + private SysUserMapper sysUserMapper; + + /** + * 批量填充用户名称 + * + * @param list 需要填充的实体列表 + */ + public void fillUserNames(List list) { + if (CollUtil.isEmpty(list)) { + return; + } + + Class clazz = list.get(0).getClass(); + Field[] fields = clazz.getDeclaredFields(); + + Field idField = null; + Field nameField = null; + + for (Field field : fields) { + if (field.isAnnotationPresent(UserIdField.class)) { + idField = field; + idField.setAccessible(true); + } + if (field.isAnnotationPresent(UserNameField.class)) { + nameField = field; + nameField.setAccessible(true); + } + } + + if (idField == null || nameField == null) { + return; + } + + Set userIds = new HashSet<>(); + for (T item : list) { + try { + Object idValue = idField.get(item); + if (idValue != null && StrUtil.isNotBlank(idValue.toString())) { + userIds.add(idValue.toString()); + } + } catch (IllegalAccessException e) { + continue; + } + } + + if (userIds.isEmpty()) { + return; + } + + List users = sysUserMapper.selectBatchIds(userIds); + Map userNameMap = users.stream() + .collect(Collectors.toMap(SysUser::getId, SysUser::getNickname, (a, b) -> a)); + + for (T item : list) { + try { + Object idValue = idField.get(item); + if (idValue != null && StrUtil.isNotBlank(idValue.toString())) { + String userName = userNameMap.get(idValue.toString()); + nameField.set(item, StrUtil.blankToDefault(userName, "未知")); + } + } catch (IllegalAccessException e) { + continue; + } + } + } + + /** + * 批量填充用户名称(支持多个用户ID字段) + *

+ * 使用方式:在实体类中定义多个 @UserIdField 和 @UserNameField 配对字段 + * + * @param list 需要填充的实体列表 + * @param idFields 用户ID字段名数组 + * @param nameFields 用户名字段名数组 + */ + public void fillUserNames(List list, String[] idFields, String[] nameFields) { + if (CollUtil.isEmpty(list) || idFields == null || nameFields == null || idFields.length != nameFields.length) { + return; + } + + Class clazz = list.get(0).getClass(); + Field[] allFields = clazz.getDeclaredFields(); + Map fieldMap = new HashMap<>(); + for (Field field : allFields) { + fieldMap.put(field.getName(), field); + } + + List idFieldList = new ArrayList<>(); + List nameFieldList = new ArrayList<>(); + for (String idFieldName : idFields) { + Field f = fieldMap.get(idFieldName); + if (f != null) { + f.setAccessible(true); + idFieldList.add(f); + } + } + for (String nameFieldName : nameFields) { + Field f = fieldMap.get(nameFieldName); + if (f != null) { + f.setAccessible(true); + nameFieldList.add(f); + } + } + + if (idFieldList.isEmpty() || nameFieldList.isEmpty()) { + return; + } + + Set userIds = new HashSet<>(); + for (T item : list) { + for (Field idField : idFieldList) { + try { + Object idValue = idField.get(item); + if (idValue != null && StrUtil.isNotBlank(idValue.toString())) { + userIds.add(idValue.toString()); + } + } catch (IllegalAccessException e) { + continue; + } + } + } + + if (userIds.isEmpty()) { + return; + } + + List users = sysUserMapper.selectBatchIds(userIds); + Map userNameMap = users.stream() + .collect(Collectors.toMap(SysUser::getId, SysUser::getNickname, (a, b) -> a)); + + for (T item : list) { + for (int i = 0; i < idFieldList.size() && i < nameFieldList.size(); i++) { + try { + Object idValue = idFieldList.get(i).get(item); + if (idValue != null && StrUtil.isNotBlank(idValue.toString())) { + String userName = userNameMap.get(idValue.toString()); + nameFieldList.get(i).set(item, StrUtil.blankToDefault(userName, "未知")); + } + } catch (IllegalAccessException e) { + continue; + } + } + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/config/MyMetaObjectHandler.java b/backend/src/main/java/com/yfd/platform/config/MyMetaObjectHandler.java index 3e8a46e..5510e43 100644 --- a/backend/src/main/java/com/yfd/platform/config/MyMetaObjectHandler.java +++ b/backend/src/main/java/com/yfd/platform/config/MyMetaObjectHandler.java @@ -27,11 +27,17 @@ public class MyMetaObjectHandler implements MetaObjectHandler { // 自动填充更新时间 this.strictInsertFill(metaObject, "updatedAt", Date.class, now); - // 自动填充更新时间 - this.strictInsertFill(metaObject, "createdBy", String.class, SecurityUtils.getUserId()); + try { + String userId = SecurityUtils.getUserId(); + // 自动填充更新时间 + this.strictInsertFill(metaObject, "createdBy", String.class, userId ); + + // 自动填充更新时间 + this.strictInsertFill(metaObject, "updatedBy", String.class, userId); + } catch (Exception e) { + e.printStackTrace(); + } - // 自动填充更新时间 - this.strictInsertFill(metaObject, "updatedBy", String.class, SecurityUtils.getUserId()); } /** @@ -41,7 +47,13 @@ public class MyMetaObjectHandler implements MetaObjectHandler { public void updateFill(MetaObject metaObject) { // 自动填充更新时间 this.strictUpdateFill(metaObject, "updatedAt", Date.class, new Date()); - // 自动填充更新人 - this.strictInsertFill(metaObject, "updatedBy", String.class, SecurityUtils.getUserId()); + try { + String userId = SecurityUtils.getUserId(); + // 自动填充更新人 + this.strictInsertFill(metaObject, "updatedBy", String.class, userId); + } catch (Exception e) { + e.printStackTrace(); + } + } } diff --git a/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java b/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java index 2bcef19..ac00ba5 100644 --- a/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java @@ -53,6 +53,10 @@ public class SecurityConfig { .authorizeHttpRequests(auth -> auth .requestMatchers("/user/login").anonymous() .requestMatchers("/user/code").permitAll() + .requestMatchers("/sms/resetPassword").permitAll() + .requestMatchers("/data/fishDraft/previewFile").permitAll() + .requestMatchers("/tempFile/**").permitAll() + .requestMatchers("/system/user/auditUser").permitAll() .requestMatchers("/eng/**").permitAll() .requestMatchers("/env/**").permitAll() .requestMatchers("/sw/**").permitAll() diff --git a/backend/src/main/java/com/yfd/platform/config/WebConfig.java b/backend/src/main/java/com/yfd/platform/config/WebConfig.java index 45b051a..8356277 100644 --- a/backend/src/main/java/com/yfd/platform/config/WebConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/WebConfig.java @@ -4,6 +4,7 @@ import cn.hutool.cache.Cache; import cn.hutool.cache.CacheUtil; import lombok.SneakyThrows; import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; @@ -17,6 +18,9 @@ public class WebConfig implements WebMvcConfigurer { @Resource private FileSpaceProperties fileSpaceProperties; + @Value("${app.zip-import.temp-dir}") + private String platformPath; + @Bean @@ -54,7 +58,8 @@ public class WebConfig implements WebMvcConfigurer { String systemUrl = "file:" + fileSpaceProperties.getSystem().replace("\\", "/")+"user\\"; registry.addResourceHandler("/avatar/**").addResourceLocations(systemUrl).setCachePeriod(0); - + String platformUrl = "file:" + platformPath.replace("\\", "/"); + registry.addResourceHandler("/tempFile/**").addResourceLocations(platformUrl).setCachePeriod(0); } diff --git a/backend/src/main/java/com/yfd/platform/data/controller/ApprovalChangeLogController.java b/backend/src/main/java/com/yfd/platform/data/controller/ApprovalChangeLogController.java index 26e019f..49a2d2f 100644 --- a/backend/src/main/java/com/yfd/platform/data/controller/ApprovalChangeLogController.java +++ b/backend/src/main/java/com/yfd/platform/data/controller/ApprovalChangeLogController.java @@ -2,6 +2,7 @@ package com.yfd.platform.data.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.common.DataSourceRequest; +import com.yfd.platform.common.utils.UserNameFillHelper; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.data.domain.ApprovalChangeLog; import com.yfd.platform.data.domain.ApprovalLog; @@ -28,6 +29,9 @@ public class ApprovalChangeLogController { @Resource private IApprovalChangeLogService approvalChangeLogService; + @Resource + private UserNameFillHelper userNameFillHelper; + @GetMapping("/list") @Operation(summary = "查询变更记录列表") public ResponseResult list() { @@ -39,7 +43,8 @@ public class ApprovalChangeLogController { @Operation(summary = "分页查询变更记录列表(通用)") public ResponseResult queryPageList(@RequestBody DataSourceRequest request) { Page approvalChangeLogPage = DataSourceRequestUtil.executeQuery(request, ApprovalChangeLog.class, approvalChangeLogService); - approvalChangeLogService.fillUserNames(approvalChangeLogPage.getRecords()); + userNameFillHelper.fillUserNames(approvalChangeLogPage.getRecords()); + // approvalChangeLogService.fillUserNames(approvalChangeLogPage.getRecords()); return ResponseResult.successData(approvalChangeLogPage); } diff --git a/backend/src/main/java/com/yfd/platform/data/controller/FishDraftDataController.java b/backend/src/main/java/com/yfd/platform/data/controller/FishDraftDataController.java index 85a449d..fd0b529 100644 --- a/backend/src/main/java/com/yfd/platform/data/controller/FishDraftDataController.java +++ b/backend/src/main/java/com/yfd/platform/data/controller/FishDraftDataController.java @@ -1,24 +1,35 @@ package com.yfd.platform.data.controller; +import java.io.File; +import java.io.FileInputStream; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +import cn.hutool.core.io.FileUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fasterxml.jackson.databind.ObjectMapper; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.config.ResponseResult; -import com.yfd.platform.data.domain.FishDraftData; -import com.yfd.platform.data.domain.FishImportRequest; -import com.yfd.platform.data.domain.FishImportResult; -import com.yfd.platform.data.domain.ImportTask; -import com.yfd.platform.data.domain.BatchApproveRequest; -import com.yfd.platform.data.domain.BatchRejectRequest; +import com.yfd.platform.data.domain.*; import com.yfd.platform.data.domain.vo.FishDraftDataVO; +import com.yfd.platform.data.service.AttachmentUploadService; import com.yfd.platform.data.service.IFishDraftDataService; import com.yfd.platform.data.service.IFishImportService; import com.yfd.platform.data.service.IImportTaskService; +import com.yfd.platform.data.utils.ZipFileUtil; import com.yfd.platform.utils.KendoUtil; import com.yfd.platform.utils.SecurityUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -33,6 +44,7 @@ import java.util.*; @RestController @RequestMapping("/data/fishDraft") @Tag(name = "过鱼数据") +@Slf4j public class FishDraftDataController { @Resource @@ -47,10 +59,13 @@ public class FishDraftDataController { @Resource private ObjectMapper objectMapper; + @Resource + private AttachmentUploadService attachmentUploadService; + @PostMapping("/page") @Operation(summary = "分页查询过鱼数据(关联电站和设施)") public ResponseResult queryPageList(@RequestBody DataSourceRequest dataSourceRequest) { - Page result = fishDraftDataService.queryPageList( dataSourceRequest); + Page result = fishDraftDataService.queryPageList(dataSourceRequest); return ResponseResult.successData(result); } @@ -109,6 +124,45 @@ public class FishDraftDataController { return result ? ResponseResult.success("保存成功") : ResponseResult.error("保存失败"); } + @PostMapping("/importBatchSaveDraft") + @Operation(summary = "批量保存草稿(导入)") + public ResponseResult importBatchSaveDraft(@RequestBody BatchSaveDraftRequest batchSaveDraftRequest) { + String taskId = batchSaveDraftRequest.getTaskId(); + ImportTask importTask = importTaskService.getById(taskId); + if (importTask == null) { + return ResponseResult.error("导入任务不存在"); + } + String resultJson = importTask.getResultJson(); + List fishDraftDataList = batchSaveDraftRequest.getFishDraftDataList(); + if (resultJson != null && !resultJson.isEmpty()) { + try { + FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class); + ZipFileUtil.ZipContent content = new ZipFileUtil.ZipContent(); + content.images = importResult.getImageFiles(); + content.videos = importResult.getVideoFiles(); + fishImportService.processAttachments(importResult, content); + fishDraftDataList.forEach(fishDraftData -> { + for (FishImportResult.FishImportRow row : importResult.getRows()) { + if (fishDraftData.getId().equals(row.getData().getId())) { + fishDraftData.setPicpth(row.getData().getPicpth()); + fishDraftData.setVdpth(row.getData().getVdpth()); + break; + } + } + fishDraftData.setStatus("DRAFT"); + fishDraftData.setDeletedFlag(0); + fishDraftData.setLockFlag(0); + }); + } catch (Exception e) { + e.printStackTrace(); + // ignore parse error + } + } + + boolean result = fishDraftDataService.saveBatch(fishDraftDataList); + return result ? ResponseResult.success("保存成功") : ResponseResult.error("保存失败"); + } + @PostMapping("/updateDraft") @Operation(summary = "更新草稿") public ResponseResult updateDraft(@RequestBody FishDraftData fishDraftData) { @@ -134,7 +188,7 @@ public class FishDraftDataController { @PostMapping("/submitDraft") @Operation(summary = "提交草稿") public ResponseResult submitDraft(@RequestParam String id, - @RequestParam String operatorId) { + @RequestParam String operatorId) { boolean result = fishDraftDataService.submitDraft(id, operatorId); return result ? ResponseResult.success("提交成功") : ResponseResult.error("提交失败"); } @@ -161,7 +215,6 @@ public class FishDraftDataController { } - @PostMapping("/lockDraft") @Operation(summary = "锁定草稿") public ResponseResult lockDraft(@RequestParam String id) { @@ -207,18 +260,22 @@ public class FishDraftDataController { @PostMapping("/importZip") @Operation(summary = "导入ZIP过鱼数据(每个用户同时只能进行一次导入)") public ResponseResult importZip(@RequestParam("file") MultipartFile file) { + log.info("开始导入ZIP文件"); if (file == null || file.isEmpty()) { return ResponseResult.error("请上传文件"); } + log.info("开始上传文件"); String fileName = file.getOriginalFilename(); if (fileName == null || (!fileName.endsWith(".zip"))) { return ResponseResult.error("请上传ZIP文件(.zip)"); } + log.info("开始处理文件"); String uploadUserId = SecurityUtils.getUserId(); if (importTaskService.hasImportingTask(uploadUserId)) { return ResponseResult.error("您有正在进行的导入任务,请等待完成后重试"); } + log.info("开始保存导入任务"); String importNo = "IMP" + System.currentTimeMillis(); String taskId = UUID.randomUUID().toString(); @@ -232,8 +289,10 @@ public class FishDraftDataController { task.setStatus("UPLOADED"); task.setUploadUserId(uploadUserId); task.setUploadTime(new Date()); + log.info("保存导入任务成功"); importTaskService.save(task); + log.info("开始保存文件"); FishImportRequest request = new FishImportRequest(); request.setImportNo(importNo); request.setUploadUserId(uploadUserId); @@ -243,7 +302,7 @@ public class FishDraftDataController { result.setTaskId(taskId); String status = "VALIDATED"; if ("1".equals(result.getCode())) { - status="FAILED"; + status = "FAILED"; } importTaskService.updateStatus(taskId, status, null); importTaskService.updateProgress(taskId, result.getTotalCount(), result.getSuccessCount(), result.getFailedCount()); @@ -262,10 +321,281 @@ public class FishDraftDataController { } } + @GetMapping("/previewTempFiles") + @Operation(summary = "预览临时文件列表") + public ResponseResult previewTempFiles(@RequestParam String taskId) { + if (taskId == null || taskId.isEmpty()) { + return ResponseResult.error("任务ID不能为空"); + } + + try { + ImportTask task = importTaskService.getById(taskId); + if (task == null) { + return ResponseResult.error("任务不存在"); + } + + String resultJson = task.getResultJson(); + if (resultJson == null || resultJson.isEmpty()) { + return ResponseResult.error("任务结果为空"); + } + + FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class); + + Map previewData = new HashMap<>(); + previewData.put("tempDir", importResult.getTempDir()); + previewData.put("excelFileName", importResult.getExcelFileName()); + previewData.put("excelFilePath", importResult.getExcelFilePath()); + + List> imageList = new ArrayList<>(); + if (importResult.getImageFiles() != null) { + for (Map.Entry entry : importResult.getImageFiles().entrySet()) { + Map fileInfo = new HashMap<>(); + fileInfo.put("originalName", entry.getKey()); + fileInfo.put("path", entry.getValue()); + fileInfo.put("type", "image"); + imageList.add(fileInfo); + } + } + previewData.put("images", imageList); + + List> videoList = new ArrayList<>(); + if (importResult.getVideoFiles() != null) { + for (Map.Entry entry : importResult.getVideoFiles().entrySet()) { + Map fileInfo = new HashMap<>(); + fileInfo.put("originalName", entry.getKey()); + fileInfo.put("path", entry.getValue()); + fileInfo.put("type", "video"); + videoList.add(fileInfo); + } + } + previewData.put("videos", videoList); + + previewData.put("totalImages", imageList.size()); + previewData.put("totalVideos", videoList.size()); + + return ResponseResult.successData(previewData); + + } catch (Exception e) { + log.error("预览临时文件失败: " + e.getMessage(), e); + return ResponseResult.error("预览失败: " + e.getMessage()); + } + } + + @GetMapping("/previewFile") + @Operation(summary = "预览临时文件内容") + public void previewFile(@RequestParam String taskId, @RequestParam String filename, @RequestParam String type, HttpServletRequest request, HttpServletResponse response) { + + ImportTask importTask = importTaskService.getById(taskId); + String resultJson = importTask.getResultJson(); + String filePath = null; + String dir = "1".equals(type) ? "images" : "videos"; + if (resultJson != null && !resultJson.isEmpty()) { + try { + FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class); + String tempDir = importResult.getTempDir(); + filePath = tempDir + File.separator + dir + File.separator + filename; + } catch (Exception e) { + e.printStackTrace(); + // ignore parse error + } + } + if (filePath == null) { + writeErrorResponse(response, "文件路径不能为空"); + return; + } + + File file = new File(filePath); + if (!file.exists() || !file.isFile()) { + writeErrorResponse(response, "文件不存在"); + return; + } + + try { + String fileName = file.getName(); + String contentType = request.getServletContext().getMimeType(fileName); + if (contentType == null) { + contentType = "application/octet-stream"; + } + + response.setContentType(contentType); + response.setHeader("Content-Disposition", "inline; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\""); + response.setContentLengthLong(file.length()); + + try (FileInputStream fis = new FileInputStream(file); + OutputStream os = response.getOutputStream()) { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = fis.read(buffer)) != -1) { + os.write(buffer, 0, bytesRead); + } + os.flush(); + } + } catch (Exception e) { + log.error("预览文件失败: " + e.getMessage(), e); + writeErrorResponse(response, "预览失败: " + e.getMessage()); + } + } + @GetMapping("/deleteFile") + @Operation(summary = "删除导入文件") + public ResponseResult deleteFile(@RequestParam String taskId, + @RequestParam String id, + @RequestParam String type, + @RequestParam String filename) { + if (taskId == null || taskId.isEmpty()) { + return ResponseResult.error("任务ID不能为空"); + } + if (id == null || id.isEmpty()) { + return ResponseResult.error("数据ID不能为空"); + } + if (filename == null || filename.isEmpty()) { + return ResponseResult.error("文件名不能为空"); + } + + ImportTask importTask = importTaskService.getById(taskId); + if (importTask == null) { + return ResponseResult.error("任务不存在"); + } + + String resultJson = importTask.getResultJson(); + if (resultJson == null || resultJson.isEmpty()) { + return ResponseResult.error("任务结果为空"); + } + + try { + FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class); + boolean found = false; + + for (FishImportResult.FishImportRow row : importResult.getRows()) { + FishDraftData data = row.getData(); + if (data == null || !id.equals(data.getId())) { + continue; + } + found = true; + + if ("1".equals(type)) { + String picpthName = data.getPicpthName(); + if (picpthName != null && !picpthName.isEmpty()) { + String[] names = picpthName.split(";"); + StringBuilder newNames = new StringBuilder(); + for (String name : names) { + if (!filename.equals(name.trim())) { + if (newNames.length() > 0) { + newNames.append(";"); + } + newNames.append(name.trim()); + } + } + data.setPicpthName(newNames.toString()); + } + + List> picpthList = row.getPicpthList(); + if (picpthList != null) { + picpthList.removeIf(map -> filename.equals(map.get("name"))); + } + + } else if ("2".equals(type)) { + String vdpthName = data.getVdpthName(); + if (vdpthName != null && !vdpthName.isEmpty()) { + String[] names = vdpthName.split(";"); + StringBuilder newNames = new StringBuilder(); + for (String name : names) { + if (!filename.equals(name.trim())) { + if (newNames.length() > 0) { + newNames.append(";"); + } + newNames.append(name.trim()); + } + } + data.setVdpthName(newNames.toString()); + } + + List> vdpthList = row.getVdpthList(); + if (vdpthList != null) { + vdpthList.removeIf(map -> filename.equals(map.get("name"))); + } + } + } + + if (!found) { + return ResponseResult.error("未找到对应的数据行"); + } + + String updatedJson = objectMapper.writeValueAsString(importResult); + importTaskService.saveResultJson(taskId, updatedJson); + + return ResponseResult.success("删除成功"); + + } catch (Exception e) { + log.error("删除文件失败: " + e.getMessage(), e); + return ResponseResult.error("删除失败: " + e.getMessage()); + } + } + + @GetMapping("/previewFileBase64") + @Operation(summary = "预览临时文件内容(Base64方式)") + public ResponseResult previewFileBase64(@RequestParam String filePath) { + if (filePath == null || filePath.isEmpty()) { + return ResponseResult.error("文件路径不能为空"); + } + + File file = new File(filePath); + if (!file.exists() || !file.isFile()) { + return ResponseResult.error("文件不存在"); + } + + try { + String fileName = file.getName(); + String contentType = getMimeType(fileName); + + byte[] fileContent = Files.readAllBytes(file.toPath()); + String base64Content = Base64.getEncoder().encodeToString(fileContent); + + Map result = new HashMap<>(); + result.put("fileName", fileName); + result.put("contentType", contentType); + result.put("base64Content", base64Content); + result.put("size", file.length()); + + return ResponseResult.successData(result); + } catch (Exception e) { + log.error("预览文件失败: " + e.getMessage(), e); + return ResponseResult.error("预览失败: " + e.getMessage()); + } + } + + private void writeErrorResponse(HttpServletResponse response, String message) { + try { + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write("{\"success\":false,\"message\":\"" + message + "\"}"); + } catch (Exception ignored) { + } + } + + private String getMimeType(String fileName) { + if (fileName == null) return "application/octet-stream"; + String lowerName = fileName.toLowerCase(); + if (lowerName.endsWith(".jpg") || lowerName.endsWith(".jpeg")) { + return "image/jpeg"; + } else if (lowerName.endsWith(".png")) { + return "image/png"; + } else if (lowerName.endsWith(".gif")) { + return "image/gif"; + } else if (lowerName.endsWith(".mp4")) { + return "video/mp4"; + } else if (lowerName.endsWith(".webm")) { + return "video/webm"; + } else if (lowerName.endsWith(".pdf")) { + return "application/pdf"; + } else if (lowerName.endsWith(".xlsx") || lowerName.endsWith(".xls")) { + return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + } + return "application/octet-stream"; + } + @PostMapping("/cancelImport") @Operation(summary = "取消导入任务") public ResponseResult cancelImport(@RequestBody FishImportRequest fishImportRequest) { - boolean result = importTaskService.cancelTask(fishImportRequest.getTaskId(),SecurityUtils.getUserId()); + boolean result = importTaskService.cancelTask(fishImportRequest.getTaskId(), SecurityUtils.getUserId()); return result ? ResponseResult.success("取消成功") : ResponseResult.error("取消失败"); } @@ -392,15 +722,11 @@ public class FishDraftDataController { } Map map = new HashMap<>(); - map.put("successRows", importResult.getSuccessRows() != null ? importResult.getSuccessRows().size() : 0); - map.put("failedRows", importResult.getFailedRows() != null ? importResult.getFailedRows().size() : 0); - map.put("unrecognizedFields", importResult.getUnrecognizedFields() != null ? - importResult.getUnrecognizedFields() : new ArrayList<>()); map.put("tempDir", importResult.getTempDir() != null ? importResult.getTempDir() : ""); map.put("excelFileName", importResult.getExcelFileName() != null ? importResult.getExcelFileName() : ""); - map.put("failedRowDetails", importResult.getFailedRows() != null ? - importResult.getFailedRows() : new ArrayList<>()); + map.put("rows", importResult.getRows() != null ? + importResult.getRows() : new ArrayList<>()); return map; } @@ -414,4 +740,316 @@ public class FishDraftDataController { return ResponseResult.error("清理失败: " + e.getMessage()); } } + + @PostMapping("/deleteAttachment") + @Operation(summary = "删除附件") + public ResponseResult deleteAttachment(@RequestParam String id) { + if (id == null || id.isEmpty()) { + return ResponseResult.error("附件ID不能为空"); + } + boolean result = attachmentUploadService.deleteFile(id); + if (result) { + return ResponseResult.success("删除成功"); + } else { + return ResponseResult.error("删除失败"); + } + } + + @PostMapping("/revalidateAndUpdateRow") + @Operation(summary = "重新校验并更新导入数据") + public ResponseResult revalidateAndUpdateRow(@RequestBody FishImportRowRequest request) { + String taskId = request.getTaskId(); + FishDraftData data = request.getData(); + + if (taskId == null || taskId.isEmpty()) { + return ResponseResult.error("任务ID不能为空"); + } + if (data == null || data.getId() == null || data.getId().isEmpty()) { + return ResponseResult.error("数据对象或其ID不能为空"); + } + + try { + ImportTask task = importTaskService.getById(taskId); + if (task == null) { + return ResponseResult.error("任务不存在"); + } + + String resultJson = task.getResultJson(); + if (resultJson == null || resultJson.isEmpty()) { + return ResponseResult.error("任务结果为空"); + } + + FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class); + + FishImportResult.FishImportRow targetRow = null; + int targetIndex = -1; + for (int i = 0; i < importResult.getRows().size(); i++) { + FishImportResult.FishImportRow row = importResult.getRows().get(i); + if (row.getData() != null && data.getId().equals(row.getData().getId())) { + targetRow = row; + targetIndex = i; + break; + } + } + + if (targetIndex == -1) { + return ResponseResult.error("未找到对应的数据行"); + } + + FishImportResult.FishImportRow newRow = new FishImportResult.FishImportRow(targetRow.getRowIndex()); + newRow.setData(data); + newRow.setVdpthList(targetRow.getVdpthList()); + newRow.setPicpthList(targetRow.getPicpthList()); + newRow.setVdpthsWarnings(targetRow.getVdpthsWarnings()); + newRow.setPicpthsWarnings(targetRow.getPicpthsWarnings()); + + List warnings = new ArrayList<>(); + validateAndNormalizeData(data, newRow, warnings); + newRow.setWarnings(warnings); + String newStatus; + if (warnings.isEmpty()) { + newStatus = FishImportResult.STATUS_SUCCESS; + } else { + newStatus = FishImportResult.STATUS_FAILED; + } + newRow.setStatus(newStatus); + + importResult.getRows().set(targetIndex, newRow); + + int successCount = 0; + int failedCount = 0; + for (FishImportResult.FishImportRow row : importResult.getRows()) { + if (FishImportResult.STATUS_SUCCESS.equals(row.getStatus())) { + successCount++; + } else if (FishImportResult.STATUS_FAILED.equals(row.getStatus())) { + failedCount++; + } + } + importResult.setSuccessCount(successCount); + importResult.setFailedCount(failedCount); + + String updatedJson = objectMapper.writeValueAsString(importResult); + importTaskService.saveResultJson(taskId, updatedJson); + + Map map = new HashMap<>(); + map.put("success", true); + map.put("row", newRow); + map.put("successCount", successCount); + map.put("failedCount", failedCount); + map.put("warnings", warnings); + return ResponseResult.successData(map); + + } catch (Exception e) { + log.error("重新校验数据失败: " + e.getMessage(), e); + return ResponseResult.error("重新校验失败: " + e.getMessage()); + } + } + + private void validateAndNormalizeData(FishDraftData data, FishImportResult.FishImportRow importRow, + List warnings) { + if (data.getStcd() == null || data.getStcd().isEmpty()) { + warnings.add("stcd"); + } else { + String stcd = fishImportService.resolveFpssCode(data.getStcd(), data.getStnm()); + if (stcd == null) { + warnings.add("stcd"); + } + } + + if (data.getRstcd() != null && !data.getRstcd().isEmpty()) { + String stationCode = fishImportService.resolveStationCode(data.getRstcd(), data.getEnnm()); + if (stationCode == null) { + warnings.add("rstcd"); + } + } else { + warnings.add("rstcd"); + } + + if (data.getBaseId() != null && !data.getBaseId().isEmpty()) { + String baseId = fishImportService.resolveBaseCode(data.getBaseId(), data.getBaseName()); + if (baseId == null) { + warnings.add("baseId"); + } + } + + if (data.getRvcd() != null && !data.getRvcd().isEmpty()) { + String rvcd = fishImportService.resolveRiverCode(data.getRvcd(), data.getRvcd()); + if (rvcd == null) { + warnings.add("rvcd"); + } + } + + if (data.getTm() == null) { + warnings.add("tm"); + } + + if (data.getFtp() == null || data.getFtp().isEmpty()) { + warnings.add("ftp"); + } else { + String ftpCode = fishImportService.resolveFishDictCode(data.getFtp(), data.getFtpName()); + if (ftpCode == null) { + warnings.add("ftp"); + } + } + + if (data.getFcnt() == null) { + warnings.add("fcnt"); + } + + if (data.getStrdt() == null) { + warnings.add("strdt"); + } + + if (data.getEnddt() != null && data.getStrdt() != null && data.getEnddt().before(data.getStrdt())) { + warnings.add("enddt"); + } + + if (data.getDirection() == null || data.getDirection().isEmpty()) { + warnings.add("direction"); + } else { + String direction = fishImportService.resolveDirection(data.getDirection().trim(), importRow); + if (direction == null) { + warnings.add("direction"); + } + } + + if (data.getIsfs() == null) { + warnings.add("isfs"); + } else { + String isfs = fishImportService.resolveIsfs(String.valueOf(data.getIsfs()), importRow); + if (isfs == null) { + warnings.add("isfs"); + } + } + + if (StringUtils.hasText(data.getBaseId()) && StringUtils.hasText(data.getRstcd())) { + if (!fishImportService.validateStationBelongsToBase(data.getRstcd(), data.getBaseId())) { + warnings.add("baseId"); + warnings.add("rstcd"); + } + } + + if (StringUtils.hasText(data.getRstcd()) && StringUtils.hasText(data.getStcd())) { + if (!fishImportService.validateFpssBelongsToStation(data.getStcd(), data.getRstcd())) { + warnings.add("stcd"); + warnings.add("rstcd"); + } + } + +// if (data.getSourceType() != null && !data.getSourceType().isEmpty()) { +// data.setSourceType(fishImportService.parseSourceType(data.getSourceType().trim())); +// } + } + + @PostMapping("/validateAndMatchRow") + @Operation(summary = "校验数据并匹配到成功或失败列表") + public ResponseResult validateAndMatchRow(@RequestBody FishImportRowRequest request) { + String taskId = request.getTaskId(); + FishDraftData data = request.getData(); + + if (taskId == null || taskId.isEmpty()) { + return ResponseResult.error("任务ID不能为空"); + } + if (data == null || data.getId() == null || data.getId().isEmpty()) { + return ResponseResult.error("数据对象或其ID不能为空"); + } + + try { + ImportTask task = importTaskService.getById(taskId); + if (task == null) { + return ResponseResult.error("任务不存在"); + } + + String resultJson = task.getResultJson(); + if (resultJson == null || resultJson.isEmpty()) { + return ResponseResult.error("任务结果为空"); + } + + FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class); + + FishImportResult.FishImportRow matchedRow = findMatchingRow(importResult, data.getId()); + + FishImportResult.FishImportRow resultRow = new FishImportResult.FishImportRow(); + resultRow.setData(data); + + if (matchedRow != null) { + resultRow.setUnrecognizedFields(matchedRow.getUnrecognizedFields()); + resultRow.setWarnings(matchedRow.getWarnings()); + resultRow.setVdpthList(matchedRow.getVdpthList()); + resultRow.setPicpthList(matchedRow.getPicpthList()); + resultRow.setVdpthsWarnings(matchedRow.getVdpthsWarnings()); + resultRow.setPicpthsWarnings(matchedRow.getPicpthsWarnings()); + resultRow.setStatus(matchedRow.getStatus()); + } + + List validationErrors = validateFishDraftData(data); + + boolean originallySuccess = matchedRow != null && matchedRow.isSuccess(); + boolean originallyFailed = matchedRow != null && matchedRow.isFailed(); + boolean hasValidationErrors = !validationErrors.isEmpty(); + + boolean shouldBeSuccess = originallySuccess && !hasValidationErrors; + boolean shouldBeFailed = originallyFailed || hasValidationErrors; + + if (shouldBeSuccess) { + resultRow.setStatus(FishImportResult.STATUS_SUCCESS); + } else if (shouldBeFailed) { + resultRow.setStatus(FishImportResult.STATUS_FAILED); + } + + return ResponseResult.successData(java.util.Map.of( + "matched", matchedRow != null, + "originallySuccess", originallySuccess, + "originallyFailed", originallyFailed, + "hasValidationErrors", hasValidationErrors, + "shouldBeSuccess", shouldBeSuccess, + "shouldBeFailed", shouldBeFailed, + "row", resultRow, + "validationErrors", validationErrors + )); + + } catch (Exception e) { + log.error("校验数据失败: " + e.getMessage(), e); + return ResponseResult.error("校验失败: " + e.getMessage()); + } + } + + private FishImportResult.FishImportRow findMatchingRow(FishImportResult importResult, String dataId) { + if (importResult.getRows() != null) { + for (FishImportResult.FishImportRow row : importResult.getRows()) { + if (row.getData() != null && dataId.equals(row.getData().getId())) { + return row; + } + } + } + return null; + } + + private List validateFishDraftData(FishDraftData data) { + List errors = new ArrayList<>(); + + if (data.getStcd() == null || data.getStcd().isEmpty()) { + errors.add("电站编码不能为空"); + } + if (data.getTm() == null) { + errors.add("填报时间不能为空"); + } + if (data.getFtp() == null || data.getFtp().isEmpty()) { + errors.add("鱼类不能为空"); + } + if (data.getFcnt() == null || data.getFcnt() < 0) { + errors.add("过鱼数量不合法"); + } + if (data.getStrdt() == null) { + errors.add("开始日期不能为空"); + } + if (data.getEnddt() == null) { + errors.add("结束日期不能为空"); + } + if (data.getStrdt() != null && data.getEnddt() != null && data.getStrdt().after(data.getEnddt())) { + errors.add("开始日期不能晚于结束日期"); + } + + return errors; + } } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/data/domain/ApprovalChangeLog.java b/backend/src/main/java/com/yfd/platform/data/domain/ApprovalChangeLog.java index 171d46f..24673fd 100644 --- a/backend/src/main/java/com/yfd/platform/data/domain/ApprovalChangeLog.java +++ b/backend/src/main/java/com/yfd/platform/data/domain/ApprovalChangeLog.java @@ -1,6 +1,8 @@ package com.yfd.platform.data.domain; import com.baomidou.mybatisplus.annotation.*; +import com.yfd.platform.annotation.UserIdField; +import com.yfd.platform.annotation.UserNameField; import lombok.Data; import lombok.EqualsAndHashCode; @@ -59,12 +61,14 @@ public class ApprovalChangeLog implements Serializable { /** * 操作人ID */ + @UserIdField private String operatorId; /** * 操作人名称 */ @TableField(exist = false) + @UserNameField private String operatorName; /** diff --git a/backend/src/main/java/com/yfd/platform/data/domain/BatchSaveDraftRequest.java b/backend/src/main/java/com/yfd/platform/data/domain/BatchSaveDraftRequest.java new file mode 100644 index 0000000..93bb8b6 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/data/domain/BatchSaveDraftRequest.java @@ -0,0 +1,23 @@ +package com.yfd.platform.data.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BatchSaveDraftRequest { + + /** + * 任务id + */ + String taskId; + + /** + * 批量保存数据 + */ + List fishDraftDataList; +} diff --git a/backend/src/main/java/com/yfd/platform/data/domain/FishDraftData.java b/backend/src/main/java/com/yfd/platform/data/domain/FishDraftData.java index dc6d5a4..f29767b 100644 --- a/backend/src/main/java/com/yfd/platform/data/domain/FishDraftData.java +++ b/backend/src/main/java/com/yfd/platform/data/domain/FishDraftData.java @@ -124,7 +124,7 @@ public class FishDraftData implements Serializable { private String approvalId; /** - * 状态(DRAFT未提交 / SUBMITTED已提交 / APPROVED已通过 / REJECTED已驳回) + * 状态(DRAFT未提交 / PENDING已提交 / APPROVED已通过 / REJECTED已驳回) */ private String status; @@ -221,6 +221,12 @@ public class FishDraftData implements Serializable { @TableField(exist = false) private String stnm; + /** + * 电站编码名称 + */ + @TableField(exist = false) + private String rstcd; + /** * 鱼类名称 */ diff --git a/backend/src/main/java/com/yfd/platform/data/domain/FishImportResult.java b/backend/src/main/java/com/yfd/platform/data/domain/FishImportResult.java index 6f8c8c4..84e3a94 100644 --- a/backend/src/main/java/com/yfd/platform/data/domain/FishImportResult.java +++ b/backend/src/main/java/com/yfd/platform/data/domain/FishImportResult.java @@ -12,11 +12,15 @@ import java.util.Map; @JsonIgnoreProperties(ignoreUnknown = true) public class FishImportResult { - private List successRows; - private List failedRows; + public static final String STATUS_SUCCESS = "SUCCESS"; + public static final String STATUS_FAILED = "FAILED"; + + private List rows; private List unrecognizedFields; private Map imageFiles; private Map videoFiles; +// public Map images; +// public Map videos; private String tempDir; private String excelFileName; private String excelFilePath; @@ -29,11 +33,24 @@ public class FishImportResult { private String taskId; public FishImportResult() { - this.successRows = new ArrayList<>(); - this.failedRows = new ArrayList<>(); + this.rows = new ArrayList<>(); this.unrecognizedFields = new ArrayList<>(); this.imageFiles = new LinkedHashMap<>(); this.videoFiles = new LinkedHashMap<>(); +// this.images = new LinkedHashMap<>(); +// this.videos = new LinkedHashMap<>(); + } + + public void addSuccessRow(FishImportRow row) { + if (rows == null) rows = new ArrayList<>(); + row.setStatus(STATUS_SUCCESS); + rows.add(row); + } + + public void addFailedRow(FishImportRow row) { + if (rows == null) rows = new ArrayList<>(); + row.setStatus(STATUS_FAILED); + rows.add(row); } @Data @@ -47,6 +64,8 @@ public class FishImportResult { private List> picpthList; private List vdpthsWarnings; private List picpthsWarnings; + private String status; + public FishImportRow() { this.unrecognizedFields = new ArrayList<>(); this.warnings = new ArrayList<>(); @@ -57,13 +76,16 @@ public class FishImportResult { } public FishImportRow(int rowIndex) { + this(); this.rowIndex = rowIndex; - this.unrecognizedFields = new ArrayList<>(); - this.warnings = new ArrayList<>(); - this.vdpthsWarnings = new ArrayList<>(); - this.picpthsWarnings = new ArrayList<>(); - this.vdpthList = new ArrayList<>(); - this.picpthList = new ArrayList<>(); + } + + public boolean isSuccess() { + return STATUS_SUCCESS.equals(status); + } + + public boolean isFailed() { + return STATUS_FAILED.equals(status); } } } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/data/domain/FishImportRowRequest.java b/backend/src/main/java/com/yfd/platform/data/domain/FishImportRowRequest.java new file mode 100644 index 0000000..3dedff7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/data/domain/FishImportRowRequest.java @@ -0,0 +1,9 @@ +package com.yfd.platform.data.domain; + +import lombok.Data; + +@Data +public class FishImportRowRequest { + private String taskId; + private FishDraftData data; +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/data/domain/vo/FishDraftDataVO.java b/backend/src/main/java/com/yfd/platform/data/domain/vo/FishDraftDataVO.java index 65c0f48..24a8199 100644 --- a/backend/src/main/java/com/yfd/platform/data/domain/vo/FishDraftDataVO.java +++ b/backend/src/main/java/com/yfd/platform/data/domain/vo/FishDraftDataVO.java @@ -113,7 +113,7 @@ public class FishDraftDataVO implements Serializable { private String approvalId; /** - * 状态(DRAFT未提交 / SUBMITTED已提交 / APPROVED已通过 / REJECTED已驳回) + * 状态(DRAFT未提交 / PENDING已提交 / APPROVED已通过 / REJECTED已驳回) */ private String status; diff --git a/backend/src/main/java/com/yfd/platform/data/service/AttachmentUploadService.java b/backend/src/main/java/com/yfd/platform/data/service/AttachmentUploadService.java index 146d18a..1cd11a4 100644 --- a/backend/src/main/java/com/yfd/platform/data/service/AttachmentUploadService.java +++ b/backend/src/main/java/com/yfd/platform/data/service/AttachmentUploadService.java @@ -2,6 +2,7 @@ package com.yfd.platform.data.service; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -27,6 +28,10 @@ import java.util.stream.Collectors; @Slf4j @Service public class AttachmentUploadService { + + @Value("${attachment.token}") + private String token; + // 定义一个固定的线程池用于文件上传(建议根据服务器性能调整核心线程数) private static final ExecutorService UPLOAD_EXECUTOR = new ThreadPoolExecutor( 5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), @@ -260,4 +265,65 @@ public class AttachmentUploadService { return null; } } + + private static final String DELETE_URL = "https://211.99.26.225:12125/FileDelete"; + + public boolean deleteFile(String attachmentId) { + if (attachmentId == null || attachmentId.isEmpty()) { + log.warn("附件ID为空"); + return false; + } + + try { + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public void checkClientTrusted(X509Certificate[] chain, String authType) {} + public void checkServerTrusted(X509Certificate[] chain, String authType) {} + public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } + } + }; + + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, new SecureRandom()); + + HttpClient secureClient = HttpClient.newBuilder() + .sslContext(sc) + .build(); + + String formData = "fileId=" + attachmentId; + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(DELETE_URL)) + .header("Content-Type", "application/x-www-form-urlencoded") + .header("token", token) + .POST(HttpRequest.BodyPublishers.ofString(formData)) + .build(); + + HttpResponse response = secureClient.send(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() == 200) { + String responseBody = response.body(); + return parseDeleteResult(responseBody); + } else { + log.error("删除附件失败: {}, 状态码: {}", attachmentId, response.statusCode()); + return false; + } + } catch (Exception e) { + log.error("删除附件过程中发生异常: {}", attachmentId, e); + return false; + } + } + + private boolean parseDeleteResult(String jsonResponse) { + if (jsonResponse == null || jsonResponse.isEmpty()) { + return false; + } + try { + JSONObject json = JSONObject.parseObject(jsonResponse); + return json.containsKey("success") && json.getBoolean("success"); + } catch (Exception e) { + log.error("解析删除结果失败: {}", jsonResponse, e); + return false; + } + } } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/data/service/IApprovalChangeLogService.java b/backend/src/main/java/com/yfd/platform/data/service/IApprovalChangeLogService.java index 756e9fb..92d7e2c 100644 --- a/backend/src/main/java/com/yfd/platform/data/service/IApprovalChangeLogService.java +++ b/backend/src/main/java/com/yfd/platform/data/service/IApprovalChangeLogService.java @@ -17,7 +17,7 @@ public interface IApprovalChangeLogService extends IService { */ List getByApprovalId(String approvalId); - void fillUserNames(List list); +// void fillUserNames(List list); /** * 根据草稿数据ID查询变更记录 diff --git a/backend/src/main/java/com/yfd/platform/data/service/IFishImportService.java b/backend/src/main/java/com/yfd/platform/data/service/IFishImportService.java index 7a597ba..fd5074d 100644 --- a/backend/src/main/java/com/yfd/platform/data/service/IFishImportService.java +++ b/backend/src/main/java/com/yfd/platform/data/service/IFishImportService.java @@ -2,6 +2,7 @@ package com.yfd.platform.data.service; import com.yfd.platform.data.domain.FishImportRequest; import com.yfd.platform.data.domain.FishImportResult; +import com.yfd.platform.data.utils.ZipFileUtil; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; @@ -18,4 +19,33 @@ public interface IFishImportService { FishImportResult parseAndMapExcelFromPath(FishImportRequest request); FishImportResult parseAndMapZip(MultipartFile file, String uploadUserId); + + String resolveStationCode(String stationName); + + String resolveFpssCode(String name); + + String resolveFishDictCode(String name); + + String resolveBaseCode(String baseName); + + String resolveRiverCode(String riverName); + + String resolveStationCode(String code,String stationName); + + String resolveFpssCode(String code,String name); + String resolveFishDictCode(String code,String name); + String resolveBaseCode(String code,String baseName); + String resolveRiverCode(String code,String riverName); + + String resolveDirection(String direction, FishImportResult.FishImportRow importRow); + + String resolveIsfs(String value, FishImportResult.FishImportRow importRow); + + String parseSourceType(String value); + + boolean validateStationBelongsToBase(String stationCode, String baseId); + + boolean validateFpssBelongsToStation(String fpssCode, String stationCode); + + void processAttachments(FishImportResult result, ZipFileUtil.ZipContent zipContent); } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/data/service/impl/ApprovalChangeLogServiceImpl.java b/backend/src/main/java/com/yfd/platform/data/service/impl/ApprovalChangeLogServiceImpl.java index c6dbc3c..0e4782a 100644 --- a/backend/src/main/java/com/yfd/platform/data/service/impl/ApprovalChangeLogServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/data/service/impl/ApprovalChangeLogServiceImpl.java @@ -29,42 +29,42 @@ public class ApprovalChangeLogServiceImpl extends ServiceImpl getByApprovalId(String approvalId) { return approvalChangeLogMapper.selectByApprovalId(approvalId); } - @Override - public void fillUserNames(List list) { - if (list == null || list.isEmpty()) { - return; - } - - Set userIds = new HashSet<>(); - for (ApprovalChangeLog vo : list) { - if (StrUtil.isNotBlank(vo.getApprovalId())) { - userIds.add(vo.getApprovalId()); - } - if (StrUtil.isNotBlank(vo.getOperatorId())) { - userIds.add(vo.getOperatorId()); - } - } - - if (userIds.isEmpty()) { - return; - } - - Map userNameMap = new HashMap<>(); - List users = sysUserMapper.selectBatchIds(userIds); - for (SysUser user : users) { - userNameMap.put(user.getId(), user.getNickname()); - } - - for (ApprovalChangeLog vo : list) { - if (StrUtil.isNotBlank(vo.getApprovalId())) { - vo.setApprovalName(userNameMap.get(vo.getApprovalId())); - } - if (StrUtil.isNotBlank(vo.getOperatorId())) { - vo.setOperatorName(userNameMap.get(vo.getOperatorId())); - } - - } - } +// @Override +// public void fillUserNames(List list) { +// if (list == null || list.isEmpty()) { +// return; +// } +// +// Set userIds = new HashSet<>(); +// for (ApprovalChangeLog vo : list) { +// if (StrUtil.isNotBlank(vo.getApprovalId())) { +// userIds.add(vo.getApprovalId()); +// } +// if (StrUtil.isNotBlank(vo.getOperatorId())) { +// userIds.add(vo.getOperatorId()); +// } +// } +// +// if (userIds.isEmpty()) { +// return; +// } +// +// Map userNameMap = new HashMap<>(); +// List users = sysUserMapper.selectBatchIds(userIds); +// for (SysUser user : users) { +// userNameMap.put(user.getId(), user.getNickname()); +// } +// +// for (ApprovalChangeLog vo : list) { +// if (StrUtil.isNotBlank(vo.getApprovalId())) { +// vo.setApprovalName(userNameMap.get(vo.getApprovalId())); +// } +// if (StrUtil.isNotBlank(vo.getOperatorId())) { +// vo.setOperatorName(userNameMap.get(vo.getOperatorId())); +// } +// +// } +// } @Override public List getByDataId(String dataId) { return approvalChangeLogMapper.selectByDataId(dataId); diff --git a/backend/src/main/java/com/yfd/platform/data/service/impl/ApprovalMainServiceImpl.java b/backend/src/main/java/com/yfd/platform/data/service/impl/ApprovalMainServiceImpl.java index 72f3d57..cab10cd 100644 --- a/backend/src/main/java/com/yfd/platform/data/service/impl/ApprovalMainServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/data/service/impl/ApprovalMainServiceImpl.java @@ -106,7 +106,7 @@ public class ApprovalMainServiceImpl extends ServiceImpl validIds = new ArrayList<>(); for (FishDraftData fishDraftData : dataList) { - if ("SUBMITTED".equals(fishDraftData.getStatus())) { + if ("PENDING".equals(fishDraftData.getStatus())) { validIds.add(fishDraftData.getId()); if (fishDraftData.getApprovalId() != null) { processedApprovalIds.add(fishDraftData.getApprovalId()); @@ -340,7 +339,7 @@ public class FishDraftDataServiceImpl extends ServiceImpl EXCEL_COLUMN_INDEX_MAPPING = new LinkedHashMap<>(); private static final Map STATION_NAME_CACHE = new LinkedHashMap<>(); + private static final Map STATION_CODE_TO_NAME_CACHE = new LinkedHashMap<>(); + private static final Map STATION_TO_BASE_CACHE = new LinkedHashMap<>(); private static final Map BASE_NAME_CACHE = new LinkedHashMap<>(); + private static final Map BASE_CODE_TO_NAME_CACHE = new LinkedHashMap<>(); private static final Map RIVER_NAME_CACHE = new LinkedHashMap<>(); + private static final Map RIVER_CODE_TO_NAME_CACHE = new LinkedHashMap<>(); private static final Map FISH_DICT_CACHE = new LinkedHashMap<>(); + private static final Map FISH_CODE_TO_NAME_CACHE = new LinkedHashMap<>(); private static final Map FPSS_BH_CACHE = new LinkedHashMap<>(); + private static final Map FPSS_CODE_TO_NAME_CACHE = new LinkedHashMap<>(); + private static final Map FPSS_TO_STATION_CACHE = new LinkedHashMap<>(); private static final Map ISFS_DICT_CACHE = new LinkedHashMap<>(); private static final String DICT_CODE_FISH = "FISH_TYPE"; @@ -146,7 +153,6 @@ public class FishImportServiceImpl implements IFishImportService { return result; } - List unrecognizedHeaders = new ArrayList<>(); Map columnIndexMap = new HashMap<>(EXCEL_COLUMN_INDEX_MAPPING); int totalRows = sheet.getLastRowNum(); @@ -156,29 +162,26 @@ public class FishImportServiceImpl implements IFishImportService { continue; } - FishImportResult.FishImportRow importRow = parseRow(row, columnIndexMap, i); + FishImportResult.FishImportRow importRow = parseRow(result, row, columnIndexMap, i); result.setTotalCount(result.getTotalCount() + 1); if (importRow.getData() != null && importRow.getWarnings().isEmpty()) { - result.getSuccessRows().add(importRow); + result.addSuccessRow(importRow); result.setSuccessCount(result.getSuccessCount() + 1); } else { - result.getFailedRows().add(importRow); + result.addFailedRow(importRow); result.setFailedCount(result.getFailedCount() + 1); } } - result.setSummary(String.format("共解析%d条数据,失败%d条,未识别字段:%s", - result.getSuccessCount(), result.getFailedCount(), - unrecognizedHeaders.isEmpty() ? "无" : String.join(", ", unrecognizedHeaders))); + result.setSummary(String.format("共解析%d条数据,失败%d条", + result.getSuccessCount(), result.getFailedCount())); return result; } - private FishImportResult.FishImportRow parseRow(Row row, Map columnIndexMap, int rowIndex) { + private FishImportResult.FishImportRow parseRow(FishImportResult result, Row row, Map columnIndexMap, int rowIndex) { FishImportResult.FishImportRow importRow = new FishImportResult.FishImportRow(rowIndex); FishDraftData data = new FishDraftData(); data.setId(UUID.randomUUID().toString()); - List missingRequiredFields = new ArrayList<>(); - for (Map.Entry entry : columnIndexMap.entrySet()) { Integer columnIndex = entry.getKey(); String fieldName = entry.getValue(); @@ -188,15 +191,17 @@ public class FishImportServiceImpl implements IFishImportService { switch (fieldName) { case "stationName": if (!StringUtils.hasText(cellValue)) { - importRow.getWarnings().add("ennm"); + importRow.getWarnings().add("rstcd"); data.setEnnm(cellValue.trim()); + data.setRstcd(cellValue); } else { String stcd = resolveStationCode(cellValue.trim()); if (stcd == null) { - importRow.getWarnings().add("ennm"); + importRow.getWarnings().add("rstcd"); data.setEnnm(cellValue.trim()); } else { data.setEnnm(cellValue.trim()); + data.setRstcd(stcd); } } break; @@ -274,12 +279,12 @@ public class FishImportServiceImpl implements IFishImportService { break; case "wt": if (!StringUtils.hasText(cellValue)) { - importRow.getWarnings().add(fieldName); +// importRow.getWarnings().add(fieldName); data.setWt(parseBigDecimal(cellValue)); } else { BigDecimal wt = parseBigDecimal(cellValue); if (wt == null) { - importRow.getWarnings().add(fieldName); +// importRow.getWarnings().add(fieldName); data.setWt(parseBigDecimal(cellValue)); } data.setWt(wt); @@ -318,17 +323,54 @@ public class FishImportServiceImpl implements IFishImportService { data.setMouth(parseInteger(cellValue)); break; case "vdpth": - data.setVdpth(cellValue.trim()); - data.setVdpthName(cellValue.trim()); + String vdpth = cellValue.trim(); + List vdpthList = new ArrayList<>(); + Map videoFiles = result.getVideoFiles(); + + for (String fileName : vdpth.split(";")) { + for (String entryName : videoFiles.keySet()) { + if (entryName.equals(fileName) || entryName.endsWith("/" + fileName) || entryName.endsWith("\\" + fileName)) { + Map objectObjectHashMap = new HashMap<>(); + objectObjectHashMap.put("name", fileName); + objectObjectHashMap.put("value", fileName); + importRow.getVdpthList().add(objectObjectHashMap); + vdpthList.add(fileName); + } + } + + } + String vdpthJoin = StrUtil.join(";", vdpthList); + data.setVdpth(vdpthJoin); + data.setVdpthName(vdpthJoin); + break; case "picpth": - data.setPicpth(cellValue.trim()); - data.setPicpthName(cellValue.trim()); + String picpth = cellValue.trim(); + List picpthList = new ArrayList<>(); + Map imageFiles = result.getImageFiles(); + + for (String fileName : picpth.split(";")) { + for (String entryName : imageFiles.keySet()) { + if (entryName.equals(fileName) || entryName.endsWith("/" + fileName) || entryName.endsWith("\\" + fileName)) { + Map objectObjectHashMap = new HashMap<>(); + objectObjectHashMap.put("name", fileName); + objectObjectHashMap.put("value", fileName); + importRow.getPicpthList().add(objectObjectHashMap); + picpthList.add(fileName); + } + } + + } + String picpthJoin = StrUtil.join(";", picpthList); + data.setPicpth(picpthJoin); + data.setPicpthName(picpthJoin); break; case "isfs": if (StringUtils.hasText(cellValue)) { String isfs = resolveIsfs(cellValue.trim(), importRow); data.setIsfs("1".equals(isfs) ? 1 : 0); + } else { + importRow.getWarnings().add("isfs"); } break; case "sourceType": @@ -336,7 +378,8 @@ public class FishImportServiceImpl implements IFishImportService { break; case "stnm": if (!StringUtils.hasText(cellValue)) { - missingRequiredFields.add("stcd"); + importRow.getWarnings().add("stcd"); + data.setStcd(cellValue); } else { String stcd = resolveFpssCode(cellValue.trim()); if (stcd == null) { @@ -344,7 +387,7 @@ public class FishImportServiceImpl implements IFishImportService { data.setStcd(cellValue.trim()); data.setStnm(cellValue.trim()); } else { - data.setStnm(stcd); + data.setStnm(cellValue.trim()); data.setStcd(stcd); } } @@ -357,10 +400,41 @@ public class FishImportServiceImpl implements IFishImportService { // importRow.getWarnings().add("字段[" + fieldName + "]解析异常: " + e.getMessage()); } } + validateStationFpssRelation(data, importRow); importRow.setData(data); return importRow; } + private void validateStationFpssRelation(FishDraftData data, FishImportResult.FishImportRow importRow) { + loadStationAndBaseCache(); + if (StringUtils.hasText(data.getBaseId()) && StringUtils.hasText(data.getRstcd())) { + if (!validateStationBelongsToBase(data.getRstcd(), data.getBaseId())) { + if (!importRow.getWarnings().contains("baseId")) { + importRow.getWarnings().add("baseId"); + } + if (!importRow.getWarnings().contains("rstcd")) { + importRow.getWarnings().add("rstcd"); + } + if (!importRow.getWarnings().contains("stcd")) { + importRow.getWarnings().add("stcd"); + } + } + } + if (StringUtils.hasText(data.getRstcd()) && StringUtils.hasText(data.getStcd())) { + if (!validateFpssBelongsToStation(data.getStcd(), data.getRstcd())) { + if (!importRow.getWarnings().contains("baseId")) { + importRow.getWarnings().add("baseId"); + } + if (!importRow.getWarnings().contains("stcd")) { + importRow.getWarnings().add("stcd"); + } + if (!importRow.getWarnings().contains("rstcd")) { + importRow.getWarnings().add("rstcd"); + } + } + } + } + private void loadStationAndBaseCache() { if (STATION_NAME_CACHE.isEmpty()) { List stationList = engInfoBHMapper.selectList(null); @@ -369,7 +443,12 @@ public class FishImportServiceImpl implements IFishImportService { STATION_NAME_CACHE.put(station.getEnnm().trim().toLowerCase(), station.getStcd()); } if (StringUtils.hasText(station.getStcd())) { - STATION_NAME_CACHE.put(station.getStcd().trim().toLowerCase(), station.getStcd()); + if (StringUtils.hasText(station.getEnnm())) { + STATION_CODE_TO_NAME_CACHE.put(station.getStcd().trim().toLowerCase(), station.getEnnm().trim().toLowerCase()); + } + if (StringUtils.hasText(station.getBaseId())) { + STATION_TO_BASE_CACHE.put(station.getStcd().trim().toLowerCase(), station.getBaseId()); + } } } } @@ -381,7 +460,9 @@ public class FishImportServiceImpl implements IFishImportService { BASE_NAME_CACHE.put(base.getBasename().trim().toLowerCase(), base.getBaseid()); } if (StringUtils.hasText(base.getBaseid())) { - BASE_NAME_CACHE.put(base.getBaseid().trim().toLowerCase(), base.getBaseid()); + if (StringUtils.hasText(base.getBasename())) { + BASE_CODE_TO_NAME_CACHE.put(base.getBaseid().trim().toLowerCase(), base.getBasename().trim().toLowerCase()); + } } } } @@ -393,7 +474,9 @@ public class FishImportServiceImpl implements IFishImportService { RIVER_NAME_CACHE.put(river.getRvnm().trim().toLowerCase(), river.getRvcd()); } if (StringUtils.hasText(river.getRvcd())) { - RIVER_NAME_CACHE.put(river.getRvcd().trim().toLowerCase(), river.getRvcd()); + if (StringUtils.hasText(river.getRvnm())) { + RIVER_CODE_TO_NAME_CACHE.put(river.getRvcd().trim().toLowerCase(), river.getRvnm().trim().toLowerCase()); + } } } } @@ -405,7 +488,9 @@ public class FishImportServiceImpl implements IFishImportService { FISH_DICT_CACHE.put(fishDictoryB.getName().trim().toLowerCase(), fishDictoryB.getCode()); } if (StringUtils.hasText(fishDictoryB.getCode())) { - FISH_DICT_CACHE.put(fishDictoryB.getCode().trim().toLowerCase(), fishDictoryB.getCode()); + if (StringUtils.hasText(fishDictoryB.getName())) { + FISH_CODE_TO_NAME_CACHE.put(fishDictoryB.getCode().trim().toLowerCase(), fishDictoryB.getName().trim().toLowerCase()); + } } } } @@ -417,15 +502,19 @@ public class FishImportServiceImpl implements IFishImportService { FPSS_BH_CACHE.put(sdFpssBH.getStnm().trim().toLowerCase(), sdFpssBH.getStcd()); } if (StringUtils.hasText(sdFpssBH.getStcd())) { - FPSS_BH_CACHE.put(sdFpssBH.getStcd().trim().toLowerCase(), sdFpssBH.getStcd()); + if (StringUtils.hasText(sdFpssBH.getStnm())) { + FPSS_CODE_TO_NAME_CACHE.put(sdFpssBH.getStcd().trim().toLowerCase(), sdFpssBH.getStnm().trim().toLowerCase()); + } + if (StringUtils.hasText(sdFpssBH.getRstcd())) { + FPSS_TO_STATION_CACHE.put(sdFpssBH.getStcd().trim().toLowerCase(), sdFpssBH.getRstcd()); + } } } } } - - private String resolveStationCode(String stationName) { + public String resolveStationCode(String stationName) { if (stationName == null) { return null; } @@ -434,14 +523,14 @@ public class FishImportServiceImpl implements IFishImportService { return STATION_NAME_CACHE.get(lowerName); } for (Map.Entry entry : STATION_NAME_CACHE.entrySet()) { - if (entry.getKey().contains(lowerName) || lowerName.contains(entry.getKey())) { + if (entry.getKey().contains(lowerName)) { return entry.getValue(); } } return null; } - private String resolveFpssCode(String name) { + public String resolveFpssCode(String name) { if (name == null) { return null; } @@ -450,14 +539,56 @@ public class FishImportServiceImpl implements IFishImportService { return FPSS_BH_CACHE.get(lowerName); } for (Map.Entry entry : FPSS_BH_CACHE.entrySet()) { - if (entry.getKey().contains(lowerName) || lowerName.contains(entry.getKey())) { + if (entry.getKey().contains(lowerName)) { return entry.getValue(); } } return null; } - private String resolveFishDictCode(String name) { + @Override + public String resolveStationCode(String code, String stationName) { + if (code == null && stationName == null) { + return null; + } + loadStationAndBaseCache(); + String lowerCode = code != null ? code.trim().toLowerCase() : null; + String lowerName = stationName != null ? stationName.trim().toLowerCase() : null; + if (lowerCode != null && lowerName != null) { + String cachedName = STATION_CODE_TO_NAME_CACHE.get(lowerCode); + if (cachedName != null && cachedName.equals(lowerName)) { + return code.trim(); + } + } + String resultByName = resolveStationCode(stationName); + if (resultByName != null && resultByName.equalsIgnoreCase(code)) { + return resultByName; + } + return null; + } + + @Override + public String resolveFpssCode(String code, String name) { + if (code == null && name == null) { + return null; + } + loadStationAndBaseCache(); + String lowerCode = code != null ? code.trim().toLowerCase() : null; + String lowerName = name != null ? name.trim().toLowerCase() : null; + if (lowerCode != null && lowerName != null) { + String cachedName = FPSS_CODE_TO_NAME_CACHE.get(lowerCode); + if (cachedName != null && cachedName.equals(lowerName)) { + return code.trim(); + } + } + String resultByName = resolveFpssCode(name); + if (resultByName != null && resultByName.equalsIgnoreCase(code)) { + return resultByName; + } + return null; + } + + public String resolveFishDictCode(String name) { if (name == null) { return null; } @@ -473,7 +604,28 @@ public class FishImportServiceImpl implements IFishImportService { return null; } - private String resolveBaseCode(String baseName) { + @Override + public String resolveFishDictCode(String code, String name) { + if (code == null && name == null) { + return null; + } + loadStationAndBaseCache(); + String lowerCode = code != null ? code.trim().toLowerCase() : null; + String lowerName = name != null ? name.trim().toLowerCase() : null; + if (lowerCode != null && lowerName != null) { + String cachedName = FISH_CODE_TO_NAME_CACHE.get(lowerCode); + if (cachedName != null && cachedName.equals(lowerName)) { + return code.trim(); + } + } + String resultByName = resolveFishDictCode(name); + if (resultByName != null && resultByName.equalsIgnoreCase(code)) { + return resultByName; + } + return null; + } + + public String resolveBaseCode(String baseName) { if (baseName == null) { return null; } @@ -482,14 +634,35 @@ public class FishImportServiceImpl implements IFishImportService { return BASE_NAME_CACHE.get(lowerName); } for (Map.Entry entry : BASE_NAME_CACHE.entrySet()) { - if (entry.getKey().contains(lowerName) || lowerName.contains(entry.getKey())) { + if (entry.getKey().contains(lowerName)) { return entry.getValue(); } } return null; } - private String resolveRiverCode(String riverName) { + @Override + public String resolveBaseCode(String code, String baseName) { + if (code == null && baseName == null) { + return null; + } + loadStationAndBaseCache(); + String lowerCode = code != null ? code.trim().toLowerCase() : null; + String lowerName = baseName != null ? baseName.trim().toLowerCase() : null; + if (lowerCode != null && lowerName != null) { + String cachedName = BASE_CODE_TO_NAME_CACHE.get(lowerCode); + if (cachedName != null && cachedName.equals(lowerName)) { + return code.trim(); + } + } + String resultByName = resolveBaseCode(baseName); + if (resultByName != null && resultByName.equalsIgnoreCase(code)) { + return resultByName; + } + return null; + } + + public String resolveRiverCode(String riverName) { if (riverName == null) { return null; } @@ -498,13 +671,56 @@ public class FishImportServiceImpl implements IFishImportService { return RIVER_NAME_CACHE.get(lowerName); } for (Map.Entry entry : RIVER_NAME_CACHE.entrySet()) { - if (entry.getKey().contains(lowerName) || lowerName.contains(entry.getKey())) { + if (entry.getKey().contains(lowerName)) { return entry.getValue(); } } return null; } + @Override + public String resolveRiverCode(String code, String riverName) { + if (code == null && riverName == null) { + return null; + } + loadStationAndBaseCache(); + String lowerCode = code != null ? code.trim().toLowerCase() : null; + String lowerName = riverName != null ? riverName.trim().toLowerCase() : null; + if (lowerCode != null && lowerName != null) { + String cachedName = RIVER_CODE_TO_NAME_CACHE.get(lowerCode); + if (cachedName != null && cachedName.equals(lowerName)) { + return code.trim(); + } + } + String resultByName = resolveRiverCode(riverName); + if (resultByName != null && resultByName.equalsIgnoreCase(code)) { + return resultByName; + } + return null; + } + + @Override + public boolean validateStationBelongsToBase(String stationCode, String baseId) { + if (stationCode == null || baseId == null) { + return false; + } + loadStationAndBaseCache(); + String stationLower = stationCode.trim().toLowerCase(); + String cachedBaseId = STATION_TO_BASE_CACHE.get(stationLower); + return baseId.equalsIgnoreCase(cachedBaseId); + } + + @Override + public boolean validateFpssBelongsToStation(String fpssCode, String stationCode) { + if (fpssCode == null || stationCode == null) { + return false; + } + loadStationAndBaseCache(); + String fpssLower = fpssCode.trim().toLowerCase(); + String cachedStationCode = FPSS_TO_STATION_CACHE.get(fpssLower); + return stationCode.equalsIgnoreCase(cachedStationCode); + } + private String validateFishType(String fishName, FishImportResult.FishImportRow importRow) { if (fishName == null) { return null; @@ -536,7 +752,7 @@ public class FishImportServiceImpl implements IFishImportService { return facilityName; } - private String resolveDirection(String direction, FishImportResult.FishImportRow importRow) { + public String resolveDirection(String direction, FishImportResult.FishImportRow importRow) { if (direction == null) { return null; } @@ -555,14 +771,14 @@ public class FishImportServiceImpl implements IFishImportService { return direction; } - private String resolveIsfs(String value, FishImportResult.FishImportRow importRow) { + public String resolveIsfs(String value, FishImportResult.FishImportRow importRow) { if (value == null) { return null; } String lowerName = value.toLowerCase().trim(); if (lowerName.equals("是")) { return "1"; - } else if (lowerName.equals("否") ) { + } else if (lowerName.equals("否")) { return "0"; } importRow.getWarnings().add("isfs"); @@ -697,6 +913,7 @@ public class FishImportServiceImpl implements IFishImportService { /** * 解析 BigDecimal 类型数据 + * * @param value 字符串数值 * @return BigDecimal 对象,解析失败或为空时返回 null */ @@ -712,7 +929,7 @@ public class FishImportServiceImpl implements IFishImportService { } - private String parseSourceType(String value) { + public String parseSourceType(String value) { if (!StringUtils.hasText(value)) { return "IMPORT"; } @@ -732,6 +949,7 @@ public class FishImportServiceImpl implements IFishImportService { FishImportResult result = new FishImportResult(); try { + log.info("开始解析ZIP文件..."); ZipFileUtil.ZipContent zipContent = ZipFileUtil.extractZipToTemp(file); if (zipContent.excelFilePath == null) { @@ -739,22 +957,23 @@ public class FishImportServiceImpl implements IFishImportService { ZipFileUtil.cleanupTempDir(zipContent.tempDir); return result; } - + result.setTempDir(zipContent.tempDir); + result.setImageFiles(zipContent.images); + result.setVideoFiles(zipContent.videos); try (Workbook workbook = new XSSFWorkbook(new FileInputStream(zipContent.excelFilePath))) { Sheet sheet = workbook.getSheetAt(0); result = parseSheet(sheet, result); } - - result.setImageFiles(zipContent.images); - result.setVideoFiles(zipContent.videos); - result.setTempDir(zipContent.tempDir); result.setExcelFileName(zipContent.excelFileName); result.setExcelFilePath(zipContent.excelFilePath); - - processAttachments(result, zipContent); - - result.setSummary(result.getSummary() + String.format("\nZIP内容: 发现%d张图片, %d个视频, 临时目录: %s", - zipContent.images.size(), zipContent.videos.size(), zipContent.tempDir)); +// result.setVideos(zipContent.videos); +// result.setImages(zipContent.images); + log.info("ZIP文件解析完成"); +// processAttachments(result, zipContent); +// +// log.info("ZIP文件处理完成"); + result.setSummary(result.getSummary() + String.format("\nZIP内容: 发现%d张图片, %d个视频", + zipContent.images.size(), zipContent.videos.size())); return result; @@ -766,8 +985,9 @@ public class FishImportServiceImpl implements IFishImportService { } } - private void processAttachments(FishImportResult result, ZipFileUtil.ZipContent zipContent) { - for (FishImportResult.FishImportRow importRow : result.getFailedRows()) { + @Override + public void processAttachments(FishImportResult result, ZipFileUtil.ZipContent zipContent) { + for (FishImportResult.FishImportRow importRow : result.getRows()) { FishDraftData data = importRow.getData(); if (data == null) { continue; @@ -777,32 +997,12 @@ public class FishImportServiceImpl implements IFishImportService { String picpth = data.getPicpth(); if (StringUtils.hasText(vdpth)) { - String uploadedVdpth = processVideoAttachments(importRow,vdpth, zipContent.videos); + String uploadedVdpth = processVideoAttachments(importRow, vdpth, zipContent.videos); data.setVdpth(uploadedVdpth); } if (StringUtils.hasText(picpth)) { - String uploadedPicpth = processImageAttachments(importRow,picpth, zipContent.images); - data.setPicpth(uploadedPicpth); - } - } - - for (FishImportResult.FishImportRow importRow : result.getSuccessRows()) { - FishDraftData data = importRow.getData(); - if (data == null) { - continue; - } - - String vdpth = data.getVdpth(); - String picpth = data.getPicpth(); - - if (StringUtils.hasText(vdpth)) { - String uploadedVdpth = processVideoAttachments(importRow,vdpth, zipContent.videos); - data.setVdpth(uploadedVdpth); - } - - if (StringUtils.hasText(picpth)) { - String uploadedPicpth = processImageAttachments(importRow,picpth, zipContent.images); + String uploadedPicpth = processImageAttachments(importRow, picpth, zipContent.images); data.setPicpth(uploadedPicpth); } } @@ -849,7 +1049,7 @@ public class FishImportServiceImpl implements IFishImportService { return String.join(",", attachmentIds); } - private String processImageAttachments(FishImportResult.FishImportRow importRow,String imageNames, Map imageMap) { + private String processImageAttachments(FishImportResult.FishImportRow importRow, String imageNames, Map imageMap) { if (imageNames == null || imageNames.isEmpty() || imageMap == null || imageMap.isEmpty()) { return imageNames; } diff --git a/backend/src/main/java/com/yfd/platform/data/service/impl/ImportTaskServiceImpl.java b/backend/src/main/java/com/yfd/platform/data/service/impl/ImportTaskServiceImpl.java index 4dfbb00..396b213 100644 --- a/backend/src/main/java/com/yfd/platform/data/service/impl/ImportTaskServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/data/service/impl/ImportTaskServiceImpl.java @@ -1,10 +1,15 @@ package com.yfd.platform.data.service.impl; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yfd.platform.data.domain.FishImportResult; import com.yfd.platform.data.domain.ImportTask; import com.yfd.platform.data.mapper.ImportTaskMapper; +import com.yfd.platform.data.service.AttachmentUploadService; import com.yfd.platform.data.service.IImportTaskService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -25,6 +30,12 @@ public class ImportTaskServiceImpl extends ServiceImpl queryPageList(Page page, String bizType, String status, String uploadUserId) { return this.page(page, this.lambdaQuery() @@ -125,7 +136,21 @@ public class ImportTaskServiceImpl extends ServiceImpl fileIds = StrUtil.split(vdpth, ","); + fileIds.forEach(fileId -> attachmentUploadService.deleteFile(fileId)); + } + String picpth = successRow.getData().getPicpth(); + if (StringUtils.hasText(picpth)) { + List fileIds = StrUtil.split(vdpth, ","); + fileIds.forEach(fileId -> attachmentUploadService.deleteFile(fileId)); + } + } + String tempDir = importResult.getTempDir(); + // del 方法会递归删除目录及其所有内容 + FileUtil.del(tempDir); + } catch (Exception e) { + e.printStackTrace(); + // ignore parse error + } + } importTask.setStatus("CANCELLED"); importTask.setErrorMsg("用户取消: " + operatorId); importTask.setUpdatedAt(new Date()); diff --git a/backend/src/main/java/com/yfd/platform/data/utils/ZipFileUtil.java b/backend/src/main/java/com/yfd/platform/data/utils/ZipFileUtil.java index 4d921e9..30fedc3 100644 --- a/backend/src/main/java/com/yfd/platform/data/utils/ZipFileUtil.java +++ b/backend/src/main/java/com/yfd/platform/data/utils/ZipFileUtil.java @@ -1,5 +1,6 @@ package com.yfd.platform.data.utils; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; @@ -18,6 +19,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @Component +@Slf4j public class ZipFileUtil { private static String tempBaseDir; @@ -35,9 +37,9 @@ public class ZipFileUtil { } String os = System.getProperty("os.name").toLowerCase(); if (os.contains("win")) { - return "D:\\zip_import_temp"; + return "D:\\qgc-platform\\zip_import_temp"; } else { - return "/tmp/zip_import_temp"; + return "/qgc-platform/tmp/zip_import_temp"; } } @@ -76,14 +78,17 @@ public class ZipFileUtil { String taskId = UUID.randomUUID().toString().substring(0, 8); Path tempDirPath = Paths.get(baseTempDir, "zip_" + taskId); + + log.info("extractZipToTemp: {}", tempDirPath); Files.createDirectories(tempDirPath); content.tempDir = tempDirPath.toString(); - File zipFile = new File(tempDirPath.toFile(), "upload.zip"); file.transferTo(zipFile); try { + log.info("--------------isValidZipFile------------"); if (!isValidZipFile(zipFile)) { + log.info("--------------文件不是有效的ZIP格式或已损坏------------"); throw new IOException("文件不是有效的ZIP格式或已损坏"); } @@ -107,6 +112,7 @@ public class ZipFileUtil { } } + log.error("extractZipToTemp: {}", lastException.getMessage()); throw lastException != null ? lastException : new IOException("无法解析ZIP文件"); } finally { diff --git a/backend/src/main/java/com/yfd/platform/env/controller/SdEngInfoBHController.java b/backend/src/main/java/com/yfd/platform/env/controller/SdEngInfoBHController.java index e810b84..aa0f625 100644 --- a/backend/src/main/java/com/yfd/platform/env/controller/SdEngInfoBHController.java +++ b/backend/src/main/java/com/yfd/platform/env/controller/SdEngInfoBHController.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.annotation.Log; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.env.domain.SdEngInfoBH; +import com.yfd.platform.env.domain.SdEngInfoBHRequest; import com.yfd.platform.env.service.ISdEngInfoBHService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -69,18 +70,16 @@ public class SdEngInfoBHController { return ResponseResult.successData(engInfoBHService.getByRvcd(rvcd)); } - @GetMapping("/dropdown") + @PostMapping("/dropdown") @Operation(summary = "电站下拉列表(根据基地编码筛选 + 支持名称模糊搜索)") - public ResponseResult dropdown(@RequestParam(required = false) String baseId, - @RequestParam(required = false) String ennm) { - return ResponseResult.successData(engInfoBHService.selectForDropdown(baseId, ennm)); + public ResponseResult dropdown(@RequestBody SdEngInfoBHRequest sdEngInfoBHRequest) { + return ResponseResult.successData(engInfoBHService.selectForDropdown(sdEngInfoBHRequest)); } - @GetMapping("/regDropdown") + @PostMapping("/regDropdown") @Operation(summary = "电站下拉列表(根据基地编码筛选 + 支持名称模糊搜索)注册") - public ResponseResult regDropdown(@RequestParam(required = false) String baseId, - @RequestParam(required = false) String ennm) { - return ResponseResult.successData(engInfoBHService.selectRegDropdown(baseId, ennm)); + public ResponseResult regDropdown(@RequestBody SdEngInfoBHRequest sdEngInfoBHRequest) { + return ResponseResult.successData(engInfoBHService.selectRegDropdown(sdEngInfoBHRequest)); } @Log(module = "电站管理", value = "新增电站") diff --git a/backend/src/main/java/com/yfd/platform/env/controller/SdFishDictoryBController.java b/backend/src/main/java/com/yfd/platform/env/controller/SdFishDictoryBController.java index 25d4586..d7c6ee7 100644 --- a/backend/src/main/java/com/yfd/platform/env/controller/SdFishDictoryBController.java +++ b/backend/src/main/java/com/yfd/platform/env/controller/SdFishDictoryBController.java @@ -1,6 +1,7 @@ package com.yfd.platform.env.controller; import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.annotation.Log; import com.yfd.platform.common.DataSourceRequest; @@ -55,8 +56,8 @@ public class SdFishDictoryBController { @Operation(summary = "根据名称查询所有鱼类字典") public ResponseResult listByName(@RequestParam(required = false) String name) { return ResponseResult.successData(sdFishDictoryBService.list( - new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() - .eq(StrUtil.isNotBlank(name), SdFishDictoryB::getName, name) + new LambdaQueryWrapper() + .eq(StrUtil.isNotBlank(name), SdFishDictoryB::getName, name).select(SdFishDictoryB::getId, SdFishDictoryB::getName, SdFishDictoryB::getAlias) )); } @@ -89,4 +90,12 @@ public class SdFishDictoryBController { boolean result = sdFishDictoryBService.deleteById(id); return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败"); } + + @GetMapping("/similar") + @Operation(summary = "查询相似鱼类列表") + public ResponseResult findSimilarFish( + @RequestParam String name, + @RequestParam(required = false, defaultValue = "10") Integer limit) { + return ResponseResult.successData(sdFishDictoryBService.findSimilarFish(name, limit)); + } } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/env/controller/SdHbrvDicController.java b/backend/src/main/java/com/yfd/platform/env/controller/SdHbrvDicController.java index 1b87633..5faa2c0 100644 --- a/backend/src/main/java/com/yfd/platform/env/controller/SdHbrvDicController.java +++ b/backend/src/main/java/com/yfd/platform/env/controller/SdHbrvDicController.java @@ -106,7 +106,7 @@ public class SdHbrvDicController { } @GetMapping("/regDropdown") - @Operation(summary = "下拉框列表查询") + @Operation(summary = "下拉框列表查询(注册)") public ResponseResult regDropdown( @RequestParam(required = false) String hbrvnm, @RequestParam(required = false) String baseid) { diff --git a/backend/src/main/java/com/yfd/platform/env/controller/SdHycdDicController.java b/backend/src/main/java/com/yfd/platform/env/controller/SdHycdDicController.java index db5351f..cead7e6 100644 --- a/backend/src/main/java/com/yfd/platform/env/controller/SdHycdDicController.java +++ b/backend/src/main/java/com/yfd/platform/env/controller/SdHycdDicController.java @@ -66,16 +66,20 @@ public class SdHycdDicController { @Operation(summary = "下拉框列表查询") public ResponseResult selectForDropdown( @RequestParam(required = false) String hynm, - @RequestParam(required = false) Integer grd) { - return ResponseResult.successData(hycdDicService.selectForDropdown(hynm, grd)); + @RequestParam(required = false) Integer grd, + @RequestParam(required = false) Integer lx, + @RequestParam(required = false) String phycd) { + return ResponseResult.successData(hycdDicService.selectForDropdown(hynm, grd,lx,phycd)); } @GetMapping("/regDropdown") @Operation(summary = "下拉框列表查询(注册)") public ResponseResult regDropdown( @RequestParam(required = false) String hynm, - @RequestParam(required = false) Integer grd) { - return ResponseResult.successData(hycdDicService.regDropdown(hynm, grd)); + @RequestParam(required = false) Integer grd, + @RequestParam(required = false) Integer lx, + @RequestParam(required = false) String phycd) { + return ResponseResult.successData(hycdDicService.regDropdown(hynm, grd,lx,phycd)); } diff --git a/backend/src/main/java/com/yfd/platform/env/domain/SdEngInfoBHRequest.java b/backend/src/main/java/com/yfd/platform/env/domain/SdEngInfoBHRequest.java new file mode 100644 index 0000000..58691ab --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/env/domain/SdEngInfoBHRequest.java @@ -0,0 +1,18 @@ +package com.yfd.platform.env.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SdEngInfoBHRequest { + private String baseId; + private List basIds; + private List hbrvcds; + private List rvcds; + private String ennm; +} diff --git a/backend/src/main/java/com/yfd/platform/env/domain/SdFpssBH.java b/backend/src/main/java/com/yfd/platform/env/domain/SdFpssBH.java index 993b6c5..1e0c733 100644 --- a/backend/src/main/java/com/yfd/platform/env/domain/SdFpssBH.java +++ b/backend/src/main/java/com/yfd/platform/env/domain/SdFpssBH.java @@ -228,7 +228,7 @@ public class SdFpssBH implements Serializable { /** * 鱼道或仿自然通道-流速,单位:m/s */ - private BigDecimal v; + private String v; /** * 仿自然通道断面形状 @@ -293,7 +293,7 @@ public class SdFpssBH implements Serializable { /** * 升鱼机集鱼槽流量,单位:m3/s */ - private BigDecimal syjq; + private String syjq; /** * 升鱼机断面尺寸(长*宽*高),单位:m @@ -303,7 +303,7 @@ public class SdFpssBH implements Serializable { /** * 升鱼机集鱼槽水深,单位:m */ - private BigDecimal syjwdp; + private String syjwdp; /** * 设计过鱼规模,单位:尾 diff --git a/backend/src/main/java/com/yfd/platform/env/domain/SdHycdDic.java b/backend/src/main/java/com/yfd/platform/env/domain/SdHycdDic.java index edf1562..aadce20 100644 --- a/backend/src/main/java/com/yfd/platform/env/domain/SdHycdDic.java +++ b/backend/src/main/java/com/yfd/platform/env/domain/SdHycdDic.java @@ -49,15 +49,6 @@ public class SdHycdDic implements Serializable { */ private Integer orderIndex; - /** - * 中心经度 - */ - private String lgtd; - - /** - * 中心纬度 - */ - private String lttd; /** * 所属国家,关联SD_COUNTRY_B.COUNTRY_ID @@ -72,7 +63,7 @@ public class SdHycdDic implements Serializable { /** * 简介 */ - private String des; + private String introduce; /** * 介绍图片 @@ -87,6 +78,6 @@ public class SdHycdDic implements Serializable { /** * 类型 1:集团 ;2:公司 */ - private String lx; + private Integer lx; } diff --git a/backend/src/main/java/com/yfd/platform/env/service/ISdEngInfoBHService.java b/backend/src/main/java/com/yfd/platform/env/service/ISdEngInfoBHService.java index 29de2f2..c99d88a 100644 --- a/backend/src/main/java/com/yfd/platform/env/service/ISdEngInfoBHService.java +++ b/backend/src/main/java/com/yfd/platform/env/service/ISdEngInfoBHService.java @@ -3,6 +3,7 @@ package com.yfd.platform.env.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.yfd.platform.env.domain.SdEngInfoBH; +import com.yfd.platform.env.domain.SdEngInfoBHRequest; import java.util.List; import java.util.Set; @@ -37,7 +38,7 @@ public interface ISdEngInfoBHService extends IService { /** * 电站下拉列表(根据基地编码筛选 + 支持名称模糊搜索) */ - List selectForDropdown(String baseId, String ennm); + List selectForDropdown(SdEngInfoBHRequest sdEngInfoBHRequest); Set getUserAuthorizedStationCodes(); @@ -56,5 +57,5 @@ public interface ISdEngInfoBHService extends IService { */ boolean deleteEngInfo(String stcd); - List selectRegDropdown(String baseId, String ennm); + List selectRegDropdown(SdEngInfoBHRequest sdEngInfoBHRequest); } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/env/service/ISdFishDictoryBService.java b/backend/src/main/java/com/yfd/platform/env/service/ISdFishDictoryBService.java index 05e03b8..0080982 100644 --- a/backend/src/main/java/com/yfd/platform/env/service/ISdFishDictoryBService.java +++ b/backend/src/main/java/com/yfd/platform/env/service/ISdFishDictoryBService.java @@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.yfd.platform.env.domain.SdFishDictoryB; +import java.util.List; + public interface ISdFishDictoryBService extends IService { Page selectPage(String name, String code, Integer type, Integer rare, Page page); @@ -15,4 +17,6 @@ public interface ISdFishDictoryBService extends IService { boolean deleteById(String id); SdFishDictoryB getById(String id); + + List findSimilarFish(String name, Integer limit); } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/env/service/ISdHycdDicService.java b/backend/src/main/java/com/yfd/platform/env/service/ISdHycdDicService.java index ea2c24a..7bf15b8 100644 --- a/backend/src/main/java/com/yfd/platform/env/service/ISdHycdDicService.java +++ b/backend/src/main/java/com/yfd/platform/env/service/ISdHycdDicService.java @@ -46,7 +46,7 @@ public interface ISdHycdDicService extends IService { /** * 下拉框列表查询(支持名称和级别过滤) */ - List selectForDropdown(String hynm, Integer grd); + List selectForDropdown(String hynm, Integer grd,Integer lx,String phycd); - List regDropdown(String hynm, Integer grd); + List regDropdown(String hynm, Integer grd,Integer lx,String phycd); } diff --git a/backend/src/main/java/com/yfd/platform/env/service/impl/SdEngInfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/env/service/impl/SdEngInfoBHServiceImpl.java index 9219ece..60e4b2f 100644 --- a/backend/src/main/java/com/yfd/platform/env/service/impl/SdEngInfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/env/service/impl/SdEngInfoBHServiceImpl.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yfd.platform.data.domain.SysUserDataScope; import com.yfd.platform.data.mapper.SysUserDataScopeMapper; import com.yfd.platform.env.domain.SdEngInfoBH; +import com.yfd.platform.env.domain.SdEngInfoBHRequest; import com.yfd.platform.env.mapper.SdEngInfoBHMapper; import com.yfd.platform.env.service.ISdEngInfoBHService; import com.yfd.platform.utils.SecurityUtils; @@ -65,9 +66,15 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl selectForDropdown(String baseId, String ennm) { + public List selectForDropdown(SdEngInfoBHRequest sdEngInfoBHRequest) { + String baseId = sdEngInfoBHRequest.getBaseId(); + String ennm = sdEngInfoBHRequest.getEnnm(); + List rvcds = sdEngInfoBHRequest.getRvcds(); + List hbrvcds = sdEngInfoBHRequest.getHbrvcds(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StringUtils.hasText(baseId), SdEngInfoBH::getBaseId, baseId) + .in(rvcds != null && !rvcds.isEmpty(), SdEngInfoBH::getRvcd, rvcds) + .in(hbrvcds != null && !hbrvcds.isEmpty(), SdEngInfoBH::getHbrvcd, hbrvcds) .like(StringUtils.hasText(ennm), SdEngInfoBH::getEnnm, ennm) .select(SdEngInfoBH::getStcd, SdEngInfoBH::getEnnm, SdEngInfoBH::getBaseId) .orderByAsc(SdEngInfoBH::getOrderIndex); @@ -143,9 +150,15 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl selectRegDropdown(String baseId, String ennm) { + public List selectRegDropdown(SdEngInfoBHRequest sdEngInfoBHRequest) { + String baseId = sdEngInfoBHRequest.getBaseId(); + String ennm = sdEngInfoBHRequest.getEnnm(); + List rvcds = sdEngInfoBHRequest.getRvcds(); + List hbrvcds = sdEngInfoBHRequest.getHbrvcds(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(StringUtils.hasText(baseId), SdEngInfoBH::getBaseId, baseId) + .in(rvcds != null && !rvcds.isEmpty(), SdEngInfoBH::getRvcd, rvcds) + .in(hbrvcds != null && !hbrvcds.isEmpty(), SdEngInfoBH::getHbrvcd, hbrvcds) .like(StringUtils.hasText(ennm), SdEngInfoBH::getEnnm, ennm) .select(SdEngInfoBH::getStcd, SdEngInfoBH::getEnnm, SdEngInfoBH::getBaseId) .orderByAsc(SdEngInfoBH::getOrderIndex); diff --git a/backend/src/main/java/com/yfd/platform/env/service/impl/SdFishDictoryBServiceImpl.java b/backend/src/main/java/com/yfd/platform/env/service/impl/SdFishDictoryBServiceImpl.java index 4a118c0..1519f3f 100644 --- a/backend/src/main/java/com/yfd/platform/env/service/impl/SdFishDictoryBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/env/service/impl/SdFishDictoryBServiceImpl.java @@ -1,5 +1,6 @@ package com.yfd.platform.env.service.impl; +import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -9,6 +10,9 @@ import com.yfd.platform.env.service.ISdFishDictoryBService; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import java.util.*; +import java.util.stream.Collectors; + @Service public class SdFishDictoryBServiceImpl extends ServiceImpl implements ISdFishDictoryBService { @@ -62,4 +66,146 @@ public class SdFishDictoryBServiceImpl extends ServiceImpl findSimilarFish(String name, Integer limit) { + if (!StringUtils.hasText(name)) { + return Collections.emptyList(); + } + + int resultLimit = (limit != null && limit > 0) ? limit : 10; + String searchName = name.trim(); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(SdFishDictoryB::getIsDeleted, 0); + List allFish = list(wrapper); + + SdFishDictoryB exactMatch = allFish.stream() + .filter(f -> f.getName() != null && f.getName().equals(searchName)) + .findFirst() + .orElse(null); + + if (exactMatch != null) { + Map similarityMap = new HashMap<>(); + for (SdFishDictoryB fish : allFish) { + if (fish.getId().equals(exactMatch.getId())) { + continue; + } + int score = calculateSimilarity(exactMatch, fish); + if (score > 0) { + similarityMap.put(fish, score); + } + } + + List result = similarityMap.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(resultLimit) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + result.add(0, exactMatch); + return result; + } + + List containsMatches = allFish.stream() + .filter(f -> f.getName() != null && f.getName().contains(searchName)) + .collect(Collectors.toList()); + + if (!containsMatches.isEmpty()) { + List result = containsMatches.stream() + .sorted((f1, f2) -> { + int len1 = f1.getName().length(); + int len2 = f2.getName().length(); + if (len1 != len2) { + return Integer.compare(len1, len2); + } + return f1.getName().compareTo(f2.getName()); + }) + .limit(resultLimit) + .collect(Collectors.toList()); + return result; + } + + SdFishDictoryB partialMatch = allFish.stream() + .filter(f -> f.getName() != null && (searchName.contains(f.getName()) || f.getName().contains(searchName))) + .findFirst() + .orElse(null); + + if (partialMatch != null) { + Map similarityMap = new HashMap<>(); + for (SdFishDictoryB fish : allFish) { + if (fish.getId().equals(partialMatch.getId())) { + continue; + } + int score = calculateSimilarity(partialMatch, fish); + if (score > 0) { + similarityMap.put(fish, score); + } + } + + List result = similarityMap.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(resultLimit) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + result.add(0, partialMatch); + return result; + } + + return Collections.emptyList(); + } + + private int calculateSimilarity(SdFishDictoryB ref, SdFishDictoryB target) { + int score = 0; + + if (ref.getGenus() != null && ref.getGenus().equals(target.getGenus()) && !ref.getGenus().isEmpty()) { + score += 25; + } + + if (ref.getSpecies() != null && ref.getSpecies().equals(target.getSpecies()) && !ref.getSpecies().isEmpty()) { + score += 20; + } + + if (ref.getFamily() != null && ref.getFamily().equals(target.getFamily()) && !ref.getFamily().isEmpty()) { + score += 15; + } + + if (ref.getOrders() != null && ref.getOrders().equals(target.getOrders()) && !ref.getOrders().isEmpty()) { + score += 10; + } + + if (ref.getType() != null && ref.getType().equals(target.getType()) && !ObjectUtil.isEmpty(ref.getType()) ) { + score += 5; + } + + if (ref.getHabitat() != null && ref.getHabitat().equals(target.getHabitat()) && !ObjectUtil.isEmpty(ref.getHabitat())) { + score += 5; + } + + if (ref.getHabitMigrat() != null && ref.getHabitMigrat().equals(target.getHabitMigrat()) && !ref.getHabitMigrat().isEmpty()) { + score += 5; + } + + if (ref.getFeedingHabit() != null && ref.getFeedingHabit().equals(target.getFeedingHabit()) && !ref.getFeedingHabit().isEmpty()) { + score += 3; + } + + if (ref.getSpawnCharact() != null && ref.getSpawnCharact().equals(target.getSpawnCharact()) && !ref.getSpawnCharact().isEmpty()) { + score += 3; + } + + if (ref.getPtype() != null && ref.getPtype().equals(target.getPtype()) && !ObjectUtil.isEmpty(ref.getPtype())) { + score += 2; + } + + if (ref.getResourceType() != null && ref.getResourceType().equals(target.getResourceType()) && !ObjectUtil.isEmpty(ref.getResourceType())) { + score += 2; + } + + if (ref.getRare() != null && ref.getRare().equals(target.getRare()) && !ObjectUtil.isEmpty(ref.getRare())) { + score += 2; + } + + return score; + } } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/env/service/impl/SdHycdDicServiceImpl.java b/backend/src/main/java/com/yfd/platform/env/service/impl/SdHycdDicServiceImpl.java index 2c0e4f7..a87c2ef 100644 --- a/backend/src/main/java/com/yfd/platform/env/service/impl/SdHycdDicServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/env/service/impl/SdHycdDicServiceImpl.java @@ -58,19 +58,23 @@ public class SdHycdDicServiceImpl extends ServiceImpl selectForDropdown(String hynm, Integer grd) { + public List selectForDropdown(String hynm, Integer grd, Integer lx,String phycd) { return this.lambdaQuery() .like(hynm != null && !hynm.isEmpty(), SdHycdDic::getHynm, hynm) + .eq(phycd != null, SdHycdDic::getPhycd, phycd) .eq(grd != null, SdHycdDic::getGrd, grd) + .eq(lx != null, SdHycdDic::getLx, lx) .orderByAsc(SdHycdDic::getOrderIndex) .list(); } @Override - public List regDropdown(String hynm, Integer grd) { + public List regDropdown(String hynm, Integer grd,Integer lx,String phycd) { return this.lambdaQuery() .like(hynm != null && !hynm.isEmpty(), SdHycdDic::getHynm, hynm) + .eq(phycd != null, SdHycdDic::getPhycd, phycd) .eq(grd != null, SdHycdDic::getGrd, grd) + .eq(lx != null, SdHycdDic::getLx, lx) .orderByAsc(SdHycdDic::getOrderIndex) .list(); } diff --git a/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java b/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java index 98b329a..edb0c44 100644 --- a/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java +++ b/backend/src/main/java/com/yfd/platform/system/controller/SmsVerifyCodeController.java @@ -4,11 +4,18 @@ import cn.hutool.json.JSONUtil; import cn.hutool.jwt.JWTUtil; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.config.WebConfig; +import com.yfd.platform.data.domain.SysUserDataScope; +import com.yfd.platform.data.service.ISysUserDataScopeService; +import com.yfd.platform.env.domain.SdEngInfoBH; +import com.yfd.platform.env.service.ISdEngInfoBHService; +import com.yfd.platform.env.service.ISdHbrvDicService; import com.yfd.platform.system.domain.*; +import com.yfd.platform.system.mapper.SysMenuMapper; import com.yfd.platform.system.service.ISmsVerifyCodeService; import com.yfd.platform.system.service.ISysLogService; import com.yfd.platform.system.service.IUserService; import com.yfd.platform.utils.RequestHolder; +import com.yfd.platform.utils.RsaUtils; import com.yfd.platform.utils.StringUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -19,12 +26,12 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import java.sql.Timestamp; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; /** *

@@ -48,6 +55,18 @@ public class SmsVerifyCodeController { @Resource private ISysLogService sysLogService; + @Resource + private SysMenuMapper sysMenuMapper; + + @Resource + private ISysUserDataScopeService sysUserDataScopeService; + + @Resource + private ISdEngInfoBHService engInfoBHService; + + @Resource + private ISdHbrvDicService hbrvDicService; + @Value("${rsa.private_key}") private String privateKey; @@ -59,26 +78,42 @@ public class SmsVerifyCodeController { public ResponseResult sendVerifyCode(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String phone = smsVerifyCodeRequest.getPhone(); Integer type = smsVerifyCodeRequest.getType(); + if (phone == null || phone.isEmpty()) { return ResponseResult.error("手机号不能为空"); } - if (type == null || (!type.equals(SmsVerifyCode.TYPE_REGISTER) && !type.equals(SmsVerifyCode.TYPE_FIND_PASSWORD))) { - return ResponseResult.error("类型错误:1-注册 2-找回密码"); + if (type == null) { + return ResponseResult.error("类型不能为空:1-注册 2-找回密码 3-登录"); } + // 1. 注册验证码校验 if (type.equals(SmsVerifyCode.TYPE_REGISTER)) { SysUser existUser = userService.getUserByPhone(phone); if (existUser != null) { return ResponseResult.error("该手机号已注册"); } } - - if (type.equals(SmsVerifyCode.TYPE_FIND_PASSWORD)) { + // 2. 找回密码验证码校验 + else if (type.equals(SmsVerifyCode.TYPE_FIND_PASSWORD)) { SysUser existUser = userService.getUserByPhone(phone); if (existUser == null) { return ResponseResult.error("该手机号未注册"); } } + // 3. 登录验证码校验(新增) + else if (type.equals(SmsVerifyCode.TYPE_LOGIN)) { + SysUser existUser = userService.getUserByPhone(phone); + if (existUser == null) { + return ResponseResult.error("该手机号未注册,请先注册"); + } + // 可选:如果用户状态异常(如被禁用),也可以在此处拦截 + // if (existUser.getStatus() == 0) { + // return ResponseResult.error("账号已被禁用"); + // } + } + else { + return ResponseResult.error("类型错误:1-注册 2-找回密码 3-登录"); + } String code = smsVerifyCodeService.sendVerifyCode(phone, type); if (code == null) { @@ -88,51 +123,62 @@ public class SmsVerifyCodeController { return ResponseResult.success(); } + /** * 注册用户 */ @PostMapping("/register") @Operation(summary = "注册用户") + @Transactional public ResponseResult register(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { - SysUser user = smsVerifyCodeRequest.getUser(); String code = smsVerifyCodeRequest.getCode(); - if (user.getPhone() == null || user.getPhone().isEmpty()) { + if (smsVerifyCodeRequest.getPhone() == null || smsVerifyCodeRequest.getPhone().isEmpty()) { return ResponseResult.error("手机号不能为空"); } - if (user.getUsername() == null || user.getUsername().isEmpty()) { + if (smsVerifyCodeRequest.getUsername() == null || smsVerifyCodeRequest.getUsername().isEmpty()) { return ResponseResult.error("用户名不能为空"); } - if (user.getPassword() == null || user.getPassword().isEmpty()) { + if (smsVerifyCodeRequest.getPassword() == null || smsVerifyCodeRequest.getPassword().isEmpty()) { return ResponseResult.error("密码不能为空"); } if (code == null || code.isEmpty()) { return ResponseResult.error("验证码不能为空"); } - boolean verified = smsVerifyCodeService.verifyCode(user.getPhone(), code, SmsVerifyCode.TYPE_REGISTER); + boolean verified = smsVerifyCodeService.verifyCode(smsVerifyCodeRequest.getPhone(), code, SmsVerifyCode.TYPE_REGISTER); if (!verified) { return ResponseResult.error("验证码错误或已过期"); } - SysUser existUser = userService.getUserByPhone(user.getPhone()); + SysUser existUser = userService.getUserByPhone(smsVerifyCodeRequest.getPhone()); if (existUser != null) { return ResponseResult.error("该手机号已注册"); } - + SysUser user = new SysUser(); try { - com.yfd.platform.utils.RsaUtils.decryptByPrivateKey(privateKey, user.getPassword()); + String password = RsaUtils.decryptByPrivateKey(privateKey, smsVerifyCodeRequest.getPassword()); + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + user.setPassword(passwordEncoder.encode(password)); } catch (Exception e) { return ResponseResult.error("密码解密失败"); } - - BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - user.setPassword(passwordEncoder.encode(user.getPassword())); - user.setRegStatus(0); + user.setRegStatus("PENDING"); + user.setPhone(smsVerifyCodeRequest.getPhone()); + user.setBelongingUnit(smsVerifyCodeRequest.getBelongingUnit()); user.setRegTime(new Date()); + user.setNickname(smsVerifyCodeRequest.getRealName()); user.setStatus(1); + user.setOrgid("e90063ced25e3d469860e88d920c082f"); user.setUsertype(1); - + user.setUsername(smsVerifyCodeRequest.getUsername()); + user.setGroupCode(smsVerifyCodeRequest.getGroupCode()); + user.setCompanyCode(smsVerifyCodeRequest.getCompanyCode()); boolean success = userService.save(user); + // 给注册用户加上默认权限 + SysUser savedUser = userService.getUserByPhone(smsVerifyCodeRequest.getPhone()); + if (savedUser != null) { + this.addDefaultRole(savedUser.getId(), smsVerifyCodeRequest); + } if (success) { return ResponseResult.success(); } else { @@ -140,6 +186,103 @@ public class SmsVerifyCodeController { } } + private boolean addDefaultRole(String userId, SmsVerifyCodeRequest smsVerifyCodeRequest) { + if (userId == null || userId.isEmpty()) { + return false; + } + + String stationCode = smsVerifyCodeRequest.getStationCode(); + String hbrvcdCode = smsVerifyCodeRequest.getHbrvcdCode(); + + Set selectedStationCodes = new HashSet<>(); + if (StringUtils.isNotEmpty(stationCode)) { + selectedStationCodes.addAll(Arrays.asList(stationCode.split(","))); + } + + Set selectedBasinCodes = new HashSet<>(); + if (StringUtils.isNotEmpty(hbrvcdCode)) { + selectedBasinCodes.addAll(Arrays.asList(hbrvcdCode.split(","))); + } + + Set addedStationCodes = new HashSet<>(); + + for (String basinCode : selectedBasinCodes) { + if (StringUtils.isEmpty(basinCode)) { + continue; + } + + List allStationsInBasin = engInfoBHService.lambdaQuery() + .eq(SdEngInfoBH::getHbrvcd, basinCode) + .list(); + + if (allStationsInBasin == null || allStationsInBasin.isEmpty()) { + SysUserDataScope scope = new SysUserDataScope(); + scope.setUserId(userId); + scope.setOrgType("HBRVCD"); + scope.setOrgId(basinCode); + scope.setStatus(1); + scope.setPermissionType("READ"); + sysUserDataScopeService.addDataScope(scope); + continue; + } + + Set allStationCodesInBasin = allStationsInBasin.stream() + .map(SdEngInfoBH::getStcd) + .collect(Collectors.toSet()); + + boolean allStationsSelected = allStationCodesInBasin.containsAll(selectedStationCodes) + && selectedStationCodes.containsAll(allStationCodesInBasin); + + if (allStationsSelected) { + SysUserDataScope scope = new SysUserDataScope(); + scope.setUserId(userId); + scope.setOrgType("HBRVCD"); + scope.setOrgId(basinCode); + scope.setStatus(1); + scope.setPermissionType("READ"); + sysUserDataScopeService.addDataScope(scope); + addedStationCodes.add(basinCode); + } else { + Set stationsInBasinAndSelected = allStationCodesInBasin.stream() + .filter(selectedStationCodes::contains) + .collect(Collectors.toSet()); + + for (String stationCd : stationsInBasinAndSelected) { + SysUserDataScope scope = new SysUserDataScope(); + scope.setUserId(userId); + scope.setOrgType("STATION"); + scope.setOrgId(stationCd); + scope.setStatus(1); + scope.setPermissionType("READ"); + sysUserDataScopeService.addDataScope(scope); + addedStationCodes.add(stationCd); + } + } + } + + Set standaloneStations = selectedStationCodes.stream() + .filter(code -> !addedStationCodes.contains(code)) + .collect(Collectors.toSet()); + + for (String stationCd : standaloneStations) { + if (StringUtils.isEmpty(stationCd)) { + continue; + } + SysUserDataScope scope = new SysUserDataScope(); + scope.setUserId(userId); + scope.setOrgType("STATION"); + scope.setOrgId(stationCd); + scope.setStatus(1); + scope.setPermissionType("READ"); + sysUserDataScopeService.addDataScope(scope); + } + SysUser user = new SysUser(); + user.setId(userId); + userService.updateUserRoles( user,"c13481a486c9ee559cf305284df4d207"); + // 加上角色权限 + return true; + } + /** * 找回密码 */ @@ -168,7 +311,11 @@ public class SmsVerifyCodeController { if (existUser == null) { return ResponseResult.error("该手机号未注册"); } - + try { + password = RsaUtils.decryptByPrivateKey(privateKey, smsVerifyCodeRequest.getPassword()); + } catch (Exception e) { + return ResponseResult.error("密码解密失败"); + } BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String encryptedPassword = passwordEncoder.encode(password); @@ -183,7 +330,7 @@ public class SmsVerifyCodeController { /** * 验证验证码是否有效 */ - @GetMapping("/verifyCode") + @PostMapping("/verifyCode") @Operation(summary = "验证验证码") public ResponseResult verifyCode(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) { String phone = smsVerifyCodeRequest.getPhone(); @@ -212,7 +359,7 @@ public class SmsVerifyCodeController { return ResponseResult.error("验证码不能为空"); } - boolean verified = smsVerifyCodeService.verifyCode(phone, code, SmsVerifyCode.TYPE_REGISTER); + boolean verified = smsVerifyCodeService.verifyCode(phone, code, SmsVerifyCode.TYPE_LOGIN); if (!verified) { return ResponseResult.error("验证码错误或已过期"); } @@ -226,11 +373,11 @@ public class SmsVerifyCodeController { return ResponseResult.error("账号已停用"); } - if (user.getRegStatus() != null && user.getRegStatus() == 0) { + if (user.getRegStatus() != null && "PENDING".equals(user.getRegStatus())) { return ResponseResult.error("账号待审核,请联系管理员"); } - if (user.getRegStatus() != null && user.getRegStatus() == 2) { + if (user.getRegStatus() != null && "REJECTED".equals(user.getRegStatus())) { return ResponseResult.error("账号审核未通过"); } @@ -242,7 +389,10 @@ public class SmsVerifyCodeController { LoginUser loginUser = new LoginUser(); loginUser.setUser(user); loginUser.setUsername(user.getUsername()); - + //Todo 根据用户查询权限信息 添加到LoginUser中 + List permissions = + sysMenuMapper.selectPermsByUserId(user.getId()); + loginUser.setPermissions( permissions); HttpServletRequest request = RequestHolder.getHttpServletRequest(); SysLog sysLog = new SysLog(); sysLog.setUsercode(user.getUsername()); @@ -275,7 +425,7 @@ public class SmsVerifyCodeController { webConfig.loginuserCache().put("login:" + userId, jsonStr); webConfig.loginuserCache().put("expire_time:" + userId, map.get("expire_time").toString()); - map.put("user", user); +// map.put("user", user); return ResponseResult.successData(map); } diff --git a/backend/src/main/java/com/yfd/platform/system/controller/UserController.java b/backend/src/main/java/com/yfd/platform/system/controller/UserController.java index 6c9d287..cda7434 100644 --- a/backend/src/main/java/com/yfd/platform/system/controller/UserController.java +++ b/backend/src/main/java/com/yfd/platform/system/controller/UserController.java @@ -6,6 +6,8 @@ import com.yfd.platform.annotation.Log; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.datasource.DataSource; import com.yfd.platform.system.domain.SysUser; +import com.yfd.platform.system.domain.SysUserRequest; +import com.yfd.platform.system.service.ISmsVerifyCodeService; import com.yfd.platform.system.service.IUserService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -32,6 +34,9 @@ public class UserController { @Resource private IUserService userService; + @Resource + private ISmsVerifyCodeService smsVerifyCodeService; + @Log(module = "系统用户", value = "新增系统用户") @PostMapping("/addUser") @Operation(summary = "新增系统用户") @@ -58,6 +63,7 @@ public class UserController { return ResponseResult.successData(reslut); } + @GetMapping("/queryUsers") @Operation(summary = "查询用户信息") @ResponseBody @@ -155,12 +161,7 @@ public class UserController { if (StrUtil.isBlank(id)) { ResponseResult.error("参数为空"); } - boolean ok = userService.resetPassword(id); - if (ok) { - return ResponseResult.success(); - } else { - return ResponseResult.error(); - } + return userService.resetPassword(id); } /*********************************** @@ -201,20 +202,27 @@ public class UserController { return ResponseResult.success(); } - @Log(module = "系统用户", value = "审核用户注册") +// @Log(module = "系统用户", value = "审核用户注册") @PostMapping("/auditUser") @Operation(summary = "审核用户注册") @ResponseBody - public ResponseResult auditUser(@RequestParam String userId, - @RequestParam Integer auditStatus) { + public ResponseResult auditUser(@RequestBody SysUserRequest sysUserRequest) { + String userId = sysUserRequest.getUserId(); + String auditStatus = sysUserRequest.getRegStatus(); if (userId == null || userId.isEmpty()) { return ResponseResult.error("用户ID不能为空"); } - if (auditStatus == null || (auditStatus != 1 && auditStatus != 2)) { + + if (StrUtil.isBlank(auditStatus) || ( !"APPROVED".equals(auditStatus)&&!"REJECTED".equals(auditStatus))) { return ResponseResult.error("审核状态错误:1-通过 2-驳回"); } + SysUser user = userService.getById(userId); + if (user == null) { + return ResponseResult.error("用户不存在"); + } boolean ok = userService.auditUser(userId, auditStatus); if (ok) { + smsVerifyCodeService.sendAuditNotify(user.getPhone(), auditStatus, sysUserRequest.getCommentInfo()); return ResponseResult.success(); } else { return ResponseResult.error("审核失败"); @@ -224,8 +232,8 @@ public class UserController { @GetMapping("/queryPendingAuditUsers") @Operation(summary = "查询待审核用户列表") @ResponseBody - public ResponseResult queryPendingAuditUsers(Page page) { - Page result = userService.queryPendingAuditUsers(page); + public ResponseResult queryPendingAuditUsers(Page page,String name,String regStatus) { + Page result = userService.queryPendingAuditUsers(page,name,regStatus); return ResponseResult.successData(result); } } diff --git a/backend/src/main/java/com/yfd/platform/system/domain/LoginUser.java b/backend/src/main/java/com/yfd/platform/system/domain/LoginUser.java index 902c628..baf4258 100644 --- a/backend/src/main/java/com/yfd/platform/system/domain/LoginUser.java +++ b/backend/src/main/java/com/yfd/platform/system/domain/LoginUser.java @@ -22,11 +22,17 @@ public class LoginUser implements UserDetails { private String username; - private List permissions; + private List permissions=new ArrayList<>(); + /** + * 自定义构造函数(如果需要特殊逻辑) + */ public LoginUser(SysUser user, List permissions) { this.user = user; - this.permissions = permissions; + // 4. 增加非空判断,确保 permissions 永远不为 null + if (permissions != null) { + this.permissions = permissions; + } } @JSONField(serialize = false) diff --git a/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java b/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java index 2cc1351..988e35a 100644 --- a/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java +++ b/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCode.java @@ -23,6 +23,7 @@ public class SmsVerifyCode implements Serializable { public static final Integer TYPE_REGISTER = 1; public static final Integer TYPE_FIND_PASSWORD = 2; + public static final Integer TYPE_LOGIN = 3; public static final Integer STATUS_UNUSED = 0; public static final Integer STATUS_USED = 1; diff --git a/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCodeRequest.java b/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCodeRequest.java index b7f59eb..70c3cb9 100644 --- a/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCodeRequest.java +++ b/backend/src/main/java/com/yfd/platform/system/domain/SmsVerifyCodeRequest.java @@ -19,20 +19,35 @@ public class SmsVerifyCodeRequest { */ private String code; + /** + * 用户名 + */ + private String username; + + private String realName; + + private String belongingUnit; + /** * 密码 */ private String password; - /** - * 用户 - */ - private SysUser user; +// /** +// * 用户 +// */ +// private SysUser user; /** * 流域编号 */ private String rvcdCode; + + /** + * 流域编号 + */ + private String hbrvcdCode; + /** * 集团编号 */ diff --git a/backend/src/main/java/com/yfd/platform/system/domain/SysUser.java b/backend/src/main/java/com/yfd/platform/system/domain/SysUser.java index ed9b24f..1fae968 100644 --- a/backend/src/main/java/com/yfd/platform/system/domain/SysUser.java +++ b/backend/src/main/java/com/yfd/platform/system/domain/SysUser.java @@ -122,15 +122,20 @@ public class SysUser implements Serializable { private String realName; /** - * 注册状态:0-待审核 1-已通过 2-已驳回 + * 审批状态(PENDING待审批 / APPROVED已通过 / REJECTED已驳回) */ - private Integer regStatus; + private String regStatus; /** * 审核人ID */ private String auditUser; + /** + * 所属单位 + */ + private String belongingUnit; + /** * 审核时间 */ @@ -141,6 +146,15 @@ public class SysUser implements Serializable { */ private Date regTime; + /** + * 集团编号 + */ + private String groupCode; + /** + * 公司编号 + */ + private String companyCode; + @TableField(exist = false) List roles; } diff --git a/backend/src/main/java/com/yfd/platform/system/domain/SysUserRequest.java b/backend/src/main/java/com/yfd/platform/system/domain/SysUserRequest.java new file mode 100644 index 0000000..346d8cc --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/domain/SysUserRequest.java @@ -0,0 +1,34 @@ +package com.yfd.platform.system.domain; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SysUserRequest { + + /** + * 用户ID + */ + private String userId; + + /** + * 用户ID + */ + private List userIds; + + /** + * 审核状态 + */ + private String regStatus; + + /** + * 审批意见 + */ + private String commentInfo; +} diff --git a/backend/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java b/backend/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java index 7aae6a3..f031cf8 100644 --- a/backend/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java +++ b/backend/src/main/java/com/yfd/platform/system/mapper/SysUserMapper.java @@ -62,7 +62,7 @@ public interface SysUserMapper extends BaseMapper { * userid 用户id * 返回值说明: ************************************/ - String getMaxLevel(@Param("userid") String userid); + String getMaxLevel(@Param("userId") String userId); /*********************************** * 用途说明:根据用户id删除所分配的角色 diff --git a/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java b/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java index a8ca1b8..95b3765 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java +++ b/backend/src/main/java/com/yfd/platform/system/service/ISmsVerifyCodeService.java @@ -37,4 +37,13 @@ public interface ISmsVerifyCodeService extends IService { * 生成6位数字验证码 */ String generateCode(); + + /** + * 发送审核结果通知短信 + * @param phone 手机号 + * @param auditStatus 审核状态 1-通过 2-驳回 + * @param reason 驳回原因(可选) + * @return 是否发送成功 + */ + boolean sendAuditNotify(String phone, String auditStatus, String reason); } \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/system/service/IUserService.java b/backend/src/main/java/com/yfd/platform/system/service/IUserService.java index f86da52..00a2868 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/IUserService.java +++ b/backend/src/main/java/com/yfd/platform/system/service/IUserService.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.yfd.platform.config.ResponseResult; import com.yfd.platform.system.domain.LoginUser; import com.yfd.platform.system.domain.SysUser; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.io.File; @@ -57,6 +58,9 @@ public interface IUserService extends IService { List list(String total, String size, String orgid, String username, String mobile, String status); + @Transactional(rollbackFor = Exception.class) + Map updateUserRoles(SysUser sysUser, String roleids); + /*********************************** * 用途说明:根据ID查询用户详情 * 参数说明 @@ -97,7 +101,7 @@ public interface IUserService extends IService { *id 重置密码的 用户id * 返回值说明: 判断是否重置成功 ************************************/ - boolean resetPassword(String id) throws Exception; + ResponseResult resetPassword(String id) throws Exception; /*********************************** * 用途说明:设置账号状态(管理员) @@ -162,7 +166,7 @@ public interface IUserService extends IService { * auditStatus 审核状态:1-通过 2-驳回 * 返回值说明: 是否审核成功 ************************************/ - boolean auditUser(String userId, Integer auditStatus); + boolean auditUser(String userId, String auditStatus); /*********************************** * 用途说明:查询待审核用户列表 @@ -170,6 +174,6 @@ public interface IUserService extends IService { *page 分页参数 * 返回值说明: 待审核用户分页列表 ************************************/ - Page queryPendingAuditUsers(Page page); + Page queryPendingAuditUsers(Page page,String name, String regStatus); } diff --git a/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java b/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java index 8e0c91f..6f25515 100644 --- a/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/system/service/impl/SmsVerifyCodeServiceImpl.java @@ -21,7 +21,7 @@ import java.util.Random; @Service public class SmsVerifyCodeServiceImpl extends ServiceImpl implements ISmsVerifyCodeService { - private static final int CODE_VALID_MINUTES = 1; + private static final int CODE_VALID_MINUTES = 5; private static final Random RANDOM = new Random(); @Resource @@ -96,4 +96,28 @@ public class SmsVerifyCodeServiceImpl extends ServiceImpl impleme return result; } + @Transactional(rollbackFor = Exception.class) + @Override + public Map updateUserRoles(SysUser sysUser, String roleids) { + Map result = new HashMap<>(); + + try { + + Timestamp currentTime = new Timestamp(System.currentTimeMillis()); + sysUser.setLastmodifydate(currentTime); + + // 更新用户信息 + boolean ok = this.updateById(sysUser); + if (!ok) { + result.put("status", "error"); + result.put("msg", "用户信息修改失败!"); + return result; + } + + // 处理角色分配 + String userId = sysUser.getId(); + if (StrUtil.isNotEmpty(roleids)) { + handleUserRoles(userId, roleids); + } else { + // 清空所有角色 + sysUserMapper.delRoleUsersByUserid(userId); + } + + result.put("status", "sucess"); + result.put("msg", "用户信息修改成功!"); + + } catch (Exception e) { + log.error("更新用户信息失败", e); + result.put("status", "error"); + result.put("msg", "操作失败:" + e.getMessage()); + throw e; // 抛出异常,触发事务回滚 + } + + return result; + } + /** * 处理用户角色分配(增量更新) * @param userId 用户 ID @@ -281,7 +319,6 @@ public class UserServiceImpl extends ServiceImpl impleme // 解析新角色列表 String[] newRoles = roleIds.split(","); - Set newRoleSet = new HashSet<>(Arrays.asList(newRoles)); // 需要新增的角色(新角色 - 当前角色) for (String roleId : newRoles) { @@ -295,9 +332,6 @@ public class UserServiceImpl extends ServiceImpl impleme sysUserMapper.delInRoleUsersByUserid(userId, newRoles); } -// ... existing code ... - - @Override public Map getOneById(String id) { QueryWrapper queryWrapper = new QueryWrapper<>(); @@ -392,17 +426,18 @@ public class UserServiceImpl extends ServiceImpl impleme * 返回值说明: 判断是重置成功 ************************************/ @Override - public boolean resetPassword(String id) throws Exception { - boolean isOk = false; + public ResponseResult resetPassword(String id) throws Exception { //根据当前用户id 查询角色表的级别 currentUser.getUser() 获取当前用户id String level = sysUserMapper.getMaxLevel(id); //判断是否获取级别 - if (StrUtil.isNotEmpty(level)) { + if ("admin".equals(SecurityUtils.getCurrentUsername())||StrUtil.isNotEmpty(level)) { //判断当前用户级别 管理员及以上权限 - if (Integer.parseInt(level) <= 2) { + if ("admin".equals(SecurityUtils.getCurrentUsername())||Integer.parseInt(level) <= 2) { + SysUser sysUser = sysUserMapper.selectById(id); UpdateWrapper updateWrapper = new UpdateWrapper<>(); + String password = PasswordGenerator.generateRandomPassword(sysUser.getUsername()); //根据id 修改密码,密码修改时间,最近修改者,最近修改日期 将密码修改为 123456 - String cryptPassword = passwordEncoder.encode("123456"); + String cryptPassword = passwordEncoder.encode(password); updateWrapper.eq("id", id).set("password", cryptPassword).set( "pwdresettime", new Timestamp(System.currentTimeMillis())).set( @@ -410,10 +445,11 @@ public class UserServiceImpl extends ServiceImpl impleme new Timestamp(System.currentTimeMillis())).set( "lastmodifier", getUsername()); //是否修改成功 - isOk = this.update(updateWrapper); + this.update(updateWrapper); + return ResponseResult.successData(password); } } - return isOk; + return ResponseResult.error(); } /*********************************** @@ -429,7 +465,7 @@ public class UserServiceImpl extends ServiceImpl impleme //根据当前用户id 查询角色表的级别 currentUser.getUser() 获取当前用户id String level = sysUserMapper.getMaxLevel(id); //判断当前用户级别 管理员及以上权限 - if (Integer.parseInt(level) <= 2) { + if ("admin".equals(SecurityUtils.getCurrentUsername())||Integer.parseInt(level) <= 2) { UpdateWrapper updateWrapper = new UpdateWrapper<>(); //根据id修改用户状态,最近修改人,最近修改时间 updateWrapper.eq("id", id).set("status", status).set( @@ -515,7 +551,7 @@ public class UserServiceImpl extends ServiceImpl impleme Page page) { Page mapPage = sysUserMapper.queryUsers(orgid, username, page); - ;mapPage.getRecords().forEach(record -> { + mapPage.getRecords().forEach(record -> { String id = record.getId(); List sysRoles = sysRoleMapper.getRoleByUserId(id); record.setRoles(sysRoles); @@ -574,34 +610,45 @@ public class UserServiceImpl extends ServiceImpl impleme updateWrapper.eq("phone", phone) .set("password", encryptedPassword) .set("pwdresettime", new Timestamp(System.currentTimeMillis())) - .set("lastmodifydate", new Timestamp(System.currentTimeMillis())) - .set("lastmodifier", getUsername()); + .set("lastmodifydate", new Timestamp(System.currentTimeMillis())); return this.update(updateWrapper); } @Override - public boolean auditUser(String userId, Integer auditStatus) { - UpdateWrapper updateWrapper = new UpdateWrapper<>(); - updateWrapper.eq("id", userId) - .set("reg_status", auditStatus) - .set("audit_user", getUsername()) - .set("audit_time", new Timestamp(System.currentTimeMillis())) - .set("lastmodifydate", new Timestamp(System.currentTimeMillis())) - .set("lastmodifier", getUsername()); - if (auditStatus == 1) { - updateWrapper.set("status", 1); - } else if (auditStatus == 2) { - updateWrapper.set("status", 0); + public boolean auditUser(String userId, String auditStatus) { + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(SysUser::getId, userId) + .set(SysUser::getRegStatus, auditStatus) + .set(SysUser::getAuditUser, SecurityUtils.getUserId()) + .set(SysUser::getAuditTime, new Timestamp(System.currentTimeMillis())) + .set(SysUser::getLastmodifydate, new Timestamp(System.currentTimeMillis())) + .set(SysUser::getLastmodifier, SecurityUtils.getCurrentUsername()); + if ("APPROVED".equals(auditStatus)) { + updateWrapper.set(SysUser::getStatus, 1); + } else if ("REJECTED".equals(auditStatus)) { + updateWrapper.set(SysUser::getStatus, 0); } return this.update(updateWrapper); } @Override - public Page queryPendingAuditUsers(Page page) { + public Page queryPendingAuditUsers(Page page,String name,String regStatus) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(SysUser::getRegStatus, 0); + queryWrapper.in(SysUser::getRegStatus, "PENDING","APPROVED","REJECTED"); + queryWrapper.eq(ObjectUtil.isNotEmpty(regStatus),SysUser::getRegStatus, regStatus); + queryWrapper.and(StrUtil.isNotBlank(name), wrapper -> + wrapper.like(SysUser::getNickname, name) + .or() + .like(SysUser::getRealName, name) + ); queryWrapper.orderByDesc(SysUser::getRegTime); - return this.page(page, queryWrapper); + Page mapPage = this.page(page, queryWrapper); + mapPage.getRecords().forEach(record -> { + String id = record.getId(); + List sysRoles = sysRoleMapper.getRoleByUserId(id); + record.setRoles(sysRoles); + }); + return mapPage; } /*********************************** diff --git a/backend/src/main/java/com/yfd/platform/utils/PasswordGenerator.java b/backend/src/main/java/com/yfd/platform/utils/PasswordGenerator.java new file mode 100644 index 0000000..43dd4c9 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/utils/PasswordGenerator.java @@ -0,0 +1,212 @@ +package com.yfd.platform.utils; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +import java.security.SecureRandom; + +/** + * 随机密码生成器,满足以下规则: + * 1. 必须包含大写字母、小写字母、数字、特殊字符中的至少三类 + * 2. 不能包含2位及以上相同字符的连续重复(如11、aa) + * 3. 不能包含2位及以上连续递增或递减的数字(如12、21) + * 4. 不能包含2位及以上连续递增或递减的字母(如ab、ba,忽略大小写) + * 5. 不能包含用户名(忽略大小写) + * 6. 密码的字母部分不能是用户名的子串(忽略大小写,连续子串) + * 7. 密码不能与用户名存在明显关联(字母部分不能等于用户名或其反转) + * 8. 密码开头和结尾不能是特殊字符 + * 9. 密码长度可配置(默认10-12位) + */ +public class PasswordGenerator { + + private static final String LOWERS = "abcdefghijklmnopqrstuvwxyz"; + private static final String UPPERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String DIGITS = "0123456789"; + private static final String SPECIALS = "!@#$%^&*()_+-=[]{}|;:,.<>?"; + + private static final SecureRandom RANDOM = new SecureRandom(); + private static final int DEFAULT_MIN_LEN = 10; + private static final int DEFAULT_MAX_LEN = 12; + private static final int MAX_ATTEMPTS = 100; + + private static int minLength = DEFAULT_MIN_LEN; + private static int maxLength = DEFAULT_MAX_LEN; + + public static void configure(int minLen, int maxLen) { + if (minLen < 6) { + minLen = 6; + } + if (maxLen > 32) { + maxLen = 32; + } + if (minLen > maxLen) { + minLen = maxLen; + } + minLength = minLen; + maxLength = maxLen; + } + + public static String generateRandomPassword(String username) { + return generateRandomPassword(username, minLength, maxLength); + } + + public static String generateRandomPassword(String username, int minLen, int maxLen) { + int configuredMin = minLen > 0 ? minLen : DEFAULT_MIN_LEN; + int configuredMax = maxLen > 0 ? maxLen : DEFAULT_MAX_LEN; + if (configuredMin > configuredMax) { + configuredMin = configuredMax; + } + for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { + int length = configuredMin + RANDOM.nextInt(configuredMax - configuredMin + 1); + String candidate = generateRandomString(length); + if (isValidPassword(candidate, username)) { + return candidate; + } + } + throw new RuntimeException("Unable to generate a valid password after " + MAX_ATTEMPTS + " attempts"); + } + + private static String generateRandomString(int length) { + String lowerChars = LOWERS; + String upperChars = UPPERS; + String digitChars = DIGITS; + String specialChars = SPECIALS; + + StringBuilder sb = new StringBuilder(length); + + int firstCharIdx = RANDOM.nextInt(lowerChars.length() + upperChars.length() + digitChars.length()); + String firstPool = lowerChars + upperChars + digitChars; + sb.append(firstPool.charAt(firstCharIdx)); + + for (int i = 1; i < length - 1; i++) { + int choice = RANDOM.nextInt(100); + String charsToUse; + if (choice < 25) { + charsToUse = specialChars; + } else if (choice < 55) { + charsToUse = lowerChars; + } else if (choice < 80) { + charsToUse = upperChars; + } else { + charsToUse = digitChars; + } + int idx = RANDOM.nextInt(charsToUse.length()); + sb.append(charsToUse.charAt(idx)); + } + + int lastCharIdx = RANDOM.nextInt(lowerChars.length() + upperChars.length() + digitChars.length()); + String lastPool = lowerChars + upperChars + digitChars; + sb.append(lastPool.charAt(lastCharIdx)); + + return sb.toString(); + } + + private static boolean isValidPassword(String password, String username) { + if (password == null || password.length() < minLength) return false; + + if (!hasEnoughCategories(password)) return false; + if (hasConsecutiveRepeats(password)) return false; + if (hasSequentialDigits(password)) return false; + if (hasSequentialLetters(password)) return false; + if (!isNormalChar(password.charAt(0))) return false; + if (!isNormalChar(password.charAt(password.length() - 1))) return false; + + if (username != null && !username.isEmpty()) { + if (containsIgnoreCase(password, username)) return false; + String lettersOnly = extractLetters(password); + if (containsIgnoreCase(lettersOnly, username)) return false; + if (isObviousRelation(lettersOnly, username)) return false; + } + return true; + } + + private static boolean isNormalChar(char c) { + return Character.isLowerCase(c) || Character.isUpperCase(c) || Character.isDigit(c); + } + + private static boolean hasEnoughCategories(String s) { + boolean hasLower = false, hasUpper = false, hasDigit = false, hasSpecial = false; + for (char c : s.toCharArray()) { + if (Character.isLowerCase(c)) hasLower = true; + else if (Character.isUpperCase(c)) hasUpper = true; + else if (Character.isDigit(c)) hasDigit = true; + else hasSpecial = true; + } + int cnt = (hasLower ? 1 : 0) + (hasUpper ? 1 : 0) + (hasDigit ? 1 : 0) + (hasSpecial ? 1 : 0); + return cnt >= 3; + } + + private static boolean hasConsecutiveRepeats(String s) { + for (int i = 0; i < s.length() - 1; i++) { + if (s.charAt(i) == s.charAt(i + 1)) return true; + } + return false; + } + + private static boolean hasSequentialDigits(String s) { + for (int i = 0; i < s.length() - 1; i++) { + char c1 = s.charAt(i); + char c2 = s.charAt(i + 1); + if (Character.isDigit(c1) && Character.isDigit(c2)) { + int diff = c2 - c1; + if (Math.abs(diff) == 1) return true; + } + } + return false; + } + + private static boolean hasSequentialLetters(String s) { + for (int i = 0; i < s.length() - 1; i++) { + char c1 = s.charAt(i); + char c2 = s.charAt(i + 1); + if (Character.isLetter(c1) && Character.isLetter(c2)) { + char low1 = Character.toLowerCase(c1); + char low2 = Character.toLowerCase(c2); + int diff = low2 - low1; + if (Math.abs(diff) == 1) return true; + } + } + return false; + } + + private static boolean containsIgnoreCase(String str, String search) { + if (str == null || search == null) return false; + return str.toLowerCase().contains(search.toLowerCase()); + } + + private static String extractLetters(String s) { + StringBuilder sb = new StringBuilder(); + for (char c : s.toCharArray()) { + if (Character.isLetter(c)) sb.append(c); + } + return sb.toString(); + } + + private static boolean isObviousRelation(String lettersOnly, String username) { + String lowerLetters = lettersOnly.toLowerCase(); + String lowerUser = username.toLowerCase(); + if (lowerLetters.equals(lowerUser)) return true; + String reversedUser = new StringBuilder(lowerUser).reverse().toString(); + return lowerLetters.equals(reversedUser); + } + + public static void main(String[] args) { + configure(10, 12); + String username = "button"; + String resetPwd = generateRandomPassword(username); + + PasswordEncoder passwordEncoder=new BCryptPasswordEncoder(); + String cryptPassword = passwordEncoder.encode(resetPwd); + System.out.println("Generated password: " + resetPwd); + System.out.println("Generated cryptPassword: " + cryptPassword); + System.out.println("Password length: " + resetPwd.length()); + System.out.println("First char: '" + resetPwd.charAt(0) + "' (is special: " + !isNormalChar(resetPwd.charAt(0)) + ")"); + System.out.println("Last char: '" + resetPwd.charAt(resetPwd.length() - 1) + "' (is special: " + !isNormalChar(resetPwd.charAt(resetPwd.length() - 1)) + ")"); + + configure(8, 16); + System.out.println("\nAfter configure(8, 16):"); + resetPwd = generateRandomPassword(username); + System.out.println("Generated password: " + resetPwd); + System.out.println("Password length: " + resetPwd.length()); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/utils/SecurityUtils.java b/backend/src/main/java/com/yfd/platform/utils/SecurityUtils.java index 4e8f518..8d667c1 100644 --- a/backend/src/main/java/com/yfd/platform/utils/SecurityUtils.java +++ b/backend/src/main/java/com/yfd/platform/utils/SecurityUtils.java @@ -22,10 +22,12 @@ import com.yfd.platform.exception.BadRequestException; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; +import java.util.Collection; import java.util.List; /** @@ -91,4 +93,16 @@ public class SecurityUtils { return JSONUtil.toList(array,Long.class); } + // 在 SecurityUtils.java 中 + public static boolean hasPermission(String permission) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null || !authentication.isAuthenticated()) { + return false; + } + + Collection authorities = authentication.getAuthorities(); + return authorities != null && authorities.stream() + .anyMatch(auth -> permission.equals(auth.getAuthority())); + } + } diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml index 8d16f53..1df2467 100644 --- a/backend/src/main/resources/application-dev.yml +++ b/backend/src/main/resources/application-dev.yml @@ -55,7 +55,7 @@ login: app: # ZIP导入临时目录配置 zip-import: - temp-dir: ${ZIP_IMPORT_TEMP_DIR:D:\zip_import_temp} + temp-dir: ${ZIP_IMPORT_TEMP_DIR:D:\qgc-platform\zip_import_temp} init: enabled: false schema: classpath:db-init/sql/min-schema.sql @@ -82,8 +82,8 @@ ip: file-space: #项目文档空间 - files: D:\demoproject\files\ #单独上传的文件附件 - system: D:\demoproject\system\ #单独上传的文件 + files: D:\qgc-platform\files\ #单独上传的文件附件 + system: D:\qgc-platform\system\ #单独上传的文件 task: pool: diff --git a/backend/src/main/resources/application-devtw.yml b/backend/src/main/resources/application-devtw.yml index 34a784a..33320bc 100644 --- a/backend/src/main/resources/application-devtw.yml +++ b/backend/src/main/resources/application-devtw.yml @@ -10,12 +10,12 @@ spring: druid: master: driverClassName: oracle.jdbc.OracleDriver - url: "${DB_MASTER_URL:jdbc:oracle:thin:@172.16.21.134:1521/SDLYZ}" + url: "${DB_MASTER_URL:jdbc:oracle:thin:@172.16.31.190:1521/SDLYZ}" username: "${DB_MASTER_USERNAME:QGC_REFA}" password: "${DB_MASTER_PASSWORD:Y4M4K1oCkL8U}" slave: driverClassName: oracle.jdbc.OracleDriver - url: "${DB_SLAVE_URL:jdbc:oracle:thin:@172.16.21.134:1521/SDLYZ}" + url: "${DB_SLAVE_URL:jdbc:oracle:thin:@172.16.31.190:1521/SDLYZ}" username: "${DB_SLAVE_USERNAME:QGC_REFA}" password: "${DB_SLAVE_PASSWORD:Y4M4K1oCkL8U}" @@ -31,12 +31,12 @@ spring: max-request-size: 100MB logging: - file: - name: logs/projectname.log - level: - com.genersoft.iot: debug - com.genersoft.iot.vmp.storager.dao: info - com.genersoft.iot.vmp.gb28181: info + file: + name: logs/platform-dev.log + level: + root: info + com.yfd.platform: info + com.yfd.platform.*.mapper: trace # 在线文档: swagger-ui(生产环境建议关闭) swagger-ui: @@ -77,7 +77,7 @@ login: app: # ZIP导入临时目录配置 zip-import: - temp-dir: ${ZIP_IMPORT_TEMP_DIR:D:\zip_import_temp} + temp-dir: ${ZIP_IMPORT_TEMP_DIR:D:\qgc-platform\zip_import_temp} init: enabled: false schema: classpath:db-init/sql/min-schema.sql diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index 8021243..27be67d 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -10,12 +10,12 @@ spring: druid: master: driverClassName: oracle.jdbc.OracleDriver - url: "${DB_MASTER_URL:jdbc:oracle:thin:@172.16.21.134:1521/SDLYZ}" + url: "${DB_MASTER_URL:jdbc:oracle:thin:@172.16.31.190:1521/SDLYZ}" username: "${DB_MASTER_USERNAME:QGC_REFA}" password: "${DB_MASTER_PASSWORD:Y4M4K1oCkL8U}" slave: driverClassName: oracle.jdbc.OracleDriver - url: "${DB_SLAVE_URL:jdbc:oracle:thin:@172.16.21.134:1521/SDLYZ}" + url: "${DB_SLAVE_URL:jdbc:oracle:thin:@172.16.31.190:1521/SDLYZ}" username: "${DB_SLAVE_USERNAME:QGC_REFA}" password: "${DB_SLAVE_PASSWORD:Y4M4K1oCkL8U}" @@ -31,12 +31,12 @@ spring: max-request-size: 100MB logging: - file: - name: logs/projectname.log - level: - com.genersoft.iot: debug - com.genersoft.iot.vmp.storager.dao: info - com.genersoft.iot.vmp.gb28181: info + file: + name: logs/platform-dev.log + level: + root: info + com.yfd.platform: info + com.yfd.platform.*.mapper: trace # 在线文档: swagger-ui(生产环境建议关闭) swagger-ui: @@ -77,7 +77,7 @@ login: app: # ZIP导入临时目录配置 zip-import: - temp-dir: ${ZIP_IMPORT_TEMP_DIR:/tmp/zip_import_temp} + temp-dir: ${ZIP_IMPORT_TEMP_DIR:/qgc-platform/tmp/zip_import_temp} init: enabled: false schema: classpath:db-init/sql/min-schema.sql @@ -104,8 +104,8 @@ ip: file-space: #项目文档空间 - files: D:\demoproject\files\ #单独上传的文件附件 - system: D:\demoproject\system\ #单独上传的文件 + files: /qgc-platform/files/ #单独上传的文件附件 + system: /qgc-platform/system/ #单独上传的文件 task: pool: diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index cfbf0b8..b11a8a0 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -39,3 +39,6 @@ springdoc: enabled: true path: /swagger-ui.html packages-to-scan: com.yfd.platform + +attachment: + token: ${ATTACHMENT_TOKEN:qgcBkod25ngBa4wu8BtfCPYsJ7lQGVDoexH} \ No newline at end of file diff --git a/backend/src/main/resources/mapper/system/SysUserMapper.xml b/backend/src/main/resources/mapper/system/SysUserMapper.xml index dfc56f0..1ee549f 100644 --- a/backend/src/main/resources/mapper/system/SysUserMapper.xml +++ b/backend/src/main/resources/mapper/system/SysUserMapper.xml @@ -49,9 +49,12 @@