Merge branch 'dev-tw'
This commit is contained in:
commit
f2767bb5b5
@ -11,6 +11,7 @@ import com.yfd.platform.constant.Constant;
|
|||||||
import com.yfd.platform.system.domain.LoginUser;
|
import com.yfd.platform.system.domain.LoginUser;
|
||||||
import com.yfd.platform.system.domain.Message;
|
import com.yfd.platform.system.domain.Message;
|
||||||
import com.yfd.platform.system.service.IMessageService;
|
import com.yfd.platform.system.service.IMessageService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
@ -25,6 +26,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@Slf4j
|
||||||
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -36,6 +38,9 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
|||||||
FilterChain filterChain) throws ServletException, IOException {
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
//获取token
|
//获取token
|
||||||
String uri = httpServletRequest.getRequestURI();
|
String uri = httpServletRequest.getRequestURI();
|
||||||
|
if(uri.contains("/data/fishDraft/importZip")){
|
||||||
|
log.info("请求地址:{}", uri);
|
||||||
|
}
|
||||||
String token = httpServletRequest.getHeader("token");
|
String token = httpServletRequest.getHeader("token");
|
||||||
if (StrUtil.isEmpty(token) || "/user/login".equals(uri)) {
|
if (StrUtil.isEmpty(token) || "/user/login".equals(uri)) {
|
||||||
filterChain.doFilter(httpServletRequest, httpServletResponse);
|
filterChain.doFilter(httpServletRequest, httpServletResponse);
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
package com.yfd.platform.config;
|
|
||||||
|
|
||||||
import jakarta.servlet.Filter;
|
|
||||||
import jakarta.servlet.FilterChain;
|
|
||||||
import jakarta.servlet.RequestDispatcher;
|
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.ServletRequest;
|
|
||||||
import jakarta.servlet.ServletResponse;
|
|
||||||
import jakarta.servlet.annotation.WebFilter;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将以 /prod-api/ 开头的请求转发到去掉前缀的真实后端接口路径。
|
|
||||||
* 例如:/prod-api/user/code -> /user/code
|
|
||||||
* 这样可以兼容前端生产环境仍使用 /prod-api 作为网关前缀的情况。
|
|
||||||
*/
|
|
||||||
@WebFilter(urlPatterns = "/prod-api/*", filterName = "prodApiPrefixFilter")
|
|
||||||
public class ProdApiPrefixFilter implements Filter {
|
|
||||||
|
|
||||||
private static final String PREFIX = "/prod-api";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
|
|
||||||
if (!(req instanceof HttpServletRequest) || !(res instanceof HttpServletResponse)) {
|
|
||||||
chain.doFilter(req, res);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpServletRequest request = (HttpServletRequest) req;
|
|
||||||
String uri = request.getRequestURI();
|
|
||||||
|
|
||||||
// 仅拦截 /prod-api/* 的接口请求并进行内部 forward
|
|
||||||
if (uri.startsWith(PREFIX + "/")) {
|
|
||||||
String forwardUri = uri.substring(PREFIX.length());
|
|
||||||
RequestDispatcher dispatcher = request.getRequestDispatcher(forwardUri);
|
|
||||||
dispatcher.forward(req, res);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
chain.doFilter(req, res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,5 @@
|
|||||||
package com.yfd.platform.config;
|
package com.yfd.platform.config;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springdoc.core.models.GroupedOpenApi;
|
import org.springdoc.core.models.GroupedOpenApi;
|
||||||
@ -6,9 +7,6 @@ import io.swagger.v3.oas.models.OpenAPI;
|
|||||||
import io.swagger.v3.oas.models.info.Info;
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
import io.swagger.v3.oas.models.info.Contact;
|
import io.swagger.v3.oas.models.info.Contact;
|
||||||
|
|
||||||
/**
|
|
||||||
* Springdoc OpenAPI 配置
|
|
||||||
*/
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class SwaggerConfig {
|
public class SwaggerConfig {
|
||||||
|
|
||||||
@ -23,37 +21,51 @@ public class SwaggerConfig {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
// @Bean
|
||||||
public GroupedOpenApi groupWebsiteApi() {
|
// public GroupedOpenApi groupPlatformApi() {
|
||||||
return GroupedOpenApi.builder()
|
// return GroupedOpenApi.builder()
|
||||||
.group("1. 平台模块")
|
// .group("1. 平台模块")
|
||||||
.packagesToScan("com.yfd.platform.modules.platformdb.controller")
|
// .packagesToScan("com.yfd.platform.modules.platformdb.controller")
|
||||||
.build();
|
// .build();
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Bean
|
// @Bean
|
||||||
public GroupedOpenApi groupQuartzApi() {
|
// public GroupedOpenApi groupQuartzApi() {
|
||||||
return GroupedOpenApi.builder()
|
// return GroupedOpenApi.builder()
|
||||||
.group("2. 定时任务")
|
// .group("2. 定时任务")
|
||||||
.packagesToScan("com.yfd.platform.modules.quartz.controller")
|
// .packagesToScan("com.yfd.platform.modules.quartz.controller")
|
||||||
.build();
|
// .build();
|
||||||
}
|
// }
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public GroupedOpenApi groupSystemApi() {
|
public GroupedOpenApi groupSystemApi() {
|
||||||
return GroupedOpenApi.builder()
|
return GroupedOpenApi.builder()
|
||||||
.group("3. 系统管理")
|
.group("1. 系统管理")
|
||||||
.packagesToScan("com.yfd.platform.system.controller")
|
.packagesToScan("com.yfd.platform.system.controller")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public GroupedOpenApi groupDataEnvApi() {
|
||||||
|
return GroupedOpenApi.builder()
|
||||||
|
.group("2. 过鱼数据模块")
|
||||||
|
.packagesToScan("com.yfd.platform.data.controller")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public GroupedOpenApi groupEnvApi() {
|
public GroupedOpenApi groupEnvApi() {
|
||||||
return GroupedOpenApi.builder()
|
return GroupedOpenApi.builder()
|
||||||
.group("4. 环境/基地/流域管理")
|
.group("3. 全过程-生态环保数据服务")
|
||||||
.packagesToScan("com.yfd.platform.env.controller")
|
.packagesToScan("com.yfd.platform.env.controller")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public GroupedOpenApi groupEngApi() {
|
||||||
|
return GroupedOpenApi.builder()
|
||||||
|
.group("4. 全过程-常规水电数据服务")
|
||||||
|
.packagesToScan("com.yfd.platform.eng.controller")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -13,6 +13,7 @@ import jakarta.annotation.Resource;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -116,4 +117,11 @@ public class ApprovalMainController {
|
|||||||
boolean result = approvalMainService.removeById(id);
|
boolean result = approvalMainService.removeById(id);
|
||||||
return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败");
|
return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/batchDelete")
|
||||||
|
@Operation(summary = "删除审批")
|
||||||
|
public ResponseResult delete(@RequestBody List<String> ids) {
|
||||||
|
boolean result = approvalMainService.removeBatchByIds(ids);
|
||||||
|
return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -3,7 +3,9 @@ package com.yfd.platform.data.controller;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@ -11,8 +13,12 @@ import java.util.Base64;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
@ -20,12 +26,15 @@ import com.yfd.platform.common.DataSourceRequest;
|
|||||||
import com.yfd.platform.config.ResponseResult;
|
import com.yfd.platform.config.ResponseResult;
|
||||||
import com.yfd.platform.data.domain.*;
|
import com.yfd.platform.data.domain.*;
|
||||||
import com.yfd.platform.data.domain.vo.FishDraftDataVO;
|
import com.yfd.platform.data.domain.vo.FishDraftDataVO;
|
||||||
|
import com.yfd.platform.data.domain.vo.FishStatisticsVO;
|
||||||
import com.yfd.platform.data.service.AttachmentUploadService;
|
import com.yfd.platform.data.service.AttachmentUploadService;
|
||||||
import com.yfd.platform.data.service.IFishDraftDataService;
|
import com.yfd.platform.data.service.IFishDraftDataService;
|
||||||
import com.yfd.platform.data.service.IFishImportService;
|
import com.yfd.platform.data.service.IFishImportService;
|
||||||
|
import com.yfd.platform.data.service.IFishStatisticsService;
|
||||||
import com.yfd.platform.data.service.IImportTaskService;
|
import com.yfd.platform.data.service.IImportTaskService;
|
||||||
import com.yfd.platform.data.utils.ZipFileUtil;
|
import com.yfd.platform.data.utils.ZipFileUtil;
|
||||||
import com.yfd.platform.utils.KendoUtil;
|
import com.yfd.platform.utils.KendoUtil;
|
||||||
|
import com.yfd.platform.utils.MultipartStreamParser;
|
||||||
import com.yfd.platform.utils.SecurityUtils;
|
import com.yfd.platform.utils.SecurityUtils;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@ -33,12 +42,12 @@ import jakarta.annotation.Resource;
|
|||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -69,6 +78,18 @@ public class FishDraftDataController {
|
|||||||
@Resource
|
@Resource
|
||||||
private AttachmentUploadService attachmentUploadService;
|
private AttachmentUploadService attachmentUploadService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IFishStatisticsService fishStatisticsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ThreadPoolTaskExecutor taskExecutor;
|
||||||
|
|
||||||
|
private final ExecutorService attachmentDeleteExecutor = Executors.newFixedThreadPool(4, r -> {
|
||||||
|
Thread t = new Thread(r, "attachment-delete");
|
||||||
|
t.setDaemon(true);
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
|
||||||
@PostMapping("/page")
|
@PostMapping("/page")
|
||||||
@Operation(summary = "分页查询过鱼数据(关联电站和设施)")
|
@Operation(summary = "分页查询过鱼数据(关联电站和设施)")
|
||||||
public ResponseResult queryPageList(@RequestBody DataSourceRequest dataSourceRequest) {
|
public ResponseResult queryPageList(@RequestBody DataSourceRequest dataSourceRequest) {
|
||||||
@ -83,6 +104,13 @@ public class FishDraftDataController {
|
|||||||
return ResponseResult.successData(list);
|
return ResponseResult.successData(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/statistics")
|
||||||
|
@Operation(summary = "过鱼到数统计(按用户月度汇总,支持流域/电站多选过滤)")
|
||||||
|
public ResponseResult statistics(@RequestBody DataSourceRequest dataSourceRequest) {
|
||||||
|
Page<FishStatisticsVO> result = fishStatisticsService.queryPage(dataSourceRequest);
|
||||||
|
return ResponseResult.successData(result);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/getById")
|
@GetMapping("/getById")
|
||||||
@Operation(summary = "根据ID查询")
|
@Operation(summary = "根据ID查询")
|
||||||
public ResponseResult getById(@RequestParam String id) {
|
public ResponseResult getById(@RequestParam String id) {
|
||||||
@ -133,23 +161,10 @@ public class FishDraftDataController {
|
|||||||
|
|
||||||
@PostMapping("/batchSaveDraft")
|
@PostMapping("/batchSaveDraft")
|
||||||
@Operation(summary = "批量保存草稿")
|
@Operation(summary = "批量保存草稿")
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public ResponseResult saveDraft(@RequestBody FishImportRowRequest request) {
|
public ResponseResult saveDraft(@RequestBody FishImportRowRequest request) {
|
||||||
String taskId = request.getTaskId();
|
String taskId = request.getTaskId();
|
||||||
ImportTask importTask = importTaskService.getById(taskId);
|
ImportTask importTask = importTaskService.getById(taskId);
|
||||||
String resultJson = importTask.getResultJson();
|
FishImportResult importResult = importTaskService.buildImportResult(taskId);
|
||||||
FishImportResult importResult = null;
|
|
||||||
Map<String, String> imageFiles = null;
|
|
||||||
Map<String, String> videoFiles = null;
|
|
||||||
if (resultJson != null && !resultJson.isEmpty()) {
|
|
||||||
try {
|
|
||||||
importResult = objectMapper.readValue(resultJson, FishImportResult.class);
|
|
||||||
imageFiles = importResult.getImageFiles();
|
|
||||||
videoFiles = importResult.getVideoFiles();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (importResult == null || importResult.getRows() == null) {
|
if (importResult == null || importResult.getRows() == null) {
|
||||||
return ResponseResult.error("导入数据不存在");
|
return ResponseResult.error("导入数据不存在");
|
||||||
@ -164,10 +179,12 @@ public class FishDraftDataController {
|
|||||||
data.setDeletedFlag(0);
|
data.setDeletedFlag(0);
|
||||||
data.setLockFlag(0);
|
data.setLockFlag(0);
|
||||||
data.setTm(date);
|
data.setTm(date);
|
||||||
|
data.setVdpthList(row.getVdpthList());
|
||||||
|
data.setPicpthList(row.getPicpthList());
|
||||||
fishDraftDataList.add(data);
|
fishDraftDataList.add(data);
|
||||||
}
|
}
|
||||||
boolean result = fishDraftDataService.saveBatch(fishDraftDataList);
|
boolean result = fishDraftDataService.batchSaveDraft(fishDraftDataList);
|
||||||
fishImportService.processAttachmentsAsync(fishDraftDataList, imageFiles, videoFiles,importTask.getTempDir());
|
fishImportService.processAttachmentsAsync(fishDraftDataList, importTask.getTempDir());
|
||||||
importTaskService.markSuccess(taskId);
|
importTaskService.markSuccess(taskId);
|
||||||
return result ? ResponseResult.success("保存成功") : ResponseResult.error("保存失败");
|
return result ? ResponseResult.success("保存成功") : ResponseResult.error("保存失败");
|
||||||
}
|
}
|
||||||
@ -180,11 +197,10 @@ public class FishDraftDataController {
|
|||||||
if (importTask == null) {
|
if (importTask == null) {
|
||||||
return ResponseResult.error("导入任务不存在");
|
return ResponseResult.error("导入任务不存在");
|
||||||
}
|
}
|
||||||
String resultJson = importTask.getResultJson();
|
FishImportResult importResult = importTaskService.buildImportResult(taskId);
|
||||||
List<FishDraftData> fishDraftDataList = batchSaveDraftRequest.getFishDraftDataList();
|
List<FishDraftData> fishDraftDataList = batchSaveDraftRequest.getFishDraftDataList();
|
||||||
if (resultJson != null && !resultJson.isEmpty()) {
|
if (importResult != null) {
|
||||||
try {
|
try {
|
||||||
FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class);
|
|
||||||
ZipFileUtil.ZipContent content = new ZipFileUtil.ZipContent();
|
ZipFileUtil.ZipContent content = new ZipFileUtil.ZipContent();
|
||||||
content.images = importResult.getImageFiles();
|
content.images = importResult.getImageFiles();
|
||||||
content.videos = importResult.getVideoFiles();
|
content.videos = importResult.getVideoFiles();
|
||||||
@ -203,7 +219,6 @@ public class FishDraftDataController {
|
|||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
// ignore parse error
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,10 +241,90 @@ public class FishDraftDataController {
|
|||||||
return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败");
|
return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// @PostMapping("/approvalIdRemoveDraft")
|
||||||
|
// @Operation(summary = "根据批次号批量删除草稿(软删除)")
|
||||||
|
// public ResponseResult approvalIdRemoveDraft(@RequestBody BatchApproveRequest request) {
|
||||||
|
// List<FishDraftData> draft = fishDraftDataService.list(new LambdaQueryWrapper<FishDraftData>().eq(FishDraftData::getDeletedFlag, 0).in(FishDraftData::getApprovalId, request.getApprovalIds()).eq(FishDraftData::getStatus, "REJECTED").select(FishDraftData::getId));
|
||||||
|
// List<String> ids = draft.stream().map(FishDraftData::getId).toList();
|
||||||
|
// boolean result = fishDraftDataService.batchRemoveDraft(ids);
|
||||||
|
// if(result){
|
||||||
|
// List<FishDraftData> list = fishDraftDataService.list(new LambdaQueryWrapper<FishDraftData>().in(FishDraftData::getId, ids).select(FishDraftData::getPicpth, FishDraftData::getVdpth, FishDraftData::getId));
|
||||||
|
// // 异步删除附件
|
||||||
|
// CompletableFuture.runAsync(() -> {
|
||||||
|
// for (FishDraftData fishDraftData : list) {
|
||||||
|
// String picpth = fishDraftData.getPicpth();
|
||||||
|
// String vdpth = fishDraftData.getVdpth();
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// if (StrUtil.isNotBlank(picpth)) {
|
||||||
|
// // 假设 picpth 是分号或逗号分隔的文件ID/路径
|
||||||
|
// List<String> split = StrUtil.split(picpth, StrUtil.C_COMMA);
|
||||||
|
// for (String fileId : split) {
|
||||||
|
// if (StrUtil.isNotBlank(fileId)) {
|
||||||
|
// attachmentUploadService.deleteFile(fileId.trim());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (StrUtil.isNotBlank(vdpth)) {
|
||||||
|
// List<String> split = StrUtil.split(vdpth, StrUtil.C_COMMA);
|
||||||
|
// for (String fileId : split) {
|
||||||
|
// if (StrUtil.isNotBlank(fileId)) {
|
||||||
|
// attachmentUploadService.deleteFile(fileId.trim());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// log.error("异步删除附件失败, dataId: {}", fishDraftData.getId(), e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }, attachmentDeleteExecutor).exceptionally(ex -> {
|
||||||
|
// log.error("异步删除任务执行异常", ex);
|
||||||
|
// return null;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败");
|
||||||
|
// }
|
||||||
@PostMapping("/batchRemoveDraft")
|
@PostMapping("/batchRemoveDraft")
|
||||||
@Operation(summary = "批量删除草稿(软删除)")
|
@Operation(summary = "批量删除草稿(软删除)")
|
||||||
public ResponseResult batchRemoveDraft(@RequestBody List<String> ids) {
|
public ResponseResult batchRemoveDraft(@RequestBody List<String> ids) {
|
||||||
boolean result = fishDraftDataService.batchRemoveDraft(ids);
|
boolean result = fishDraftDataService.batchRemoveDraft(ids);
|
||||||
|
if(result){
|
||||||
|
List<FishDraftData> list = fishDraftDataService.list(new LambdaQueryWrapper<FishDraftData>().in(FishDraftData::getId, ids).select(FishDraftData::getPicpth, FishDraftData::getVdpth, FishDraftData::getId));
|
||||||
|
// 异步删除附件
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
for (FishDraftData fishDraftData : list) {
|
||||||
|
String picpth = fishDraftData.getPicpth();
|
||||||
|
String vdpth = fishDraftData.getVdpth();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (StrUtil.isNotBlank(picpth)) {
|
||||||
|
// 假设 picpth 是分号或逗号分隔的文件ID/路径
|
||||||
|
List<String> split = StrUtil.split(picpth, StrUtil.C_COMMA);
|
||||||
|
for (String fileId : split) {
|
||||||
|
if (StrUtil.isNotBlank(fileId)) {
|
||||||
|
attachmentUploadService.deleteFile(fileId.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(vdpth)) {
|
||||||
|
List<String> split = StrUtil.split(vdpth, StrUtil.C_COMMA);
|
||||||
|
for (String fileId : split) {
|
||||||
|
if (StrUtil.isNotBlank(fileId)) {
|
||||||
|
attachmentUploadService.deleteFile(fileId.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("异步删除附件失败, dataId: {}", fishDraftData.getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, attachmentDeleteExecutor).exceptionally(ex -> {
|
||||||
|
log.error("异步删除任务执行异常", ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败");
|
return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,16 +448,11 @@ public class FishDraftDataController {
|
|||||||
|
|
||||||
@PostMapping("/importZip")
|
@PostMapping("/importZip")
|
||||||
@Operation(summary = "导入ZIP过鱼数据(每个用户同时只能进行一次导入)")
|
@Operation(summary = "导入ZIP过鱼数据(每个用户同时只能进行一次导入)")
|
||||||
public ResponseResult importZip(@RequestParam("file") MultipartFile file) {
|
public ResponseResult importZip(HttpServletRequest request) {
|
||||||
log.info("开始导入ZIP文件");
|
log.info("开始导入ZIP文件");
|
||||||
if (file == null || file.isEmpty()) {
|
|
||||||
return ResponseResult.error("请上传文件");
|
|
||||||
}
|
|
||||||
String fileName = file.getOriginalFilename();
|
|
||||||
if (fileName == null || (!fileName.endsWith(".zip"))) {
|
|
||||||
return ResponseResult.error("请上传ZIP文件(.zip)");
|
|
||||||
}
|
|
||||||
String uploadUserId = SecurityUtils.getUserId();
|
String uploadUserId = SecurityUtils.getUserId();
|
||||||
|
|
||||||
if (importTaskService.hasImportingTask(uploadUserId)) {
|
if (importTaskService.hasImportingTask(uploadUserId)) {
|
||||||
return ResponseResult.error("您有正在进行的导入任务,请等待完成后重试");
|
return ResponseResult.error("您有正在进行的导入任务,请等待完成后重试");
|
||||||
}
|
}
|
||||||
@ -370,52 +460,72 @@ public class FishDraftDataController {
|
|||||||
String importNo = "IMP" + System.currentTimeMillis();
|
String importNo = "IMP" + System.currentTimeMillis();
|
||||||
String taskId = UUID.randomUUID().toString();
|
String taskId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
ImportTask task = null;
|
||||||
|
Path tempDirPath = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String baseTempDir = ZipFileUtil.getDefaultTempDir();
|
String baseTempDir = ZipFileUtil.getDefaultTempDir();
|
||||||
String taskDirName = "zip_" + UUID.randomUUID().toString().substring(0, 8);
|
String taskDirName = "zip_" + UUID.randomUUID().toString().substring(0, 8);
|
||||||
Path tempDirPath = Paths.get(baseTempDir, taskDirName);
|
tempDirPath = Paths.get(baseTempDir, taskDirName);
|
||||||
Files.createDirectories(tempDirPath);
|
|
||||||
File savedZipFile = new File(tempDirPath.toFile(), "upload.zip");
|
|
||||||
file.transferTo(savedZipFile);
|
|
||||||
log.info("ZIP文件已保存到: {}", savedZipFile.getAbsolutePath());
|
|
||||||
|
|
||||||
ImportTask task = new ImportTask();
|
task = new ImportTask();
|
||||||
task.setId(taskId);
|
task.setId(taskId);
|
||||||
task.setImportNo(importNo);
|
task.setImportNo(importNo);
|
||||||
task.setBizType("FISH");
|
task.setBizType("FISH");
|
||||||
task.setFileName(fileName);
|
task.setStatus("UPLOADING");
|
||||||
task.setFileSize(file.getSize());
|
|
||||||
task.setStatus("UPLOADED");
|
|
||||||
task.setUploadUserId(uploadUserId);
|
task.setUploadUserId(uploadUserId);
|
||||||
task.setUploadTime(new Date());
|
task.setUploadTime(new Date());
|
||||||
task.setTempDir(tempDirPath.toString());
|
task.setTempDir(tempDirPath.toString());
|
||||||
importTaskService.save(task);
|
importTaskService.save(task);
|
||||||
log.info("导入任务已创建: {}", taskId);
|
log.info("导入任务已预创建: {}, 状态: UPLOADING", taskId);
|
||||||
|
|
||||||
|
MultipartStreamParser.StreamedFile streamedFile =
|
||||||
|
MultipartStreamParser.streamToFile(request, tempDirPath);
|
||||||
|
|
||||||
|
String fileName = streamedFile.getOriginalFileName();
|
||||||
|
if (fileName == null || !fileName.toLowerCase().endsWith(".zip")) {
|
||||||
|
importTaskService.markFailed(taskId, "文件格式错误: 请上传ZIP文件");
|
||||||
|
return ResponseResult.error("请上传ZIP文件(.zip)");
|
||||||
|
}
|
||||||
|
|
||||||
|
File savedZipFile = streamedFile.getTargetFile();
|
||||||
|
long fileSize = streamedFile.getFileSize();
|
||||||
|
log.info("ZIP文件已保存到: {} ({} bytes)", savedZipFile.getAbsolutePath(), fileSize);
|
||||||
|
|
||||||
|
task.setFileName(fileName);
|
||||||
|
task.setFileSize(fileSize);
|
||||||
|
task.setStatus("UPLOADED");
|
||||||
|
importTaskService.updateById(task);
|
||||||
|
log.info("导入任务已更新: {}, 状态: UPLOADED, 文件名: {}", taskId, fileName);
|
||||||
|
|
||||||
SecurityContext securityContext = SecurityContextHolder.getContext();
|
SecurityContext securityContext = SecurityContextHolder.getContext();
|
||||||
|
Path finalTempDirPath = tempDirPath;
|
||||||
CompletableFuture.runAsync(() -> {
|
CompletableFuture.runAsync(() -> {
|
||||||
try {
|
try {
|
||||||
SecurityContextHolder.setContext(securityContext);
|
SecurityContextHolder.setContext(securityContext);
|
||||||
log.info("异步开始解析ZIP文件, taskId: {}", taskId);
|
log.info("异步开始解析ZIP文件, taskId: {}", taskId);
|
||||||
|
|
||||||
FishImportResult result = fishImportService.parseAndMapZipFromFile(
|
FishImportResult result = fishImportService.parseAndMapZipFromFile(
|
||||||
savedZipFile, tempDirPath.toString(), uploadUserId);
|
savedZipFile, finalTempDirPath.toString(), uploadUserId);
|
||||||
result.setTaskId(taskId);
|
result.setTaskId(taskId);
|
||||||
String status = "VALIDATED";
|
|
||||||
if ("1".equals(result.getCode())) {
|
String status = "1".equals(result.getCode()) ? "FAILED" : "VALIDATED";
|
||||||
status = "FAILED";
|
|
||||||
}
|
|
||||||
importTaskService.updateStatus(taskId, status, result.getTempDir(), null);
|
importTaskService.updateStatus(taskId, status, result.getTempDir(), null);
|
||||||
importTaskService.updateProgress(taskId, result.getTotalCount(),
|
importTaskService.updateProgress(taskId, result.getTotalCount(),
|
||||||
result.getSuccessCount(), result.getFailedCount());
|
result.getSuccessCount(), result.getFailedCount());
|
||||||
String resultJson = objectMapper.writeValueAsString(result);
|
importTaskService.saveResultJson(taskId, result);
|
||||||
importTaskService.saveResultJson(taskId, resultJson);
|
|
||||||
log.info("异步解析完成, taskId: {}, 状态: {}", taskId, status);
|
log.info("异步解析完成, taskId: {}, 状态: {}, 成功: {}, 失败: {}",
|
||||||
|
taskId, status, result.getSuccessCount(), result.getFailedCount());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("异步解析ZIP失败, taskId: {}", taskId, e);
|
log.error("异步解析ZIP失败, taskId: {}", taskId, e);
|
||||||
importTaskService.markFailed(taskId, "导入失败: " + e.getMessage());
|
importTaskService.markFailed(taskId, "导入失败: " + e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
SecurityContextHolder.clearContext();
|
SecurityContextHolder.clearContext();
|
||||||
if (savedZipFile.exists()) {
|
if (savedZipFile.exists()) {
|
||||||
savedZipFile.delete();
|
boolean deleted = savedZipFile.delete();
|
||||||
|
log.debug("临时ZIP文件删除: {}, 结果: {}", savedZipFile.getAbsolutePath(), deleted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -425,9 +535,23 @@ public class FishDraftDataController {
|
|||||||
response.put("importNo", importNo);
|
response.put("importNo", importNo);
|
||||||
response.put("status", "UPLOADED");
|
response.put("status", "UPLOADED");
|
||||||
return ResponseResult.successData(response);
|
return ResponseResult.successData(response);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("创建导入任务失败", e);
|
log.error("导入ZIP文件失败, taskId: {}", taskId, e);
|
||||||
importTaskService.markFailed(taskId, "导入失败: " + e.getMessage());
|
|
||||||
|
if (task != null) {
|
||||||
|
importTaskService.markFailed(taskId, "导入失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempDirPath != null) {
|
||||||
|
try {
|
||||||
|
cn.hutool.core.io.FileUtil.del(tempDirPath.toFile());
|
||||||
|
log.debug("清理临时目录: {}", tempDirPath);
|
||||||
|
} catch (Exception cleanupEx) {
|
||||||
|
log.warn("清理临时目录失败: {}", tempDirPath, cleanupEx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ResponseResult.error("导入失败: " + e.getMessage());
|
return ResponseResult.error("导入失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -445,13 +569,11 @@ public class FishDraftDataController {
|
|||||||
return ResponseResult.error("任务不存在");
|
return ResponseResult.error("任务不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
String resultJson = task.getResultJson();
|
FishImportResult importResult = importTaskService.buildImportResult(taskId);
|
||||||
if (resultJson == null || resultJson.isEmpty()) {
|
if (importResult == null) {
|
||||||
return ResponseResult.error("任务结果为空");
|
return ResponseResult.error("任务结果为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class);
|
|
||||||
|
|
||||||
Map<String, Object> previewData = new HashMap<>();
|
Map<String, Object> previewData = new HashMap<>();
|
||||||
previewData.put("tempDir", importResult.getTempDir());
|
previewData.put("tempDir", importResult.getTempDir());
|
||||||
previewData.put("excelFileName", importResult.getExcelFileName());
|
previewData.put("excelFileName", importResult.getExcelFileName());
|
||||||
@ -495,20 +617,14 @@ public class FishDraftDataController {
|
|||||||
@GetMapping("/previewFile")
|
@GetMapping("/previewFile")
|
||||||
@Operation(summary = "预览临时文件内容")
|
@Operation(summary = "预览临时文件内容")
|
||||||
public void previewFile(@RequestParam String taskId, @RequestParam String filename, @RequestParam String type, HttpServletRequest request, HttpServletResponse response) {
|
public void previewFile(@RequestParam String taskId, @RequestParam String filename, @RequestParam String type, HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
String decodedFilename = URLDecoder.decode(filename, StandardCharsets.UTF_8);
|
||||||
|
log.debug("原始文件名: {}, 解码后: {}", filename, decodedFilename);
|
||||||
|
|
||||||
ImportTask importTask = importTaskService.getById(taskId);
|
ImportTask importTask = importTaskService.getById(taskId);
|
||||||
String resultJson = importTask.getResultJson();
|
|
||||||
String filePath = null;
|
String filePath = null;
|
||||||
String dir = "1".equals(type) ? "images" : "videos";
|
String dir = "1".equals(type) ? "images" : "videos";
|
||||||
if (resultJson != null && !resultJson.isEmpty()) {
|
if (importTask != null && importTask.getTempDir() != null) {
|
||||||
try {
|
filePath = importTask.getTempDir() + File.separator + dir + File.separator + decodedFilename;
|
||||||
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) {
|
if (filePath == null) {
|
||||||
writeErrorResponse(response, "文件路径不能为空");
|
writeErrorResponse(response, "文件路径不能为空");
|
||||||
@ -568,13 +684,12 @@ public class FishDraftDataController {
|
|||||||
return ResponseResult.error("任务不存在");
|
return ResponseResult.error("任务不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
String resultJson = importTask.getResultJson();
|
FishImportResult importResult = importTaskService.buildImportResult(taskId);
|
||||||
if (resultJson == null || resultJson.isEmpty()) {
|
if (importResult == null) {
|
||||||
return ResponseResult.error("任务结果为空");
|
return ResponseResult.error("任务结果为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class);
|
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
|
|
||||||
for (FishImportResult.FishImportRow row : importResult.getRows()) {
|
for (FishImportResult.FishImportRow row : importResult.getRows()) {
|
||||||
@ -632,8 +747,7 @@ public class FishDraftDataController {
|
|||||||
return ResponseResult.error("未找到对应的数据行");
|
return ResponseResult.error("未找到对应的数据行");
|
||||||
}
|
}
|
||||||
|
|
||||||
String updatedJson = objectMapper.writeValueAsString(importResult);
|
importTaskService.saveResultJson(taskId, importResult);
|
||||||
importTaskService.saveResultJson(taskId, updatedJson);
|
|
||||||
|
|
||||||
return ResponseResult.success("删除成功");
|
return ResponseResult.success("删除成功");
|
||||||
|
|
||||||
@ -723,7 +837,6 @@ public class FishDraftDataController {
|
|||||||
result.put("currentTask", null);
|
result.put("currentTask", null);
|
||||||
return ResponseResult.successData(result);
|
return ResponseResult.successData(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
String statusText = getStatusText(currentTask.getStatus());
|
String statusText = getStatusText(currentTask.getStatus());
|
||||||
boolean canImport = isTaskComplete(currentTask.getStatus());
|
boolean canImport = isTaskComplete(currentTask.getStatus());
|
||||||
|
|
||||||
@ -752,7 +865,8 @@ public class FishDraftDataController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isTaskComplete(String status) {
|
private boolean isTaskComplete(String status) {
|
||||||
return "CONFIRMED".equals(status) || "FAILED".equals(status) || "CANCELLED".equals(status);
|
return "CONFIRMED".equals(status) || "CANCELLED".equals(status);
|
||||||
|
// return "CONFIRMED".equals(status) || "FAILED".equals(status) || "CANCELLED".equals(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/getLastImportResult")
|
@GetMapping("/getLastImportResult")
|
||||||
@ -768,15 +882,7 @@ public class FishDraftDataController {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
FishImportResult importResult = null;
|
FishImportResult importResult = importTaskService.buildImportResult(lastTask.getId());
|
||||||
if (lastTask.getResultJson() != null && !lastTask.getResultJson().isEmpty()) {
|
|
||||||
try {
|
|
||||||
importResult = objectMapper.readValue(lastTask.getResultJson(), FishImportResult.class);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
// ignore parse error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int totalCount = lastTask.getTotalCount() != null ? lastTask.getTotalCount() : 0;
|
int totalCount = lastTask.getTotalCount() != null ? lastTask.getTotalCount() : 0;
|
||||||
int successCount = lastTask.getSuccessCount() != null ? lastTask.getSuccessCount() : 0;
|
int successCount = lastTask.getSuccessCount() != null ? lastTask.getSuccessCount() : 0;
|
||||||
@ -871,13 +977,11 @@ public class FishDraftDataController {
|
|||||||
return ResponseResult.error("任务不存在");
|
return ResponseResult.error("任务不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
String resultJson = task.getResultJson();
|
FishImportResult importResult = importTaskService.buildImportResult(taskId);
|
||||||
if (resultJson == null || resultJson.isEmpty()) {
|
if (importResult == null) {
|
||||||
return ResponseResult.error("任务结果为空");
|
return ResponseResult.error("任务结果为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class);
|
|
||||||
|
|
||||||
FishImportResult.FishImportRow targetRow = null;
|
FishImportResult.FishImportRow targetRow = null;
|
||||||
int targetIndex = -1;
|
int targetIndex = -1;
|
||||||
for (int i = 0; i < importResult.getRows().size(); i++) {
|
for (int i = 0; i < importResult.getRows().size(); i++) {
|
||||||
@ -925,8 +1029,8 @@ public class FishDraftDataController {
|
|||||||
importResult.setSuccessCount(successCount);
|
importResult.setSuccessCount(successCount);
|
||||||
importResult.setFailedCount(failedCount);
|
importResult.setFailedCount(failedCount);
|
||||||
|
|
||||||
String updatedJson = objectMapper.writeValueAsString(importResult);
|
// String updatedJson = objectMapper.writeValueAsString(importResult);
|
||||||
importTaskService.saveResultJson(taskId, updatedJson);
|
importTaskService.saveResultJson(taskId, importResult);
|
||||||
|
|
||||||
Map<String, Object> map = new HashMap<>();
|
Map<String, Object> map = new HashMap<>();
|
||||||
map.put("success", true);
|
map.put("success", true);
|
||||||
@ -961,13 +1065,11 @@ public class FishDraftDataController {
|
|||||||
return ResponseResult.error("任务不存在");
|
return ResponseResult.error("任务不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
String resultJson = task.getResultJson();
|
FishImportResult importResult = importTaskService.buildImportResult(taskId);
|
||||||
if (resultJson == null || resultJson.isEmpty()) {
|
if (importResult == null) {
|
||||||
return ResponseResult.error("任务结果为空");
|
return ResponseResult.error("任务结果为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class);
|
|
||||||
|
|
||||||
FishImportResult.FishImportRow targetRow = null;
|
FishImportResult.FishImportRow targetRow = null;
|
||||||
int targetIndex = -1;
|
int targetIndex = -1;
|
||||||
for (int i = 0; i < importResult.getRows().size(); i++) {
|
for (int i = 0; i < importResult.getRows().size(); i++) {
|
||||||
@ -983,8 +1085,8 @@ public class FishDraftDataController {
|
|||||||
return ResponseResult.error("未找到对应的数据行");
|
return ResponseResult.error("未找到对应的数据行");
|
||||||
}
|
}
|
||||||
importResult.getRows().remove(targetIndex);
|
importResult.getRows().remove(targetIndex);
|
||||||
String updatedJson = objectMapper.writeValueAsString(importResult);
|
// String updatedJson = objectMapper.writeValueAsString(importResult);
|
||||||
importTaskService.saveResultJson(taskId, updatedJson);
|
importTaskService.saveResultJson(taskId, importResult);
|
||||||
return ResponseResult.success();
|
return ResponseResult.success();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -1122,13 +1224,11 @@ public class FishDraftDataController {
|
|||||||
return ResponseResult.error("任务不存在");
|
return ResponseResult.error("任务不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
String resultJson = task.getResultJson();
|
FishImportResult importResult = importTaskService.buildImportResult(taskId);
|
||||||
if (resultJson == null || resultJson.isEmpty()) {
|
if (importResult == null) {
|
||||||
return ResponseResult.error("任务结果为空");
|
return ResponseResult.error("任务结果为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
FishImportResult importResult = objectMapper.readValue(resultJson, FishImportResult.class);
|
|
||||||
|
|
||||||
FishImportResult.FishImportRow matchedRow = findMatchingRow(importResult, data.getId());
|
FishImportResult.FishImportRow matchedRow = findMatchingRow(importResult, data.getId());
|
||||||
|
|
||||||
FishImportResult.FishImportRow resultRow = new FishImportResult.FishImportRow();
|
FishImportResult.FishImportRow resultRow = new FishImportResult.FishImportRow();
|
||||||
@ -1214,4 +1314,17 @@ public class FishDraftDataController {
|
|||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@jakarta.annotation.PreDestroy
|
||||||
|
public void shutdown() {
|
||||||
|
attachmentDeleteExecutor.shutdown();
|
||||||
|
try {
|
||||||
|
if (!attachmentDeleteExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
|
||||||
|
attachmentDeleteExecutor.shutdownNow();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
attachmentDeleteExecutor.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -8,6 +8,8 @@ import lombok.EqualsAndHashCode;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -65,11 +67,23 @@ public class FishDraftData implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private Date strdt;
|
private Date strdt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始日期
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String strdtStr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 结束日期
|
* 结束日期
|
||||||
*/
|
*/
|
||||||
private Date enddt;
|
private Date enddt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束日期
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String enddtStr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 游向(上行/下行/上行折返/下行折返)
|
* 游向(上行/下行/上行折返/下行折返)
|
||||||
*/
|
*/
|
||||||
@ -254,4 +268,10 @@ public class FishDraftData implements Serializable {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String ftpName;
|
private String ftpName;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private List<Map<String, String>> vdpthList;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private List<Map<String, String>> picpthList;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -21,8 +21,6 @@ public class FishImportResult {
|
|||||||
private List<String> unrecognizedFields;
|
private List<String> unrecognizedFields;
|
||||||
private Map<String, String> imageFiles;
|
private Map<String, String> imageFiles;
|
||||||
private Map<String, String> videoFiles;
|
private Map<String, String> videoFiles;
|
||||||
// public Map<String, String> images;
|
|
||||||
// public Map<String, String> videos;
|
|
||||||
private String tempDir;
|
private String tempDir;
|
||||||
private String excelFileName;
|
private String excelFileName;
|
||||||
private String excelFilePath;
|
private String excelFilePath;
|
||||||
|
|||||||
@ -96,9 +96,34 @@ public class ImportTask implements Serializable {
|
|||||||
private Date expireTime;
|
private Date expireTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导入结果JSON(存储校验后的数据)
|
* 解析结果摘要
|
||||||
*/
|
*/
|
||||||
private String resultJson;
|
private String summary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结果代码(0成功 1失败)
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结果消息
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未识别字段(逗号分隔)
|
||||||
|
*/
|
||||||
|
private String unrecognizedFields;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 图片文件映射JSON
|
||||||
|
// */
|
||||||
|
// private String imageFilesJson;
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 视频文件映射JSON
|
||||||
|
// */
|
||||||
|
// private String videoFilesJson;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建时间
|
* 创建时间
|
||||||
|
|||||||
@ -0,0 +1,104 @@
|
|||||||
|
package com.yfd.platform.data.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 导入任务行数据表(DATA_JSON 拆为独立字段,彻底消除 CLOB)
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@TableName("IMPORT_TASK_ROW")
|
||||||
|
public class ImportTaskRow implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(type = IdType.ASSIGN_UUID)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
private String taskId;
|
||||||
|
|
||||||
|
private Integer rowIndex;
|
||||||
|
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
// ========== FishDraftData 字段(原 DATA_JSON) ==========
|
||||||
|
|
||||||
|
private String dataId;
|
||||||
|
|
||||||
|
private String stcd;
|
||||||
|
|
||||||
|
private String stnm;
|
||||||
|
|
||||||
|
private String rstcd;
|
||||||
|
|
||||||
|
private String ennm;
|
||||||
|
|
||||||
|
private String hbrvcd;
|
||||||
|
|
||||||
|
private String hbrvnm;
|
||||||
|
|
||||||
|
private String rvcd;
|
||||||
|
|
||||||
|
private String baseId;
|
||||||
|
|
||||||
|
private String baseName;
|
||||||
|
|
||||||
|
private Date strdt;
|
||||||
|
|
||||||
|
private String strdtStr;
|
||||||
|
|
||||||
|
private Date enddt;
|
||||||
|
|
||||||
|
private String ftp;
|
||||||
|
|
||||||
|
private String ftpName;
|
||||||
|
|
||||||
|
private Integer isfs;
|
||||||
|
|
||||||
|
private String direction;
|
||||||
|
|
||||||
|
private Integer fcnt;
|
||||||
|
|
||||||
|
private String fsz;
|
||||||
|
|
||||||
|
private String fwet;
|
||||||
|
|
||||||
|
private BigDecimal wt;
|
||||||
|
|
||||||
|
private String picpth;
|
||||||
|
|
||||||
|
private String vdpth;
|
||||||
|
|
||||||
|
private Date tm;
|
||||||
|
|
||||||
|
private String sourceType;
|
||||||
|
|
||||||
|
private Integer mouth;
|
||||||
|
|
||||||
|
private Integer yr;
|
||||||
|
|
||||||
|
// ========== 校验相关 ==========
|
||||||
|
|
||||||
|
private String warnings;
|
||||||
|
|
||||||
|
private String unrecognizedFields;
|
||||||
|
|
||||||
|
private String vdpthListJson;
|
||||||
|
|
||||||
|
private String picpthListJson;
|
||||||
|
|
||||||
|
private String vdpthWarnings;
|
||||||
|
|
||||||
|
private String picpthWarnings;
|
||||||
|
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createdAt;
|
||||||
|
}
|
||||||
@ -49,6 +49,12 @@ public class SysUserDataScope implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private String orgId;
|
private String orgId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源名称(根据orgType关联查询得出)
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String orgName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上级资源编码(可选,用于层级追溯)
|
* 上级资源编码(可选,用于层级追溯)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.yfd.platform.data.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class FishStatisticsVO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
private String realName;
|
||||||
|
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
private String contact;
|
||||||
|
|
||||||
|
private String basinNames;
|
||||||
|
|
||||||
|
private String stationNames;
|
||||||
|
|
||||||
|
private String basinCode;
|
||||||
|
|
||||||
|
private String stationCode;
|
||||||
|
|
||||||
|
private String reportMonth;
|
||||||
|
|
||||||
|
private Date minStrdt;
|
||||||
|
|
||||||
|
private Date maxEnddt;
|
||||||
|
|
||||||
|
private Integer totalFcnt;
|
||||||
|
|
||||||
|
private Integer hasData;
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package com.yfd.platform.data.mapper;
|
||||||
|
|
||||||
|
import com.yfd.platform.data.domain.vo.FishStatisticsVO;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface FishStatisticsMapper {
|
||||||
|
|
||||||
|
List<FishStatisticsVO> queryStatistics(@Param("basinCode") String basinCode,
|
||||||
|
@Param("stationCode") String stationCode,
|
||||||
|
@Param("startRow") int startRow,
|
||||||
|
@Param("endRow") int endRow);
|
||||||
|
|
||||||
|
int countStatistics(@Param("basinCode") String basinCode,
|
||||||
|
@Param("stationCode") String stationCode);
|
||||||
|
}
|
||||||
@ -40,11 +40,10 @@ public interface ImportTaskMapper extends BaseMapper<ImportTask> {
|
|||||||
|
|
||||||
@Select("SELECT * FROM (" +
|
@Select("SELECT * FROM (" +
|
||||||
"SELECT ID, IMPORT_NO, BIZ_TYPE, FILE_NAME, FILE_SIZE, FILE_PATH, TEMP_DIR, " +
|
"SELECT ID, IMPORT_NO, BIZ_TYPE, FILE_NAME, FILE_SIZE, FILE_PATH, TEMP_DIR, " +
|
||||||
"TOTAL_COUNT, SUCCESS_COUNT, FAIL_COUNT, STATUS, ERROR_MSG, " +
|
"TOTAL_COUNT, SUCCESS_COUNT, FAIL_COUNT, STATUS, ERROR_MSG " +
|
||||||
"UPLOAD_USER_ID, UPLOAD_TIME, EXPIRE_TIME, CREATED_AT, UPDATED_AT " +
|
|
||||||
"FROM IMPORT_TASK " +
|
"FROM IMPORT_TASK " +
|
||||||
"WHERE UPLOAD_USER_ID = #{uploadUserId} " +
|
"WHERE UPLOAD_USER_ID = #{uploadUserId} " +
|
||||||
"AND STATUS IN ('UPLOADED', 'PARSING', 'VALIDATED') " +
|
"AND STATUS IN ('UPLOADED', 'PARSING', 'VALIDATED','FAILED') " +
|
||||||
"ORDER BY CREATED_AT DESC" +
|
"ORDER BY CREATED_AT DESC" +
|
||||||
") WHERE ROWNUM = 1")
|
") WHERE ROWNUM = 1")
|
||||||
List<ImportTask> selectByUserIdAndStatuses(@Param("uploadUserId") String uploadUserId);
|
List<ImportTask> selectByUserIdAndStatuses(@Param("uploadUserId") String uploadUserId);
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.yfd.platform.data.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.yfd.platform.data.domain.ImportTaskRow;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 导入任务行数据表 Mapper 接口
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface ImportTaskRowMapper extends BaseMapper<ImportTaskRow> {
|
||||||
|
|
||||||
|
List<ImportTaskRow> selectByTaskId(@Param("taskId") String taskId);
|
||||||
|
|
||||||
|
List<ImportTaskRow> selectByTaskIdAndStatus(@Param("taskId") String taskId, @Param("status") String status);
|
||||||
|
|
||||||
|
int deleteByTaskId(@Param("taskId") String taskId);
|
||||||
|
}
|
||||||
@ -43,4 +43,18 @@ public interface SysUserDataScopeMapper extends BaseMapper<SysUserDataScope> {
|
|||||||
* 查询有效权限(状态=1且在有效期内的)
|
* 查询有效权限(状态=1且在有效期内的)
|
||||||
*/
|
*/
|
||||||
List<SysUserDataScope> selectValidPermissions(@Param("userId") String userId);
|
List<SysUserDataScope> selectValidPermissions(@Param("userId") String userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户ID查询权限列表(含关联资源名称)
|
||||||
|
* orgType=STATION时关联SD_ENGINFO_B_H获取ennm(工程名称)
|
||||||
|
* orgType=HBRVCD时关联SD_HBRV_DIC获取hbrvnm(基地流域名称)
|
||||||
|
* orgType=BASE时关联SD_HYDROBASE获取basename(基地名称)
|
||||||
|
* orgType=RVCD时关联SD_RVCD_DIC获取rvnm(流域名称)
|
||||||
|
*/
|
||||||
|
List<SysUserDataScope> selectValidPermissionsWithName(@Param("userId") String userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量根据用户ID查询权限列表(含关联资源名称)
|
||||||
|
*/
|
||||||
|
List<SysUserDataScope> selectValidPermissionsWithNameByUserIds(@Param("userIds") List<String> userIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,4 +96,9 @@ public interface IFishDraftDataService extends IService<FishDraftData> {
|
|||||||
* 审批人修改数据并记录变更日志
|
* 审批人修改数据并记录变更日志
|
||||||
*/
|
*/
|
||||||
boolean updateByIdWithLog(FishDraftData fishDraftData);
|
boolean updateByIdWithLog(FishDraftData fishDraftData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存草稿(带事务,仅包裹 INSERT)
|
||||||
|
*/
|
||||||
|
boolean batchSaveDraft(List<FishDraftData> fishDraftDataList);
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.yfd.platform.data.service;
|
package com.yfd.platform.data.service;
|
||||||
|
|
||||||
|
import com.yfd.platform.data.domain.FishDraftData;
|
||||||
import com.yfd.platform.data.domain.FishImportRequest;
|
import com.yfd.platform.data.domain.FishImportRequest;
|
||||||
import com.yfd.platform.data.domain.FishImportResult;
|
import com.yfd.platform.data.domain.FishImportResult;
|
||||||
import com.yfd.platform.data.utils.ZipFileUtil;
|
import com.yfd.platform.data.utils.ZipFileUtil;
|
||||||
@ -7,6 +8,8 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -52,8 +55,6 @@ public interface IFishImportService {
|
|||||||
|
|
||||||
void processAttachments(FishImportResult result, ZipFileUtil.ZipContent zipContent);
|
void processAttachments(FishImportResult result, ZipFileUtil.ZipContent zipContent);
|
||||||
|
|
||||||
void processAttachmentsAsync(java.util.List<com.yfd.platform.data.domain.FishDraftData> dataList,
|
void processAttachmentsAsync(List<FishDraftData> dataList,
|
||||||
java.util.Map<String, String> imageFiles,
|
|
||||||
java.util.Map<String, String> videoFiles,
|
|
||||||
String tempDir);
|
String tempDir);
|
||||||
}
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package com.yfd.platform.data.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.yfd.platform.common.DataSourceRequest;
|
||||||
|
import com.yfd.platform.data.domain.vo.FishStatisticsVO;
|
||||||
|
|
||||||
|
public interface IFishStatisticsService {
|
||||||
|
|
||||||
|
Page<FishStatisticsVO> queryPage(DataSourceRequest dataSourceRequest);
|
||||||
|
}
|
||||||
@ -2,7 +2,9 @@ package com.yfd.platform.data.service;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.yfd.platform.data.domain.FishImportResult;
|
||||||
import com.yfd.platform.data.domain.ImportTask;
|
import com.yfd.platform.data.domain.ImportTask;
|
||||||
|
import com.yfd.platform.data.domain.ImportTaskRow;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -79,12 +81,27 @@ public interface IImportTaskService extends IService<ImportTask> {
|
|||||||
ImportTask getCurrentTaskByUserId(String uploadUserId);
|
ImportTask getCurrentTaskByUserId(String uploadUserId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存导入结果JSON
|
* 保存导入结果(同时写入 IMPORT_TASK 汇总字段和 IMPORT_TASK_ROW 行数据)
|
||||||
*/
|
*/
|
||||||
boolean saveResultJson(String taskId, String resultJson);
|
boolean saveResultJson(String taskId, FishImportResult result);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户最后一次导入结果(用于断点续传或查看历史)
|
* 获取用户最后一次导入结果(用于断点续传或查看历史)
|
||||||
*/
|
*/
|
||||||
ImportTask getLastImportResult(String uploadUserId);
|
ImportTask getLastImportResult(String uploadUserId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据任务ID查询行数据列表
|
||||||
|
*/
|
||||||
|
List<ImportTaskRow> getRowsByTaskId(String taskId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据任务ID和状态查询行数据列表
|
||||||
|
*/
|
||||||
|
List<ImportTaskRow> getRowsByTaskIdAndStatus(String taskId, String status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据任务ID重建 FishImportResult 对象(从行表组装)
|
||||||
|
*/
|
||||||
|
FishImportResult buildImportResult(String taskId);
|
||||||
}
|
}
|
||||||
@ -198,6 +198,7 @@ public class FishDraftDataServiceImpl extends ServiceImpl<FishDraftDataMapper, F
|
|||||||
public boolean saveDraft(FishDraftData fishDraftData) {
|
public boolean saveDraft(FishDraftData fishDraftData) {
|
||||||
fishDraftData.setStatus("DRAFT");
|
fishDraftData.setStatus("DRAFT");
|
||||||
fishDraftData.setDeletedFlag(0);
|
fishDraftData.setDeletedFlag(0);
|
||||||
|
fishDraftData.setEnddt(fishDraftData.getStrdt());
|
||||||
fishDraftData.setLockFlag(0);
|
fishDraftData.setLockFlag(0);
|
||||||
return this.save(fishDraftData);
|
return this.save(fishDraftData);
|
||||||
}
|
}
|
||||||
@ -209,6 +210,7 @@ public class FishDraftDataServiceImpl extends ServiceImpl<FishDraftDataMapper, F
|
|||||||
if (existing == null || existing.getLockFlag() == 1) {
|
if (existing == null || existing.getLockFlag() == 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
fishDraftData.setEnddt(fishDraftData.getStrdt());
|
||||||
boolean isSubmitted = existing.getApprovalId() != null && StrUtil.isNotBlank(existing.getApprovalId());
|
boolean isSubmitted = existing.getApprovalId() != null && StrUtil.isNotBlank(existing.getApprovalId());
|
||||||
try {
|
try {
|
||||||
String beforeJson = objectMapper.writeValueAsString(existing);
|
String beforeJson = objectMapper.writeValueAsString(existing);
|
||||||
@ -655,5 +657,15 @@ public class FishDraftDataServiceImpl extends ServiceImpl<FishDraftDataMapper, F
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean batchSaveDraft(List<FishDraftData> fishDraftDataList) {
|
||||||
|
if (fishDraftDataList == null || fishDraftDataList.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
boolean result = this.saveBatch(fishDraftDataList, 500);
|
||||||
|
log.info("批量保存草稿完成, 共{}条, 耗时{}ms", fishDraftDataList.size(), System.currentTimeMillis() - start);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -3,6 +3,7 @@ package com.yfd.platform.data.service.impl;
|
|||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
|
||||||
import com.yfd.platform.data.domain.FishDraftData;
|
import com.yfd.platform.data.domain.FishDraftData;
|
||||||
import com.yfd.platform.data.domain.FishImportRequest;
|
import com.yfd.platform.data.domain.FishImportRequest;
|
||||||
import com.yfd.platform.data.domain.FishImportResult;
|
import com.yfd.platform.data.domain.FishImportResult;
|
||||||
@ -19,6 +20,7 @@ import com.yfd.platform.system.service.ISysDictionaryService;
|
|||||||
import com.yfd.platform.utils.SecurityUtils;
|
import com.yfd.platform.utils.SecurityUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.poi.EncryptedDocumentException;
|
||||||
import org.apache.poi.ss.usermodel.*;
|
import org.apache.poi.ss.usermodel.*;
|
||||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
@ -33,9 +35,13 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
import java.text.ParsePosition;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -74,6 +80,12 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
@Resource
|
@Resource
|
||||||
private SysUserDataScopeMapper userDataScopeMapper;
|
private SysUserDataScopeMapper userDataScopeMapper;
|
||||||
|
|
||||||
|
private final ExecutorService attachmentExecutor = Executors.newFixedThreadPool(4, r -> {
|
||||||
|
Thread t = new Thread(r, "attachment-upload");
|
||||||
|
t.setDaemon(true);
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
|
||||||
private static final Map<String, String> EXCEL_COLUMN_MAPPING = new LinkedHashMap<>();
|
private static final Map<String, String> EXCEL_COLUMN_MAPPING = new LinkedHashMap<>();
|
||||||
private static final Map<Integer, String> EXCEL_COLUMN_INDEX_MAPPING = new LinkedHashMap<>();
|
private static final Map<Integer, String> EXCEL_COLUMN_INDEX_MAPPING = new LinkedHashMap<>();
|
||||||
|
|
||||||
@ -176,38 +188,14 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
|
|
||||||
Map<Integer, String> columnIndexMap = new HashMap<>(EXCEL_COLUMN_INDEX_MAPPING);
|
Map<Integer, String> columnIndexMap = new HashMap<>(EXCEL_COLUMN_INDEX_MAPPING);
|
||||||
|
|
||||||
int totalRows = sheet.getLastRowNum();
|
|
||||||
for (int i = 1; i <= totalRows; i++) {
|
|
||||||
Row row = sheet.getRow(i);
|
|
||||||
if (row == null || isRowEmpty(row)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
FishImportResult.FishImportRow importRow = parseRow(result, row, columnIndexMap, i, uploadUserId);
|
|
||||||
result.setTotalCount(result.getTotalCount() + 1);
|
|
||||||
if (importRow.getData() != null && importRow.getWarnings().isEmpty()) {
|
|
||||||
result.addSuccessRow(importRow);
|
|
||||||
result.setSuccessCount(result.getSuccessCount() + 1);
|
|
||||||
} else {
|
|
||||||
result.addFailedRow(importRow);
|
|
||||||
result.setFailedCount(result.getFailedCount() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.setSummary(String.format("共解析%d条数据,失败%d条",
|
|
||||||
result.getSuccessCount(), result.getFailedCount()));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private FishImportResult.FishImportRow parseRow(FishImportResult result, Row row, Map<Integer, String> columnIndexMap, int rowIndex, String userId) {
|
|
||||||
FishImportResult.FishImportRow importRow = new FishImportResult.FishImportRow(rowIndex);
|
|
||||||
FishDraftData data = new FishDraftData();
|
|
||||||
data.setId(UUID.randomUUID().toString());
|
|
||||||
Set<String> allowedHbrvcdSet = new HashSet<>();
|
Set<String> allowedHbrvcdSet = new HashSet<>();
|
||||||
Set<String> directStcdSet = new HashSet<>();
|
Set<String> directStcdSet = new HashSet<>();
|
||||||
Set<String> directBHSet = new HashSet<>();
|
|
||||||
if (userId != null) {
|
List<SdFpssBH> sdFpssList=new ArrayList<>();
|
||||||
List<SysUserDataScope> permissions = userDataScopeMapper.selectValidPermissions(userId);
|
List<SdHbrvDic> allowedHbrvcdList = new ArrayList<>();
|
||||||
|
List<SdEngInfoBH> directStcdList = new ArrayList<>();
|
||||||
|
if (uploadUserId != null) {
|
||||||
|
List<SysUserDataScope> permissions = userDataScopeMapper.selectValidPermissions(uploadUserId);
|
||||||
if (permissions != null && !permissions.isEmpty()) {
|
if (permissions != null && !permissions.isEmpty()) {
|
||||||
for (SysUserDataScope permission : permissions) {
|
for (SysUserDataScope permission : permissions) {
|
||||||
String orgType = permission.getOrgType();
|
String orgType = permission.getOrgType();
|
||||||
@ -226,7 +214,6 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set<String> allStcdSet = new HashSet<>();
|
|
||||||
|
|
||||||
if (!allowedHbrvcdSet.isEmpty() || !directStcdSet.isEmpty()) {
|
if (!allowedHbrvcdSet.isEmpty() || !directStcdSet.isEmpty()) {
|
||||||
if (!allowedHbrvcdSet.isEmpty()) {
|
if (!allowedHbrvcdSet.isEmpty()) {
|
||||||
@ -250,14 +237,52 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!allowedHbrvcdSet.isEmpty()){
|
||||||
|
List<SdHbrvDic> sdHbrvDics = sdHbrvDicMapper.selectList(new LambdaQueryWrapper<SdHbrvDic>().in(SdHbrvDic::getHbrvcd, allowedHbrvcdSet).select(SdHbrvDic::getHbrvcd, SdHbrvDic::getHbrvnm));
|
||||||
|
allowedHbrvcdList.addAll(sdHbrvDics);
|
||||||
|
}
|
||||||
|
|
||||||
if (!directStcdSet.isEmpty()) {
|
if (!directStcdSet.isEmpty()) {
|
||||||
List<SdFpssBH> sdFpssBHS = fpssBHMapper.selectList(new LambdaQueryWrapper<SdFpssBH>().in(SdFpssBH::getRstcd, directStcdSet).select(SdFpssBH::getStcd));
|
List<SdFpssBH> sdFpssBHS = fpssBHMapper.selectList(new LambdaQueryWrapper<SdFpssBH>().in(SdFpssBH::getRstcd, directStcdSet).select(SdFpssBH::getStcd, SdFpssBH::getStnm));
|
||||||
for (SdFpssBH sdFpssBH : sdFpssBHS) {
|
//设施
|
||||||
if (sdFpssBH.getStcd() != null) {
|
sdFpssList.addAll(sdFpssBHS.stream().filter(sdFpssBH -> sdFpssBH.getStcd() != null).toList());
|
||||||
directBHSet.add(sdFpssBH.getStcd());
|
|
||||||
}
|
//电站
|
||||||
|
directStcdList.addAll(engInfoBHMapper.selectList(new LambdaQueryWrapper<SdEngInfoBH>().in(SdEngInfoBH::getStcd, directStcdSet).select(SdEngInfoBH::getStcd, SdEngInfoBH::getEnnm)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalRows = sheet.getLastRowNum();
|
||||||
|
for (int i = 1; i <= totalRows; i++) {
|
||||||
|
Row row = sheet.getRow(i);
|
||||||
|
if (row == null || isRowEmpty(row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FishImportResult.FishImportRow importRow = parseRow(result, row, columnIndexMap, i, uploadUserId,allowedHbrvcdList,directStcdList,sdFpssList);
|
||||||
|
result.setTotalCount(result.getTotalCount() + 1);
|
||||||
|
if (importRow.getData() != null && importRow.getWarnings().isEmpty()) {
|
||||||
|
result.addSuccessRow(importRow);
|
||||||
|
result.setSuccessCount(result.getSuccessCount() + 1);
|
||||||
|
} else {
|
||||||
|
result.addFailedRow(importRow);
|
||||||
|
result.setFailedCount(result.getFailedCount() + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result.setSummary(String.format("共解析%d条数据,失败%d条",
|
||||||
|
result.getSuccessCount(), result.getFailedCount()));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FishImportResult.FishImportRow parseRow(FishImportResult result, Row row, Map<Integer, String> columnIndexMap, int rowIndex, String userId,List<SdHbrvDic> allowedHbrvcdList,List<SdEngInfoBH> directStcdList,List<SdFpssBH> sdFpssList) {
|
||||||
|
|
||||||
|
|
||||||
|
FishImportResult.FishImportRow importRow = new FishImportResult.FishImportRow(rowIndex);
|
||||||
|
FishDraftData data = new FishDraftData();
|
||||||
|
data.setId(UUID.randomUUID().toString());
|
||||||
|
|
||||||
for (Map.Entry<Integer, String> entry : columnIndexMap.entrySet()) {
|
for (Map.Entry<Integer, String> entry : columnIndexMap.entrySet()) {
|
||||||
Integer columnIndex = entry.getKey();
|
Integer columnIndex = entry.getKey();
|
||||||
String fieldName = entry.getValue();
|
String fieldName = entry.getValue();
|
||||||
@ -271,20 +296,14 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
data.setEnnm(cellValue.trim());
|
data.setEnnm(cellValue.trim());
|
||||||
data.setRstcd(cellValue);
|
data.setRstcd(cellValue);
|
||||||
} else {
|
} else {
|
||||||
String stcd = resolveStationCode(cellValue.trim());
|
String stcd = directStcdList.stream().filter(sdEngInfoBH -> sdEngInfoBH.getEnnm().equals(cellValue.trim())).map(SdEngInfoBH::getStcd).findFirst().orElse( null);
|
||||||
if (stcd == null) {
|
if (StrUtil.isBlank(stcd)) {
|
||||||
importRow.getWarnings().add("rstcd");
|
importRow.getWarnings().add("rstcd");
|
||||||
data.setEnnm(cellValue.trim());
|
data.setEnnm(cellValue.trim());
|
||||||
|
data.setRstcd(cellValue.trim());
|
||||||
} else {
|
} else {
|
||||||
if (directStcdSet.contains(stcd)) {
|
data.setEnnm(cellValue.trim());
|
||||||
data.setEnnm(cellValue.trim());
|
data.setRstcd(stcd);
|
||||||
data.setRstcd(stcd);
|
|
||||||
} else {
|
|
||||||
importRow.getWarnings().add("rstcd");
|
|
||||||
data.setEnnm(cellValue.trim());
|
|
||||||
data.setRstcd(cellValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -322,33 +341,17 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
data.setHbrvcd(cellValue);
|
data.setHbrvcd(cellValue);
|
||||||
data.setHbrvnm(cellValue);
|
data.setHbrvnm(cellValue);
|
||||||
} else {
|
} else {
|
||||||
String hbrvcdCode = resolveHbrvcdCode(cellValue.trim());
|
String hbrvcdCode = allowedHbrvcdList.stream().filter(sdHbrvDic -> sdHbrvDic.getHbrvnm().equals(cellValue.trim())).map(SdHbrvDic::getHbrvcd).findFirst().orElse( null);
|
||||||
if (hbrvcdCode == null) {
|
if (StrUtil.isBlank(hbrvcdCode)) {
|
||||||
importRow.getWarnings().add("hbrvcd");
|
importRow.getWarnings().add("hbrvcd");
|
||||||
data.setHbrvcd(cellValue.trim());
|
data.setHbrvcd(cellValue.trim());
|
||||||
data.setHbrvnm(cellValue.trim());
|
data.setHbrvnm(cellValue.trim());
|
||||||
} else {
|
} else {
|
||||||
if (allowedHbrvcdSet.contains(hbrvcdCode)) {
|
data.setHbrvcd(hbrvcdCode);
|
||||||
data.setHbrvcd(hbrvcdCode);
|
data.setHbrvnm(cellValue.trim());
|
||||||
data.setHbrvnm(cellValue.trim());
|
|
||||||
} else {
|
|
||||||
importRow.getWarnings().add("hbrvcd");
|
|
||||||
data.setHbrvcd(cellValue.trim());
|
|
||||||
data.setHbrvnm(cellValue.trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if (StringUtils.hasText(cellValue)) {
|
//
|
||||||
// String rvcd = resolveHbrvcdCode(cellValue.trim());
|
|
||||||
// if (rvcd == null) {
|
|
||||||
// importRow.getWarnings().add(fieldName);
|
|
||||||
// data.setRvcd(cellValue);
|
|
||||||
// } else {
|
|
||||||
// data.setHbrvcd(rvcd);
|
|
||||||
// data.setHbrvcd(rvcd);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
break;
|
break;
|
||||||
case "tm":
|
case "tm":
|
||||||
if (!StringUtils.hasText(cellValue)) {
|
if (!StringUtils.hasText(cellValue)) {
|
||||||
@ -379,7 +382,10 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "fsz":
|
case "fsz":
|
||||||
data.setFsz(cellValue.trim());
|
if (StringUtils.hasText(cellValue)) {
|
||||||
|
String parsedFsz = parseFishSizeRange(cellValue.trim());
|
||||||
|
data.setFsz(parsedFsz);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "fcnt":
|
case "fcnt":
|
||||||
if (!StringUtils.hasText(cellValue)) {
|
if (!StringUtils.hasText(cellValue)) {
|
||||||
@ -412,13 +418,20 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
break;
|
break;
|
||||||
case "strdt":
|
case "strdt":
|
||||||
if (!StringUtils.hasText(cellValue)) {
|
if (!StringUtils.hasText(cellValue)) {
|
||||||
importRow.getWarnings().add(fieldName);
|
importRow.getWarnings().add("strdtStr");
|
||||||
|
data.setStrdtStr(cellValue);
|
||||||
} else {
|
} else {
|
||||||
Date strdt = parseDate(cellValue);
|
Date strdt = parseDate(cellValue);
|
||||||
if (strdt == null) {
|
if (strdt == null) {
|
||||||
importRow.getWarnings().add(fieldName);
|
importRow.getWarnings().add("strdtStr");
|
||||||
|
data.setStrdt(null);
|
||||||
|
data.setStrdtStr(cellValue.replaceAll("T", " "));
|
||||||
|
}else{
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
String dateString = sdf.format(strdt);
|
||||||
|
data.setStrdtStr(dateString);
|
||||||
|
data.setStrdt(strdt);
|
||||||
}
|
}
|
||||||
data.setStrdt(strdt);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "enddt":
|
case "enddt":
|
||||||
@ -445,6 +458,9 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
Map<String, String> videoFiles = result.getVideoFiles();
|
Map<String, String> videoFiles = result.getVideoFiles();
|
||||||
|
|
||||||
for (String fileName : vdpth.split(";")) {
|
for (String fileName : vdpth.split(";")) {
|
||||||
|
if(StrUtil.isBlank(fileName)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (String entryName : videoFiles.keySet()) {
|
for (String entryName : videoFiles.keySet()) {
|
||||||
if (entryName.equals(fileName) || entryName.endsWith("/" + fileName) || entryName.endsWith("\\" + fileName)) {
|
if (entryName.equals(fileName) || entryName.endsWith("/" + fileName) || entryName.endsWith("\\" + fileName)) {
|
||||||
Map<String, String> objectObjectHashMap = new HashMap<>();
|
Map<String, String> objectObjectHashMap = new HashMap<>();
|
||||||
@ -452,6 +468,13 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
objectObjectHashMap.put("value", fileName);
|
objectObjectHashMap.put("value", fileName);
|
||||||
importRow.getVdpthList().add(objectObjectHashMap);
|
importRow.getVdpthList().add(objectObjectHashMap);
|
||||||
vdpthList.add(fileName);
|
vdpthList.add(fileName);
|
||||||
|
} else if (!com.yfd.platform.utils.FileUtil.isVideoFileName(fileName)) {
|
||||||
|
Map<String, String> objectObjectHashMap = new HashMap<>();
|
||||||
|
objectObjectHashMap.put("name", fileName);
|
||||||
|
objectObjectHashMap.put("value", fileName);
|
||||||
|
importRow.getVdpthList().add(objectObjectHashMap);
|
||||||
|
vdpthList.add(fileName);
|
||||||
|
importRow.getVdpthsWarnings().add(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,6 +490,9 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
Map<String, String> imageFiles = result.getImageFiles();
|
Map<String, String> imageFiles = result.getImageFiles();
|
||||||
|
|
||||||
for (String fileName : picpth.split(";")) {
|
for (String fileName : picpth.split(";")) {
|
||||||
|
if(StrUtil.isBlank(fileName)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (String entryName : imageFiles.keySet()) {
|
for (String entryName : imageFiles.keySet()) {
|
||||||
if (entryName.equals(fileName) || entryName.endsWith("/" + fileName) || entryName.endsWith("\\" + fileName)) {
|
if (entryName.equals(fileName) || entryName.endsWith("/" + fileName) || entryName.endsWith("\\" + fileName)) {
|
||||||
Map<String, String> objectObjectHashMap = new HashMap<>();
|
Map<String, String> objectObjectHashMap = new HashMap<>();
|
||||||
@ -474,6 +500,13 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
objectObjectHashMap.put("value", fileName);
|
objectObjectHashMap.put("value", fileName);
|
||||||
importRow.getPicpthList().add(objectObjectHashMap);
|
importRow.getPicpthList().add(objectObjectHashMap);
|
||||||
picpthList.add(fileName);
|
picpthList.add(fileName);
|
||||||
|
} else if (!com.yfd.platform.utils.FileUtil.isImageFileName(fileName)) {
|
||||||
|
Map<String, String> objectObjectHashMap = new HashMap<>();
|
||||||
|
objectObjectHashMap.put("name", fileName);
|
||||||
|
objectObjectHashMap.put("value", fileName);
|
||||||
|
importRow.getPicpthList().add(objectObjectHashMap);
|
||||||
|
picpthList.add(fileName);
|
||||||
|
importRow.getPicpthsWarnings().add(fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,21 +531,14 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
importRow.getWarnings().add("stcd");
|
importRow.getWarnings().add("stcd");
|
||||||
data.setStcd(cellValue);
|
data.setStcd(cellValue);
|
||||||
} else {
|
} else {
|
||||||
String stcd = resolveFpssCode(cellValue.trim());
|
String stcd = sdFpssList.stream().filter(sdFpssBH -> sdFpssBH.getStnm().equals(cellValue.trim())).map(SdFpssBH::getStcd).findFirst().orElse( null);
|
||||||
if (stcd == null) {
|
if (StrUtil.isBlank(stcd)) {
|
||||||
importRow.getWarnings().add("stcd");
|
importRow.getWarnings().add("stcd");
|
||||||
data.setStcd(cellValue.trim());
|
data.setStcd(cellValue.trim());
|
||||||
data.setStnm(cellValue.trim());
|
data.setStnm(cellValue.trim());
|
||||||
} else {
|
} else {
|
||||||
if (directBHSet.contains(stcd)) {
|
data.setStnm(cellValue.trim());
|
||||||
data.setStnm(cellValue.trim());
|
data.setStcd(stcd);
|
||||||
data.setStcd(stcd);
|
|
||||||
} else {
|
|
||||||
importRow.getWarnings().add("stcd");
|
|
||||||
data.setStcd(cellValue.trim());
|
|
||||||
data.setStnm(cellValue.trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -520,7 +546,8 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
log.error("字段[" + fieldName + "]解析异常: " + e.getMessage());
|
||||||
|
// e.printStackTrace();
|
||||||
// importRow.getWarnings().add("字段[" + fieldName + "]解析异常: " + e.getMessage());
|
// importRow.getWarnings().add("字段[" + fieldName + "]解析异常: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -529,34 +556,111 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
return importRow;
|
return importRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析鱼类体长范围
|
||||||
|
* 从混乱的字符串中提取所有数字(支持小数),返回 "最小值~最大值" 格式
|
||||||
|
* 例如: "123123&234.dey76fd78" -> 提取出 123123, 234., 76, 78 -> "76~123123"
|
||||||
|
*
|
||||||
|
* @param input 原始字符串
|
||||||
|
* @return 格式化后的范围字符串,如果没有有效数字则返回原字符串
|
||||||
|
*/
|
||||||
|
private String parseFishSizeRange(String input) {
|
||||||
|
if (!StringUtils.hasText(input)) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用正则表达式提取所有数字(包括整数和小数)
|
||||||
|
// 解释: \d+ 匹配一个或多个数字, (\.\d+)? 匹配可选的小数部分
|
||||||
|
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\d+(?:\\.\\d+)?");
|
||||||
|
java.util.regex.Matcher matcher = pattern.matcher(input);
|
||||||
|
|
||||||
|
List<Double> numbers = new ArrayList<>();
|
||||||
|
while (matcher.find()) {
|
||||||
|
try {
|
||||||
|
String numStr = matcher.group();
|
||||||
|
// 排除单独的点或无效格式
|
||||||
|
if (numStr != null && !numStr.isEmpty()) {
|
||||||
|
numbers.add(Double.parseDouble(numStr));
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 忽略无法解析的数字
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有提取到任何数字,返回原字符串或空
|
||||||
|
if (numbers.isEmpty()) {
|
||||||
|
log.warn("鱼类体长字段未提取到有效数字: {}", input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出最小值和最大值
|
||||||
|
double min = numbers.stream().mapToDouble(Double::doubleValue).min().getAsDouble();
|
||||||
|
double max = numbers.stream().mapToDouble(Double::doubleValue).max().getAsDouble();
|
||||||
|
|
||||||
|
// 格式化结果:如果是整数则不显示小数点,否则保留原有精度
|
||||||
|
String minStr = formatNumber(min);
|
||||||
|
String maxStr = formatNumber(max);
|
||||||
|
|
||||||
|
return minStr + "~" + maxStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化数字:如果是整数则去掉 .0,否则保留小数
|
||||||
|
*/
|
||||||
|
private String formatNumber(double value) {
|
||||||
|
if (value == Math.floor(value) && !Double.isInfinite(value)) {
|
||||||
|
return String.valueOf((long) value);
|
||||||
|
} else {
|
||||||
|
// 去除末尾多余的 0,例如 12.50 -> 12.5
|
||||||
|
return String.valueOf(value).replaceAll("\\.?0+$", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void validateStationFpssRelation(FishDraftData data, FishImportResult.FishImportRow importRow) {
|
private void validateStationFpssRelation(FishDraftData data, FishImportResult.FishImportRow importRow) {
|
||||||
loadStationAndBaseCache();
|
if (importRow.getWarnings().contains("hbrvcd")) {
|
||||||
if (StringUtils.hasText(data.getHbrvcd()) && StringUtils.hasText(data.getRstcd())) {
|
if (!importRow.getWarnings().contains("rstcd")) {
|
||||||
if (!validateStationBelongsToBase(data.getRstcd(), data.getHbrvcd())) {
|
importRow.getWarnings().add("rstcd");
|
||||||
if (!importRow.getWarnings().contains("hbrvcd")) {
|
}
|
||||||
importRow.getWarnings().add("hbrvcd");
|
if (!importRow.getWarnings().contains("stcd")) {
|
||||||
}
|
importRow.getWarnings().add("stcd");
|
||||||
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 (importRow.getWarnings().contains("rstcd")) {
|
||||||
if (!validateFpssBelongsToStation(data.getStcd(), data.getRstcd())) {
|
if (!importRow.getWarnings().contains("stcd")) {
|
||||||
if (!importRow.getWarnings().contains("hbrvcd")) {
|
importRow.getWarnings().add("stcd");
|
||||||
importRow.getWarnings().add("hbrvcd");
|
|
||||||
}
|
|
||||||
if (!importRow.getWarnings().contains("stcd")) {
|
|
||||||
importRow.getWarnings().add("stcd");
|
|
||||||
}
|
|
||||||
if (!importRow.getWarnings().contains("rstcd")) {
|
|
||||||
importRow.getWarnings().add("rstcd");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadStationAndBaseCache();
|
||||||
|
// if (StringUtils.hasText(data.getHbrvcd()) && StringUtils.hasText(data.getRstcd())) {
|
||||||
|
// if (!validateStationBelongsToBase(data.getRstcd(), data.getHbrvcd())) {
|
||||||
|
// if (!importRow.getWarnings().contains("hbrvcd")) {
|
||||||
|
// importRow.getWarnings().add("hbrvcd");
|
||||||
|
// }
|
||||||
|
// 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("hbrvcd")) {
|
||||||
|
// importRow.getWarnings().add("hbrvcd");
|
||||||
|
// }
|
||||||
|
// if (!importRow.getWarnings().contains("stcd")) {
|
||||||
|
// importRow.getWarnings().add("stcd");
|
||||||
|
// }
|
||||||
|
// if (!importRow.getWarnings().contains("rstcd")) {
|
||||||
|
// importRow.getWarnings().add("rstcd");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadStationAndBaseCache() {
|
private void loadStationAndBaseCache() {
|
||||||
@ -1119,28 +1223,103 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Date parseDate(String dateStr) {
|
|
||||||
|
private static Date parseDate(String dateStr) {
|
||||||
if (!StringUtils.hasText(dateStr)) {
|
if (!StringUtils.hasText(dateStr)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
dateStr = dateStr.trim();
|
||||||
|
|
||||||
|
// 支持的日期格式列表(按常用程度排序)
|
||||||
String[] patterns = {
|
String[] patterns = {
|
||||||
"yyyy-MM-dd HH:mm:ss",
|
"yyyy-MM-dd HH:mm:ss", // 2024-01-15 14:30:00
|
||||||
"yyyy-MM-dd",
|
"yyyy-MM-dd'T'HH:mm:ss", // ⭐ 新增:支持 2024-01-15T14:30:00 (ISO格式)
|
||||||
"yyyy/MM/dd",
|
"yyyy-MM-dd HH:mm", // 2024-01-15 14:30
|
||||||
"yyyy.MM.dd",
|
"yyyy-MM-dd'T'HH:mm", // ⭐ 确保这一行存在且正确:支持 2024-01-15T14:30
|
||||||
"yyyyMMdd"
|
"yyyy-MM-dd", // 2024-01-15
|
||||||
|
"yyyy/MM/dd HH:mm:ss", // 2024/01/15 14:30:00
|
||||||
|
"yyyy/MM/dd HH:mm", // 2024/01/15 14:30
|
||||||
|
"yyyy/MM/dd", // 2024/01/15 (标准双位)
|
||||||
|
"yyyy/M/d HH:mm:ss", // 支持 2024/1/1 14:30:00
|
||||||
|
"yyyy/M/d HH:mm", // 支持 2024/1/1 14:30
|
||||||
|
"yyyy/M/d", // 支持 2024/1/1 (单位数)
|
||||||
|
"yyyy.MM.dd HH:mm:ss", // 2024.01.15 14:30:00
|
||||||
|
"yyyy.MM.dd", // 2024.01.15
|
||||||
|
"yyyy.M.d", // 支持 2024.1.1
|
||||||
|
"yyyyMMdd HHmmss", // 20240115 143000
|
||||||
|
"yyyyMMdd", // 20240115
|
||||||
|
"yyyy年MM月dd日", // 2024年01月15日
|
||||||
|
"yyyy年M月d日", // 支持 2024年1月1日
|
||||||
|
"yyyy年MM月dd日HH时mm分ss秒" // 2024年01月15日14时30分00秒
|
||||||
};
|
};
|
||||||
|
|
||||||
for (String pattern : patterns) {
|
for (String pattern : patterns) {
|
||||||
try {
|
try {
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
||||||
sdf.setLenient(false);
|
sdf.setLenient(false);
|
||||||
return sdf.parse(dateStr);
|
ParsePosition pos = new ParsePosition(0);
|
||||||
} catch (ParseException ignored) {
|
Date parsedDate = sdf.parse(dateStr, pos);
|
||||||
|
if (parsedDate != null && pos.getIndex() == dateStr.length()) {
|
||||||
|
// 整个字符串都被成功解析
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(parsedDate);
|
||||||
|
int year = cal.get(Calendar.YEAR);
|
||||||
|
if (year >= 1900 && year <= 2100) {
|
||||||
|
return parsedDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.debug("无法解析日期: '{}'", dateStr);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// private Date parseDate(String dateStr) {
|
||||||
|
// if (!StringUtils.hasText(dateStr)) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 去除首尾空格
|
||||||
|
// dateStr = dateStr.trim();
|
||||||
|
//
|
||||||
|
// // 支持的日期格式列表(按常用程度排序)
|
||||||
|
// String[] patterns = {
|
||||||
|
// "yyyy-MM-dd HH:mm:ss", // 2024-01-15 14:30:00
|
||||||
|
// "yyyy-MM-dd", // 2024-01-15
|
||||||
|
// "yyyy/MM/dd HH:mm:ss", // 2024/01/15 14:30:00
|
||||||
|
// "yyyy/MM/dd", // 2024/01/15
|
||||||
|
// "yyyy.MM.dd HH:mm:ss", // 2024.01.15 14:30:00
|
||||||
|
// "yyyy.MM.dd", // 2024.01.15
|
||||||
|
// "yyyyMMdd HHmmss", // 20240115 143000
|
||||||
|
// "yyyyMMdd", // 20240115
|
||||||
|
// "yyyy年MM月dd日", // 2024年01月15日
|
||||||
|
// "yyyy年MM月dd日HH时mm分ss秒" // 2024年01月15日14时30分00秒
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// for (String pattern : patterns) {
|
||||||
|
// try {
|
||||||
|
// SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
||||||
|
// sdf.setLenient(false); // 严格模式,不允许非法日期
|
||||||
|
// Date parsedDate = sdf.parse(dateStr);
|
||||||
|
//
|
||||||
|
// // 验证解析后的日期是否合理(例如年份不能是 0001)
|
||||||
|
// Calendar cal = Calendar.getInstance();
|
||||||
|
// cal.setTime(parsedDate);
|
||||||
|
// int year = cal.get(Calendar.YEAR);
|
||||||
|
// if (year >= 1900 && year <= 2100) {
|
||||||
|
// return parsedDate;
|
||||||
|
// }
|
||||||
|
// } catch (ParseException e) {
|
||||||
|
// // 尝试下一个格式
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// log.debug("无法解析日期: '{}'", dateStr);
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
private Integer parseInteger(String value) {
|
private Integer parseInteger(String value) {
|
||||||
if (!StringUtils.hasText(value)) {
|
if (!StringUtils.hasText(value)) {
|
||||||
return null;
|
return null;
|
||||||
@ -1247,9 +1426,40 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
result.setTempDir(zipContent.tempDir);
|
result.setTempDir(zipContent.tempDir);
|
||||||
result.setImageFiles(zipContent.images);
|
result.setImageFiles(zipContent.images);
|
||||||
result.setVideoFiles(zipContent.videos);
|
result.setVideoFiles(zipContent.videos);
|
||||||
try (Workbook workbook = new XSSFWorkbook(new FileInputStream(zipContent.excelFilePath))) {
|
Workbook workbook = null;
|
||||||
|
// 1. 验证文件是否存在且有效
|
||||||
|
File excelFile = new File(zipContent.excelFilePath);
|
||||||
|
if (!excelFile.exists() || excelFile.length() == 0) {
|
||||||
|
log.error("Excel文件不存在或为空: {}", zipContent.excelFilePath);
|
||||||
|
throw new RuntimeException("Excel文件不存在或已损坏");
|
||||||
|
}
|
||||||
|
|
||||||
|
try (InputStream fis = new FileInputStream(excelFile)) {
|
||||||
|
// 2. 使用 WorkbookFactory 自动识别 .xls 和 .xlsx 格式
|
||||||
|
// 这样可以避免因为格式不匹配导致的 NotOfficeXmlFileException
|
||||||
|
workbook = WorkbookFactory.create(fis);
|
||||||
|
|
||||||
Sheet sheet = workbook.getSheetAt(0);
|
Sheet sheet = workbook.getSheetAt(0);
|
||||||
result = parseSheet(sheet, result, uploadUserId);
|
result = parseSheet(sheet, result, uploadUserId);
|
||||||
|
|
||||||
|
} catch (EncryptedDocumentException e) {
|
||||||
|
log.error("Excel文件已加密,无法解析: {}", excelFile.getName(), e);
|
||||||
|
throw new RuntimeException("Excel文件已设置密码,请移除密码后重新上传");
|
||||||
|
} catch (InvalidFormatException e) {
|
||||||
|
log.error("Excel文件格式无效: {}", excelFile.getName(), e);
|
||||||
|
throw new RuntimeException("Excel文件格式无效或已损坏");
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("读取Excel文件IO异常: {}", excelFile.getName(), e);
|
||||||
|
throw new RuntimeException("读取Excel文件失败");
|
||||||
|
} finally {
|
||||||
|
// 3. 确保 Workbook 被正确关闭,释放资源
|
||||||
|
if (workbook != null) {
|
||||||
|
try {
|
||||||
|
workbook.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("关闭Workbook失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result.setExcelFileName(zipContent.excelFileName);
|
result.setExcelFileName(zipContent.excelFileName);
|
||||||
result.setExcelFilePath(zipContent.excelFilePath);
|
result.setExcelFilePath(zipContent.excelFilePath);
|
||||||
@ -1293,8 +1503,6 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processAttachmentsAsync(List<FishDraftData> dataList,
|
public void processAttachmentsAsync(List<FishDraftData> dataList,
|
||||||
Map<String, String> imageFiles,
|
|
||||||
Map<String, String> videoFiles,
|
|
||||||
String tempDir) {
|
String tempDir) {
|
||||||
if (dataList == null || dataList.isEmpty()) {
|
if (dataList == null || dataList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@ -1308,10 +1516,16 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
String vdpth = data.getVdpth();
|
String vdpth = data.getVdpth();
|
||||||
String picpth = data.getPicpth();
|
String picpth = data.getPicpth();
|
||||||
|
|
||||||
if(StrUtil.isBlank(vdpth)||StrUtil.isBlank(picpth)){
|
if (StrUtil.isBlank(vdpth) && StrUtil.isBlank(picpth)) {
|
||||||
log.error("数据不完整, 忽略处理");
|
log.error("数据不完整, 忽略处理");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
List<Map<String, String>> vdpthList = data.getVdpthList();
|
||||||
|
List<Map<String, String>> picpthList = data.getPicpthList();
|
||||||
|
|
||||||
|
Map<String, String> videoFiles = buildFileMap(vdpthList, tempDir, "videos");
|
||||||
|
Map<String, String> imageFiles = buildFileMap(picpthList, tempDir, "images");
|
||||||
|
|
||||||
if (StringUtils.hasText(vdpth) && videoFiles != null && !videoFiles.isEmpty()) {
|
if (StringUtils.hasText(vdpth) && videoFiles != null && !videoFiles.isEmpty()) {
|
||||||
String uploadedVdpth = uploadVideoFilesAsync(vdpth, videoFiles);
|
String uploadedVdpth = uploadVideoFilesAsync(vdpth, videoFiles);
|
||||||
if (uploadedVdpth != null) {
|
if (uploadedVdpth != null) {
|
||||||
@ -1325,27 +1539,28 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
data.setPicpth(uploadedPicpth);
|
data.setPicpth(uploadedPicpth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fishDraftDataService.updateById(data);
|
|
||||||
log.info("异步上传附件完成, dataId: {},{},{}", data.getId(), data.getPicpth(), data.getVdpth());
|
log.info("异步上传附件完成, dataId: {},{},{}", data.getId(), data.getPicpth(), data.getVdpth());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("异步上传附件失败, dataId: {}", data.getId(), e);
|
log.error("异步上传附件失败, dataId: {}", data.getId(), e);
|
||||||
} finally {
|
} finally {
|
||||||
SecurityContextHolder.clearContext();
|
SecurityContextHolder.clearContext();
|
||||||
}
|
}
|
||||||
});
|
}, attachmentExecutor);
|
||||||
futures.add(future);
|
futures.add(future);
|
||||||
}
|
}
|
||||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||||
.thenRun(() -> {
|
.thenRun(() -> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// del 方法会递归删除目录及其所有内容
|
fishDraftDataService.updateBatchById(dataList);
|
||||||
|
log.info("批量更新附件路径完成, 共{}条", dataList.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("批量更新附件路径失败", e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
FileUtil.del(tempDir);
|
FileUtil.del(tempDir);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("删除临时目录失败{}", e.getMessage());
|
log.error("删除临时目录失败{}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
.exceptionally(ex -> {
|
.exceptionally(ex -> {
|
||||||
log.error("等待异步任务完成时发生异常", ex);
|
log.error("等待异步任务完成时发生异常", ex);
|
||||||
@ -1353,6 +1568,27 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> buildFileMap(List<Map<String, String>> fileList, String tempDir, String subDir) {
|
||||||
|
if (fileList == null || fileList.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> fileMap = new HashMap<>();
|
||||||
|
String baseDir = tempDir + File.separator + subDir + File.separator;
|
||||||
|
|
||||||
|
for (Map<String, String> item : fileList) {
|
||||||
|
String name = item.get("name");
|
||||||
|
String value = item.get("value");
|
||||||
|
if (name != null && value != null) {
|
||||||
|
fileMap.put(name, baseDir + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileMap.isEmpty() ? null : fileMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private String uploadVideoFilesAsync(String videoNames, Map<String, String> videoMap) {
|
private String uploadVideoFilesAsync(String videoNames, Map<String, String> videoMap) {
|
||||||
String[] fileNames = videoNames.split(";");
|
String[] fileNames = videoNames.split(";");
|
||||||
List<String> attachmentIds = new ArrayList<>();
|
List<String> attachmentIds = new ArrayList<>();
|
||||||
@ -1508,4 +1744,17 @@ public class FishImportServiceImpl implements IFishImportService {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@jakarta.annotation.PreDestroy
|
||||||
|
public void shutdown() {
|
||||||
|
attachmentExecutor.shutdown();
|
||||||
|
try {
|
||||||
|
if (!attachmentExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
|
||||||
|
attachmentExecutor.shutdownNow();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
attachmentExecutor.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
package com.yfd.platform.data.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.yfd.platform.common.DataSourceLoadOptionsBase;
|
||||||
|
import com.yfd.platform.common.DataSourceRequest;
|
||||||
|
import com.yfd.platform.data.domain.vo.FishStatisticsVO;
|
||||||
|
import com.yfd.platform.data.mapper.FishStatisticsMapper;
|
||||||
|
import com.yfd.platform.data.service.IFishStatisticsService;
|
||||||
|
import com.yfd.platform.utils.KendoUtil;
|
||||||
|
import com.yfd.platform.utils.QgcQueryWrapperUtil;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class FishStatisticsServiceImpl implements IFishStatisticsService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FishStatisticsMapper fishStatisticsMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<FishStatisticsVO> queryPage(DataSourceRequest dataSourceRequest) {
|
||||||
|
DataSourceLoadOptionsBase loadOptions = dataSourceRequest.toDevRequest();
|
||||||
|
|
||||||
|
String basinNamesStr = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "basinCode");
|
||||||
|
String stationNamesStr = QgcQueryWrapperUtil.getFilterFieldValue(loadOptions, "stationCode");
|
||||||
|
|
||||||
|
// List<String> basinNames = parseFilterList(basinNamesStr);
|
||||||
|
// List<String> stationNames = parseFilterList(stationNamesStr);
|
||||||
|
|
||||||
|
int currentPage = dataSourceRequest.getSkip();
|
||||||
|
int pageSize = dataSourceRequest.getTake();
|
||||||
|
|
||||||
|
if (currentPage <= 0) {
|
||||||
|
currentPage = 1;
|
||||||
|
}
|
||||||
|
if (pageSize <= 0) {
|
||||||
|
pageSize = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startRow = (currentPage - 1) * pageSize;
|
||||||
|
int endRow = startRow + pageSize;
|
||||||
|
|
||||||
|
List<FishStatisticsVO> records = fishStatisticsMapper.queryStatistics(
|
||||||
|
basinNamesStr, stationNamesStr, startRow, endRow);
|
||||||
|
|
||||||
|
int total = fishStatisticsMapper.countStatistics(basinNamesStr, basinNamesStr);
|
||||||
|
|
||||||
|
Page<FishStatisticsVO> page = new Page<>();
|
||||||
|
page.setRecords(records);
|
||||||
|
page.setTotal(total);
|
||||||
|
page.setSize(pageSize);
|
||||||
|
page.setCurrent(currentPage);
|
||||||
|
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> parseFilterList(String filterValue) {
|
||||||
|
if (StrUtil.isBlank(filterValue)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return Arrays.asList(filterValue.split(","));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,35 +3,45 @@ package com.yfd.platform.data.service.impl;
|
|||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.yfd.platform.data.domain.FishDraftData;
|
||||||
import com.yfd.platform.data.domain.FishImportResult;
|
import com.yfd.platform.data.domain.FishImportResult;
|
||||||
import com.yfd.platform.data.domain.ImportTask;
|
import com.yfd.platform.data.domain.ImportTask;
|
||||||
|
import com.yfd.platform.data.domain.ImportTaskRow;
|
||||||
import com.yfd.platform.data.mapper.ImportTaskMapper;
|
import com.yfd.platform.data.mapper.ImportTaskMapper;
|
||||||
|
import com.yfd.platform.data.mapper.ImportTaskRowMapper;
|
||||||
import com.yfd.platform.data.service.AttachmentUploadService;
|
import com.yfd.platform.data.service.AttachmentUploadService;
|
||||||
import com.yfd.platform.data.service.IImportTaskService;
|
import com.yfd.platform.data.service.IImportTaskService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* 导入任务表 服务实现类
|
* 导入任务表 服务实现类
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class ImportTaskServiceImpl extends ServiceImpl<ImportTaskMapper, ImportTask> implements IImportTaskService {
|
public class ImportTaskServiceImpl extends ServiceImpl<ImportTaskMapper, ImportTask> implements IImportTaskService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ImportTaskMapper importTaskMapper;
|
private ImportTaskMapper importTaskMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ImportTaskRowMapper importTaskRowMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ObjectMapper objectMapper;
|
private ObjectMapper objectMapper;
|
||||||
@Resource
|
@Resource
|
||||||
@ -133,14 +143,10 @@ public class ImportTaskServiceImpl extends ServiceImpl<ImportTaskMapper, ImportT
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean markSuccess(String id) {
|
public boolean markSuccess(String id) {
|
||||||
ImportTask importTask = this.getById(id);
|
return this.update(new LambdaUpdateWrapper<ImportTask>()
|
||||||
if (importTask == null) {
|
.eq(ImportTask::getId, id)
|
||||||
return false;
|
.set(ImportTask::getStatus, "CONFIRMED")
|
||||||
}
|
.set(ImportTask::getUpdatedAt, new Date())); // 如果没有配置自动填充,建议保留此行
|
||||||
importTask.setStatus("CONFIRMED");
|
|
||||||
importTask.setUpdatedAt(new Date());
|
|
||||||
boolean b = this.updateById(importTask);
|
|
||||||
return b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -151,6 +157,9 @@ public class ImportTaskServiceImpl extends ServiceImpl<ImportTaskMapper, ImportT
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
List<String> ids = expiredTasks.stream().map(ImportTask::getId).toList();
|
List<String> ids = expiredTasks.stream().map(ImportTask::getId).toList();
|
||||||
|
for (String id : ids) {
|
||||||
|
importTaskRowMapper.deleteByTaskId(id);
|
||||||
|
}
|
||||||
return this.removeByIds(ids);
|
return this.removeByIds(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,34 +173,38 @@ public class ImportTaskServiceImpl extends ServiceImpl<ImportTaskMapper, ImportT
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean cancelTask(String taskId, String operatorId) {
|
public boolean cancelTask(String taskId, String operatorId) {
|
||||||
ImportTask importTask = this.getById(taskId);
|
ImportTask importTask = this.getOne(new LambdaQueryWrapper<ImportTask>().eq(ImportTask::getId, taskId).select(ImportTask::getId,ImportTask::getStatus, ImportTask::getTempDir));
|
||||||
if (importTask == null) {
|
if (importTask == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String currentStatus = importTask.getStatus();
|
String currentStatus = importTask.getStatus();
|
||||||
if ("CONFIRMED".equals(currentStatus) || "FAILED".equals(currentStatus) || "CANCELLED".equals(currentStatus)) {
|
// if ("CONFIRMED".equals(currentStatus) || "FAILED".equals(currentStatus) || "CANCELLED".equals(currentStatus)) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if ("CONFIRMED".equals(currentStatus) || "CANCELLED".equals(currentStatus)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String temp = importTask.getTempDir();
|
String temp = importTask.getTempDir();
|
||||||
if (StrUtil.isNotBlank( temp)) {
|
|
||||||
FileUtil.del(temp);
|
|
||||||
}else{
|
|
||||||
if (importTask.getResultJson() != null && !importTask.getResultJson().isEmpty()) {
|
|
||||||
try {
|
|
||||||
FishImportResult importResult = objectMapper.readValue(importTask.getResultJson(), FishImportResult.class);
|
|
||||||
String tempDir = importResult.getTempDir();
|
|
||||||
// del 方法会递归删除目录及其所有内容
|
|
||||||
FileUtil.del(tempDir);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
// ignore parse error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
importTask.setStatus("CANCELLED");
|
importTask.setStatus("CANCELLED");
|
||||||
importTask.setErrorMsg("用户取消: " + operatorId);
|
importTask.setErrorMsg("用户取消: " + operatorId);
|
||||||
importTask.setUpdatedAt(new Date());
|
importTask.setUpdatedAt(new Date());
|
||||||
return this.updateById(importTask);
|
importTaskRowMapper.deleteByTaskId(taskId);
|
||||||
|
boolean result = this.updateById(importTask);
|
||||||
|
|
||||||
|
if (result && StrUtil.isNotBlank(temp)) {
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
try {
|
||||||
|
FileUtil.del(temp);
|
||||||
|
log.info("异步删除临时目录成功: {}", temp);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("异步删除临时目录失败: {}", temp, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -206,14 +219,103 @@ public class ImportTaskServiceImpl extends ServiceImpl<ImportTaskMapper, ImportT
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean saveResultJson(String taskId, String resultJson) {
|
public boolean saveResultJson(String taskId, FishImportResult importResult) {
|
||||||
ImportTask importTask = this.getById(taskId);
|
ImportTask importTask = this.getById(taskId);
|
||||||
if (importTask == null) {
|
if (importTask == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
importTask.setResultJson(resultJson);
|
|
||||||
|
// importTask.setResultJson(resultJson);
|
||||||
importTask.setUpdatedAt(new Date());
|
importTask.setUpdatedAt(new Date());
|
||||||
return this.updateById(importTask);
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
importTask.setSummary(importResult.getSummary());
|
||||||
|
importTask.setCode(importResult.getCode());
|
||||||
|
importTask.setMessage(importResult.getMessage());
|
||||||
|
if (importResult.getUnrecognizedFields() != null && !importResult.getUnrecognizedFields().isEmpty()) {
|
||||||
|
importTask.setUnrecognizedFields(String.join(",", importResult.getUnrecognizedFields()));
|
||||||
|
}
|
||||||
|
// if (importResult.getImageFiles() != null && !importResult.getImageFiles().isEmpty()) {
|
||||||
|
// importTask.setImageFilesJson(objectMapper.writeValueAsString(importResult.getImageFiles()));
|
||||||
|
// }
|
||||||
|
// if (importResult.getVideoFiles() != null && !importResult.getVideoFiles().isEmpty()) {
|
||||||
|
// importTask.setVideoFilesJson(objectMapper.writeValueAsString(importResult.getVideoFiles()));
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.updateById(importTask);
|
||||||
|
|
||||||
|
importTaskRowMapper.deleteByTaskId(taskId);
|
||||||
|
|
||||||
|
if (importResult.getRows() != null && !importResult.getRows().isEmpty()) {
|
||||||
|
List<ImportTaskRow> rows = new ArrayList<>();
|
||||||
|
for (FishImportResult.FishImportRow row : importResult.getRows()) {
|
||||||
|
ImportTaskRow taskRow = new ImportTaskRow();
|
||||||
|
taskRow.setTaskId(taskId);
|
||||||
|
taskRow.setRowIndex(row.getRowIndex());
|
||||||
|
taskRow.setStatus(row.getStatus());
|
||||||
|
|
||||||
|
FishDraftData data = row.getData();
|
||||||
|
if (data != null) {
|
||||||
|
taskRow.setDataId(data.getId());
|
||||||
|
taskRow.setStcd(data.getStcd());
|
||||||
|
taskRow.setStnm(data.getStnm());
|
||||||
|
taskRow.setRstcd(data.getRstcd());
|
||||||
|
taskRow.setEnnm(data.getEnnm());
|
||||||
|
taskRow.setHbrvcd(data.getHbrvcd());
|
||||||
|
taskRow.setHbrvnm(data.getHbrvnm());
|
||||||
|
taskRow.setRvcd(data.getRvcd());
|
||||||
|
taskRow.setBaseId(data.getBaseId());
|
||||||
|
taskRow.setBaseName(data.getBaseName());
|
||||||
|
taskRow.setStrdt(data.getStrdt());
|
||||||
|
taskRow.setStrdtStr(data.getStrdtStr());
|
||||||
|
taskRow.setEnddt(data.getEnddt());
|
||||||
|
taskRow.setFtp(data.getFtp());
|
||||||
|
taskRow.setFtpName(data.getFtpName());
|
||||||
|
taskRow.setIsfs(data.getIsfs());
|
||||||
|
taskRow.setDirection(data.getDirection());
|
||||||
|
taskRow.setFcnt(data.getFcnt());
|
||||||
|
taskRow.setFsz(data.getFsz());
|
||||||
|
taskRow.setFwet(data.getFwet());
|
||||||
|
taskRow.setWt(data.getWt());
|
||||||
|
taskRow.setPicpth(data.getPicpth());
|
||||||
|
taskRow.setVdpth(data.getVdpth());
|
||||||
|
taskRow.setTm(data.getTm());
|
||||||
|
taskRow.setSourceType(data.getSourceType());
|
||||||
|
taskRow.setMouth(data.getMouth());
|
||||||
|
taskRow.setYr(data.getYr());
|
||||||
|
}
|
||||||
|
if (row.getWarnings() != null && !row.getWarnings().isEmpty()) {
|
||||||
|
taskRow.setWarnings(String.join(",", row.getWarnings()));
|
||||||
|
}
|
||||||
|
if (row.getUnrecognizedFields() != null && !row.getUnrecognizedFields().isEmpty()) {
|
||||||
|
taskRow.setUnrecognizedFields(String.join(",", row.getUnrecognizedFields()));
|
||||||
|
}
|
||||||
|
if (row.getVdpthList() != null && !row.getVdpthList().isEmpty()) {
|
||||||
|
taskRow.setVdpthListJson(objectMapper.writeValueAsString(row.getVdpthList()));
|
||||||
|
}
|
||||||
|
if (row.getPicpthList() != null && !row.getPicpthList().isEmpty()) {
|
||||||
|
taskRow.setPicpthListJson(objectMapper.writeValueAsString(row.getPicpthList()));
|
||||||
|
}
|
||||||
|
if (row.getVdpthsWarnings() != null && !row.getVdpthsWarnings().isEmpty()) {
|
||||||
|
taskRow.setVdpthWarnings(String.join(",", row.getVdpthsWarnings()));
|
||||||
|
}
|
||||||
|
if (row.getPicpthsWarnings() != null && !row.getPicpthsWarnings().isEmpty()) {
|
||||||
|
taskRow.setPicpthWarnings(String.join(",", row.getPicpthsWarnings()));
|
||||||
|
}
|
||||||
|
taskRow.setCreatedAt(new Date());
|
||||||
|
rows.add(taskRow);
|
||||||
|
}
|
||||||
|
importTaskRowMapper.insert(rows);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("保存导入结果行数据失败, taskId: {}", taskId, e);
|
||||||
|
importTask.setErrorMsg("保存行数据失败: " + e.getMessage());
|
||||||
|
this.updateById(importTask);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -221,4 +323,140 @@ public class ImportTaskServiceImpl extends ServiceImpl<ImportTaskMapper, ImportT
|
|||||||
ImportTask importTask = importTaskMapper.selectLastByUserId(uploadUserId);
|
ImportTask importTask = importTaskMapper.selectLastByUserId(uploadUserId);
|
||||||
return importTask;
|
return importTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ImportTaskRow> getRowsByTaskId(String taskId) {
|
||||||
|
return importTaskRowMapper.selectByTaskId(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ImportTaskRow> getRowsByTaskIdAndStatus(String taskId, String status) {
|
||||||
|
return importTaskRowMapper.selectByTaskIdAndStatus(taskId, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FishImportResult buildImportResult(String taskId) {
|
||||||
|
ImportTask importTask = this.getById(taskId);
|
||||||
|
if (importTask == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FishImportResult result = new FishImportResult();
|
||||||
|
result.setTaskId(taskId);
|
||||||
|
result.setTempDir(importTask.getTempDir());
|
||||||
|
result.setSummary(importTask.getSummary());
|
||||||
|
result.setCode(importTask.getCode());
|
||||||
|
result.setMessage(importTask.getMessage());
|
||||||
|
result.setTotalCount(importTask.getTotalCount() != null ? importTask.getTotalCount() : 0);
|
||||||
|
result.setSuccessCount(importTask.getSuccessCount() != null ? importTask.getSuccessCount() : 0);
|
||||||
|
result.setFailedCount(importTask.getFailCount() != null ? importTask.getFailCount() : 0);
|
||||||
|
|
||||||
|
if (importTask.getUnrecognizedFields() != null && !importTask.getUnrecognizedFields().isEmpty()) {
|
||||||
|
result.setUnrecognizedFields(Arrays.asList(importTask.getUnrecognizedFields().split(",")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List<ImportTaskRow> rows = importTaskRowMapper.selectByTaskId(taskId);
|
||||||
|
if (rows != null && !rows.isEmpty()) {
|
||||||
|
for (ImportTaskRow row : rows) {
|
||||||
|
FishImportResult.FishImportRow importRow = new FishImportResult.FishImportRow(row.getRowIndex());
|
||||||
|
importRow.setStatus(row.getStatus());
|
||||||
|
|
||||||
|
FishDraftData data = new FishDraftData();
|
||||||
|
data.setId(row.getDataId());
|
||||||
|
data.setStcd(row.getStcd());
|
||||||
|
data.setStnm(row.getStnm());
|
||||||
|
data.setRstcd(row.getRstcd());
|
||||||
|
data.setEnnm(row.getEnnm());
|
||||||
|
data.setHbrvcd(row.getHbrvcd());
|
||||||
|
data.setHbrvnm(row.getHbrvnm());
|
||||||
|
data.setRvcd(row.getRvcd());
|
||||||
|
data.setBaseId(row.getBaseId());
|
||||||
|
data.setBaseName(row.getBaseName());
|
||||||
|
data.setStrdt(row.getStrdt());
|
||||||
|
data.setStrdtStr(row.getStrdtStr());
|
||||||
|
data.setEnddt(row.getEnddt());
|
||||||
|
data.setFtp(row.getFtp());
|
||||||
|
data.setFtpName(row.getFtpName());
|
||||||
|
data.setIsfs(row.getIsfs());
|
||||||
|
data.setDirection(row.getDirection());
|
||||||
|
data.setFcnt(row.getFcnt());
|
||||||
|
data.setFsz(row.getFsz());
|
||||||
|
data.setFwet(row.getFwet());
|
||||||
|
data.setWt(row.getWt());
|
||||||
|
data.setPicpth(row.getPicpth());
|
||||||
|
data.setVdpth(row.getVdpth());
|
||||||
|
data.setTm(row.getTm());
|
||||||
|
data.setSourceType(row.getSourceType());
|
||||||
|
data.setMouth(row.getMouth());
|
||||||
|
data.setYr(row.getYr());
|
||||||
|
importRow.setData(data);
|
||||||
|
|
||||||
|
if (row.getWarnings() != null && !row.getWarnings().isEmpty()) {
|
||||||
|
importRow.setWarnings(Arrays.asList(row.getWarnings().split(",")));
|
||||||
|
}
|
||||||
|
if (row.getUnrecognizedFields() != null && !row.getUnrecognizedFields().isEmpty()) {
|
||||||
|
importRow.setUnrecognizedFields(Arrays.asList(row.getUnrecognizedFields().split(",")));
|
||||||
|
}
|
||||||
|
if (row.getVdpthListJson() != null && !row.getVdpthListJson().isEmpty()) {
|
||||||
|
try {
|
||||||
|
List<Map<String, String>> vdpthList = objectMapper.readValue(
|
||||||
|
row.getVdpthListJson(), new TypeReference<>() {
|
||||||
|
});
|
||||||
|
importRow.setVdpthList(vdpthList);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("解析视频列表JSON失败, rowId: {}", row.getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (row.getPicpthListJson() != null && !row.getPicpthListJson().isEmpty()) {
|
||||||
|
try {
|
||||||
|
List<Map<String, String>> picpthList = objectMapper.readValue(
|
||||||
|
row.getPicpthListJson(), new TypeReference<List<Map<String, String>>>() {});
|
||||||
|
importRow.setPicpthList(picpthList);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("解析图片列表JSON失败, rowId: {}", row.getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (row.getVdpthWarnings() != null && !row.getVdpthWarnings().isEmpty()) {
|
||||||
|
importRow.setVdpthsWarnings(Arrays.asList(row.getVdpthWarnings().split(",")));
|
||||||
|
}
|
||||||
|
if (row.getPicpthWarnings() != null && !row.getPicpthWarnings().isEmpty()) {
|
||||||
|
importRow.setPicpthsWarnings(Arrays.asList(row.getPicpthWarnings().split(",")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("SUCCESS".equals(row.getStatus())) {
|
||||||
|
result.addSuccessRow(importRow);
|
||||||
|
} else {
|
||||||
|
result.addFailedRow(importRow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> imageFiles = new LinkedHashMap<>();
|
||||||
|
Map<String, String> videoFiles = new LinkedHashMap<>();
|
||||||
|
for (FishImportResult.FishImportRow row : result.getRows()) {
|
||||||
|
if (row.getPicpthList() != null) {
|
||||||
|
for (Map<String, String> pic : row.getPicpthList()) {
|
||||||
|
String name = pic.get("name");
|
||||||
|
String path = pic.get("path");
|
||||||
|
if (name != null && path != null) {
|
||||||
|
imageFiles.putIfAbsent(name, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (row.getVdpthList() != null) {
|
||||||
|
for (Map<String, String> vid : row.getVdpthList()) {
|
||||||
|
String name = vid.get("name");
|
||||||
|
String path = vid.get("path");
|
||||||
|
if (name != null && path != null) {
|
||||||
|
videoFiles.putIfAbsent(name, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.setImageFiles(imageFiles);
|
||||||
|
result.setVideoFiles(videoFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
package com.yfd.platform.data.utils;
|
package com.yfd.platform.data.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -364,22 +366,99 @@ public class ZipFileUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String saveFileToDir(InputStream is, File tempDir, String subFolder, String fileName) throws IOException {
|
private static String saveFileToDir(InputStream is, File tempDir, String subFolder, String fileName) throws IOException {
|
||||||
|
// 使用 Hutool 构建文件路径
|
||||||
|
String safeFileName = sanitizeFileName(fileName);
|
||||||
File folder = new File(tempDir, subFolder);
|
File folder = new File(tempDir, subFolder);
|
||||||
if (!folder.exists()) {
|
|
||||||
folder.mkdirs();
|
// 使用 Hutool 创建目录(会自动创建父目录)
|
||||||
|
FileUtil.mkdir(folder);
|
||||||
|
|
||||||
|
log.info("保存文件: fileName{} -> safeFileName{}->subFolder{}", fileName, safeFileName,subFolder);
|
||||||
|
// 构建完整文件路径
|
||||||
|
File file = new File(folder, safeFileName);
|
||||||
|
|
||||||
|
// 安全检查:防止目录穿越
|
||||||
|
String canonicalPath = file.getCanonicalPath();
|
||||||
|
String canonicalDir = folder.getCanonicalPath();
|
||||||
|
if (!canonicalPath.startsWith(canonicalDir)) {
|
||||||
|
throw new IOException("非法的文件路径: " + fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
File file = new File(folder, fileName);
|
// 使用 Hutool 从流复制到文件
|
||||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
try {
|
||||||
byte[] buffer = new byte[4096];
|
FileUtil.writeFromStream(is, file);
|
||||||
int len;
|
log.debug("保存文件: {} -> {}, 大小: {} bytes", fileName, safeFileName, file.length());
|
||||||
while ((len = is.read(buffer)) > 0) {
|
} catch (Exception e) {
|
||||||
fos.write(buffer, 0, len);
|
throw new IOException("保存文件失败: " + fileName, e);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return file.getAbsolutePath();
|
return file.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理和标准化文件名,确保在 Linux 上正确显示中文
|
||||||
|
*/
|
||||||
|
private static String sanitizeFileName(String fileName) {
|
||||||
|
if (StrUtil.isBlank(fileName)) {
|
||||||
|
return "unnamed_" + System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 Hutool 去除首尾空格
|
||||||
|
String sanitized = StrUtil.trim(fileName);
|
||||||
|
|
||||||
|
// 替换非法字符(Linux/Windows 都不允许的字符)
|
||||||
|
sanitized = sanitized.replaceAll("[\\\\/:*?\"<>|]", "_");
|
||||||
|
|
||||||
|
// 去除连续的下划线
|
||||||
|
sanitized = sanitized.replaceAll("_+", "_");
|
||||||
|
|
||||||
|
// 如果文件名过长,截断(Linux 文件名最大 255 字节)
|
||||||
|
byte[] bytes = sanitized.getBytes(StandardCharsets.UTF_8);
|
||||||
|
if (bytes.length > 200) {
|
||||||
|
String extension = "";
|
||||||
|
int dotIndex = sanitized.lastIndexOf('.');
|
||||||
|
if (dotIndex > 0 && dotIndex < sanitized.length() - 1) {
|
||||||
|
extension = sanitized.substring(dotIndex);
|
||||||
|
sanitized = sanitized.substring(0, dotIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新计算长度
|
||||||
|
bytes = sanitized.getBytes(StandardCharsets.UTF_8);
|
||||||
|
int maxNameLength = 200 - extension.getBytes(StandardCharsets.UTF_8).length;
|
||||||
|
|
||||||
|
if (bytes.length > maxNameLength) {
|
||||||
|
// 安全截断,避免切断多字节字符
|
||||||
|
sanitized = StrUtil.subPre(sanitized, maxNameLength);
|
||||||
|
}
|
||||||
|
sanitized = sanitized + extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保文件名不为空
|
||||||
|
if (StrUtil.isBlank(sanitized) || ".".equals(sanitized)) {
|
||||||
|
return "file_" + System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sanitized;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private static String saveFileToDir(InputStream is, File tempDir, String subFolder, String fileName) throws IOException {
|
||||||
|
// File folder = new File(tempDir, subFolder);
|
||||||
|
// if (!folder.exists()) {
|
||||||
|
// folder.mkdirs();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// File file = new File(folder, fileName);
|
||||||
|
// try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||||
|
// byte[] buffer = new byte[4096];
|
||||||
|
// int len;
|
||||||
|
// while ((len = is.read(buffer)) > 0) {
|
||||||
|
// fos.write(buffer, 0, len);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return file.getAbsolutePath();
|
||||||
|
// }
|
||||||
|
|
||||||
private static boolean isImageFile(String fileName) {
|
private static boolean isImageFile(String fileName) {
|
||||||
return fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") ||
|
return fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") ||
|
||||||
fileName.endsWith(".png") || fileName.endsWith(".gif") ||
|
fileName.endsWith(".png") || fileName.endsWith(".gif") ||
|
||||||
|
|||||||
11
backend/src/main/java/com/yfd/platform/env/domain/StationBasinInfo.java
vendored
Normal file
11
backend/src/main/java/com/yfd/platform/env/domain/StationBasinInfo.java
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package com.yfd.platform.env.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class StationBasinInfo {
|
||||||
|
private String stcd; // 电站编码
|
||||||
|
private String ennm; // 电站名称
|
||||||
|
private String hbrvcd; // 流域编码
|
||||||
|
private String hbrvnm; // 流域名称
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ package com.yfd.platform.env.mapper;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.yfd.platform.env.domain.SdEngInfoBH;
|
import com.yfd.platform.env.domain.SdEngInfoBH;
|
||||||
|
import com.yfd.platform.env.domain.StationBasinInfo;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -37,4 +38,7 @@ public interface SdEngInfoBHMapper extends BaseMapper<SdEngInfoBH> {
|
|||||||
* 根据基地流域编码列表批量查询电站
|
* 根据基地流域编码列表批量查询电站
|
||||||
*/
|
*/
|
||||||
List<SdEngInfoBH> selectByHbrvcdList(@Param("hbrvcdList") List<String> hbrvcdList);
|
List<SdEngInfoBH> selectByHbrvcdList(@Param("hbrvcdList") List<String> hbrvcdList);
|
||||||
|
|
||||||
|
|
||||||
|
List<StationBasinInfo> selectStationBasinInfo();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,16 +83,19 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl<SdEngInfoBHMapper, SdEng
|
|||||||
.select(SdEngInfoBH::getStcd, SdEngInfoBH::getEnnm, SdEngInfoBH::getBaseId)
|
.select(SdEngInfoBH::getStcd, SdEngInfoBH::getEnnm, SdEngInfoBH::getBaseId)
|
||||||
.orderByAsc(SdEngInfoBH::getOrderIndex);
|
.orderByAsc(SdEngInfoBH::getOrderIndex);
|
||||||
|
|
||||||
|
if("admin".equals(SecurityUtils.getCurrentUsername())){
|
||||||
|
return this.list(wrapper);
|
||||||
|
}
|
||||||
Set<String> authorizedStations = getUserAuthorizedStationCodes();
|
Set<String> authorizedStations = getUserAuthorizedStationCodes();
|
||||||
if (authorizedStations != null && !authorizedStations.isEmpty()) {
|
if (authorizedStations != null && !authorizedStations.isEmpty()) {
|
||||||
wrapper.in(SdEngInfoBH::getStcd, authorizedStations);
|
List<SdEngInfoBH> list = this.list(wrapper);
|
||||||
} else if (!"admin".equals(SecurityUtils.getCurrentUsername())){
|
return list.stream()
|
||||||
|
.filter(item -> authorizedStations.contains(item.getStcd()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}else{
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.list(wrapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getUserAuthorizedStationCodes() {
|
public Set<String> getUserAuthorizedStationCodes() {
|
||||||
String userId = SecurityUtils.getUserId();
|
String userId = SecurityUtils.getUserId();
|
||||||
|
|||||||
@ -67,28 +67,42 @@ public class SdFpssBHServiceImpl extends ServiceImpl<SdFpssBHMapper, SdFpssBH> i
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SdFpssBH> selectForDropdown(String rstcd, String stnm, String baseId) {
|
public List<SdFpssBH> selectForDropdown(String rstcd, String stnm, String baseId) {
|
||||||
|
// 管理员直接查询,无需权限过滤
|
||||||
|
if ("admin".equals(SecurityUtils.getCurrentUsername())) {
|
||||||
|
return queryFpssList(rstcd, stnm, baseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户有权限的工程编码
|
||||||
Set<String> authorizedStations = getUserAuthorizedStationCodes();
|
Set<String> authorizedStations = getUserAuthorizedStationCodes();
|
||||||
|
|
||||||
List<SdFpssBH> result;
|
// 无权限直接返回空列表
|
||||||
|
if (authorizedStations == null || authorizedStations.isEmpty()) {
|
||||||
if (StringUtils.hasText(baseId)) {
|
return new ArrayList<>();
|
||||||
result = baseMapper.selectForDropdownWithBaseId(rstcd, stnm, baseId);
|
|
||||||
} else {
|
|
||||||
LambdaQueryWrapper<SdFpssBH> wrapper = new LambdaQueryWrapper<>();
|
|
||||||
wrapper.eq(StringUtils.hasText(rstcd), SdFpssBH::getRstcd, rstcd)
|
|
||||||
.like(StringUtils.hasText(stnm), SdFpssBH::getStnm, stnm)
|
|
||||||
.orderByDesc(SdFpssBH::getOrderIndex);
|
|
||||||
result = list(wrapper);
|
|
||||||
}
|
|
||||||
if (authorizedStations.isEmpty()&&"admin".equals(SecurityUtils.getCurrentUsername())) {
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询数据
|
||||||
|
List<SdFpssBH> result = queryFpssList(rstcd, stnm, baseId);
|
||||||
|
|
||||||
|
// 权限过滤
|
||||||
return result.stream()
|
return result.stream()
|
||||||
.filter(fpss -> authorizedStations.contains(fpss.getRstcd()))
|
.filter(fpss -> fpss.getRstcd() != null && authorizedStations.contains(fpss.getRstcd()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询过鱼设施列表(公共方法)
|
||||||
|
*/
|
||||||
|
private List<SdFpssBH> queryFpssList(String rstcd, String stnm, String baseId) {
|
||||||
|
if (StringUtils.hasText(baseId)) {
|
||||||
|
return baseMapper.selectForDropdownWithBaseId(rstcd, stnm, baseId);
|
||||||
|
} else {
|
||||||
|
LambdaQueryWrapper<SdFpssBH> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(StringUtils.hasText(rstcd), SdFpssBH::getRstcd, rstcd)
|
||||||
|
.like(StringUtils.hasText(stnm), SdFpssBH::getStnm, stnm)
|
||||||
|
.orderByDesc(SdFpssBH::getOrderIndex);
|
||||||
|
return list(wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getUserAuthorizedStationCodes() {
|
public Set<String> getUserAuthorizedStationCodes() {
|
||||||
String userId = SecurityUtils.getUserId();
|
String userId = SecurityUtils.getUserId();
|
||||||
|
|||||||
@ -92,8 +92,16 @@ public class SdHbrvDicServiceImpl extends ServiceImpl<SdHbrvDicMapper, SdHbrvDic
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SdHbrvDic> selectForDropdown(String hbrvnm, String baseid) {
|
public List<SdHbrvDic> selectForDropdown(String hbrvnm, String baseid) {
|
||||||
Set<String> authorizedStations = getUserAuthorizedStationCodes();
|
|
||||||
LambdaQueryWrapper<SdHbrvDic> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<SdHbrvDic> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.like(hbrvnm != null && !hbrvnm.isEmpty(), SdHbrvDic::getHbrvnm, hbrvnm)
|
||||||
|
.eq(baseid != null && !baseid.isEmpty(), SdHbrvDic::getBaseid, baseid)
|
||||||
|
.eq(SdHbrvDic::getEnabled, 1)
|
||||||
|
.orderByAsc(SdHbrvDic::getOrderIndex);
|
||||||
|
if("admin".equals(SecurityUtils.getCurrentUsername())){
|
||||||
|
return this.list(wrapper);
|
||||||
|
}
|
||||||
|
Set<String> authorizedStations = getUserAuthorizedStationCodes();
|
||||||
if (authorizedStations != null && !authorizedStations.isEmpty()) {
|
if (authorizedStations != null && !authorizedStations.isEmpty()) {
|
||||||
List<SdEngInfoBH> engInfos = engInfoBHMapper.selectList(
|
List<SdEngInfoBH> engInfos = engInfoBHMapper.selectList(
|
||||||
new LambdaQueryWrapper<SdEngInfoBH>()
|
new LambdaQueryWrapper<SdEngInfoBH>()
|
||||||
@ -104,21 +112,19 @@ public class SdHbrvDicServiceImpl extends ServiceImpl<SdHbrvDicMapper, SdHbrvDic
|
|||||||
.map(SdEngInfoBH::getHbrvcd)
|
.map(SdEngInfoBH::getHbrvcd)
|
||||||
.filter(id -> id != null && !id.isEmpty())
|
.filter(id -> id != null && !id.isEmpty())
|
||||||
.distinct()
|
.distinct()
|
||||||
.collect(Collectors.toList());
|
.toList();
|
||||||
|
|
||||||
if (!hbrvcds.isEmpty()) {
|
if (!hbrvcds.isEmpty()) {
|
||||||
wrapper.in(SdHbrvDic::getHbrvcd, hbrvcds);
|
List<SdHbrvDic> list = this.list(wrapper);
|
||||||
|
return list.stream()
|
||||||
|
.filter(hbrvDic -> hbrvcds.contains(hbrvDic.getHbrvcd()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
} else {
|
} else {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
}else if (!"admin".equals(SecurityUtils.getCurrentUsername())){
|
}else {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
wrapper.like(hbrvnm != null && !hbrvnm.isEmpty(), SdHbrvDic::getHbrvnm, hbrvnm)
|
|
||||||
.eq(baseid != null && !baseid.isEmpty(), SdHbrvDic::getBaseid, baseid)
|
|
||||||
.eq(SdHbrvDic::getEnabled, 1)
|
|
||||||
.orderByAsc(SdHbrvDic::getOrderIndex);
|
|
||||||
return this.list(wrapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -41,6 +42,7 @@ import java.util.stream.Collectors;
|
|||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/sms")
|
@RequestMapping("/sms")
|
||||||
@Tag(name = "短信验证码管理")
|
@Tag(name = "短信验证码管理")
|
||||||
|
@Slf4j
|
||||||
public class SmsVerifyCodeController {
|
public class SmsVerifyCodeController {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@ -69,6 +71,7 @@ public class SmsVerifyCodeController {
|
|||||||
|
|
||||||
@Value("${rsa.private_key}")
|
@Value("${rsa.private_key}")
|
||||||
private String privateKey;
|
private String privateKey;
|
||||||
|
private static final String URGE_CONTENT ="根据生态环境部要求,请贵电站尽快完成过鱼数据的报送工作,感谢支持。";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送验证码
|
* 发送验证码
|
||||||
@ -124,6 +127,53 @@ public class SmsVerifyCodeController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量发送短信
|
||||||
|
*/
|
||||||
|
@PostMapping("/batchSendContent")
|
||||||
|
@Operation(summary = "批量发送短信")
|
||||||
|
public ResponseResult batchSendContent(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) {
|
||||||
|
|
||||||
|
List<String> phoneList = smsVerifyCodeRequest.getPhoneList();
|
||||||
|
if(phoneList==null){
|
||||||
|
return ResponseResult.error("手机号不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
smsVerifyCodeService.batchSendContent(phoneList, smsVerifyCodeRequest.getContent());
|
||||||
|
return ResponseResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量催促
|
||||||
|
*/
|
||||||
|
@PostMapping("/batchUrgeContent")
|
||||||
|
@Operation(summary = "催促")
|
||||||
|
public ResponseResult batchUrgeContent(@RequestBody SmsVerifyCodeRequest smsVerifyCodeRequest) {
|
||||||
|
List<String> userIds = smsVerifyCodeRequest.getUserIds();
|
||||||
|
String content = smsVerifyCodeRequest.getContent();
|
||||||
|
|
||||||
|
if ((userIds == null || userIds.isEmpty())) {
|
||||||
|
return ResponseResult.error("用户ID不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content == null || content.isEmpty()) {
|
||||||
|
content = URGE_CONTENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int successCount = smsVerifyCodeService.urgeByUserIds(userIds, content);
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
// result.put("successCount", successCount);
|
||||||
|
result.put("totalCount", userIds.size());
|
||||||
|
return ResponseResult.successData(result);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("批量催促短信发送失败", e);
|
||||||
|
return ResponseResult.error("短信发送失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册用户
|
* 注册用户
|
||||||
*/
|
*/
|
||||||
@ -204,7 +254,7 @@ public class SmsVerifyCodeController {
|
|||||||
selectedBasinCodes.addAll(Arrays.asList(hbrvcdCode.split(",")));
|
selectedBasinCodes.addAll(Arrays.asList(hbrvcdCode.split(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> addedStationCodes = new HashSet<>();
|
// Set<String> addedStationCodes = new HashSet<>();
|
||||||
|
|
||||||
for (String basinCode : selectedBasinCodes) {
|
for (String basinCode : selectedBasinCodes) {
|
||||||
if (StringUtils.isEmpty(basinCode)) {
|
if (StringUtils.isEmpty(basinCode)) {
|
||||||
@ -241,7 +291,7 @@ public class SmsVerifyCodeController {
|
|||||||
scope.setStatus(1);
|
scope.setStatus(1);
|
||||||
scope.setPermissionType("READ");
|
scope.setPermissionType("READ");
|
||||||
sysUserDataScopeService.addDataScope(scope);
|
sysUserDataScopeService.addDataScope(scope);
|
||||||
addedStationCodes.add(basinCode);
|
// addedStationCodes.add(basinCode);
|
||||||
} else {
|
} else {
|
||||||
Set<String> stationsInBasinAndSelected = allStationCodesInBasin.stream()
|
Set<String> stationsInBasinAndSelected = allStationCodesInBasin.stream()
|
||||||
.filter(selectedStationCodes::contains)
|
.filter(selectedStationCodes::contains)
|
||||||
@ -255,27 +305,27 @@ public class SmsVerifyCodeController {
|
|||||||
scope.setStatus(1);
|
scope.setStatus(1);
|
||||||
scope.setPermissionType("READ");
|
scope.setPermissionType("READ");
|
||||||
sysUserDataScopeService.addDataScope(scope);
|
sysUserDataScopeService.addDataScope(scope);
|
||||||
addedStationCodes.add(stationCd);
|
// addedStationCodes.add(stationCd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> standaloneStations = selectedStationCodes.stream()
|
// Set<String> standaloneStations = selectedStationCodes.stream()
|
||||||
.filter(code -> !addedStationCodes.contains(code))
|
// .filter(code -> !addedStationCodes.contains(code))
|
||||||
.collect(Collectors.toSet());
|
// .collect(Collectors.toSet());
|
||||||
|
//
|
||||||
for (String stationCd : standaloneStations) {
|
// for (String stationCd : standaloneStations) {
|
||||||
if (StringUtils.isEmpty(stationCd)) {
|
// if (StringUtils.isEmpty(stationCd)) {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
SysUserDataScope scope = new SysUserDataScope();
|
// SysUserDataScope scope = new SysUserDataScope();
|
||||||
scope.setUserId(userId);
|
// scope.setUserId(userId);
|
||||||
scope.setOrgType("STATION");
|
// scope.setOrgType("STATION");
|
||||||
scope.setOrgId(stationCd);
|
// scope.setOrgId(stationCd);
|
||||||
scope.setStatus(1);
|
// scope.setStatus(1);
|
||||||
scope.setPermissionType("READ");
|
// scope.setPermissionType("READ");
|
||||||
sysUserDataScopeService.addDataScope(scope);
|
// sysUserDataScopeService.addDataScope(scope);
|
||||||
}
|
// }
|
||||||
SysUser user = new SysUser();
|
SysUser user = new SysUser();
|
||||||
user.setId(userId);
|
user.setId(userId);
|
||||||
userService.updateUserRoles( user,"c13481a486c9ee559cf305284df4d207");
|
userService.updateUserRoles( user,"c13481a486c9ee559cf305284df4d207");
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package com.yfd.platform.system.domain;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class SmsVerifyCodeRequest {
|
public class SmsVerifyCodeRequest {
|
||||||
|
|
||||||
@ -61,5 +63,14 @@ public class SmsVerifyCodeRequest {
|
|||||||
*/
|
*/
|
||||||
private String stationCode;
|
private String stationCode;
|
||||||
|
|
||||||
|
private List<String> phoneList;
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户编号
|
||||||
|
*/
|
||||||
|
private List<String> userIds;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -163,4 +163,10 @@ public class SysUser implements Serializable {
|
|||||||
|
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
List<SysRole> roles;
|
List<SysRole> roles;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String basinNames;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String stationNames;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,6 +63,12 @@ public interface SysRoleMapper extends BaseMapper<SysRole> {
|
|||||||
***********************************/
|
***********************************/
|
||||||
List<SysRole> getRoleByUserId(String id);
|
List<SysRole> getRoleByUserId(String id);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量获取用户角色(含userId映射)
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> getUserRolesByUserIds(@Param("userIds") List<String> userIds);
|
||||||
|
|
||||||
/**********************************
|
/**********************************
|
||||||
* 用途说明: 根据角色ID删除菜单与角色关联信息
|
* 用途说明: 根据角色ID删除菜单与角色关联信息
|
||||||
* 参数说明 id 角色id
|
* 参数说明 id 角色id
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package com.yfd.platform.system.service;
|
|||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.yfd.platform.system.domain.SmsVerifyCode;
|
import com.yfd.platform.system.domain.SmsVerifyCode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* 短信验证码表 服务类
|
* 短信验证码表 服务类
|
||||||
@ -46,4 +48,7 @@ public interface ISmsVerifyCodeService extends IService<SmsVerifyCode> {
|
|||||||
* @return 是否发送成功
|
* @return 是否发送成功
|
||||||
*/
|
*/
|
||||||
boolean sendAuditNotify(String phone, String auditStatus, String reason);
|
boolean sendAuditNotify(String phone, String auditStatus, String reason);
|
||||||
|
|
||||||
|
void batchSendContent(List<String> phoneList, String content);
|
||||||
|
int urgeByUserIds(List<String> userIds, String content);
|
||||||
}
|
}
|
||||||
@ -1,17 +1,24 @@
|
|||||||
package com.yfd.platform.system.service.impl;
|
package com.yfd.platform.system.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.yfd.platform.system.domain.SmsVerifyCode;
|
import com.yfd.platform.system.domain.SmsVerifyCode;
|
||||||
|
import com.yfd.platform.system.domain.SysUser;
|
||||||
import com.yfd.platform.system.mapper.SmsVerifyCodeMapper;
|
import com.yfd.platform.system.mapper.SmsVerifyCodeMapper;
|
||||||
|
import com.yfd.platform.system.mapper.SysUserMapper;
|
||||||
import com.yfd.platform.system.service.ISmsVerifyCodeService;
|
import com.yfd.platform.system.service.ISmsVerifyCodeService;
|
||||||
import com.yfd.platform.utils.SmsSender;
|
import com.yfd.platform.utils.SmsSender;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -19,14 +26,19 @@ import java.util.Random;
|
|||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
public class SmsVerifyCodeServiceImpl extends ServiceImpl<SmsVerifyCodeMapper, SmsVerifyCode> implements ISmsVerifyCodeService {
|
public class SmsVerifyCodeServiceImpl extends ServiceImpl<SmsVerifyCodeMapper, SmsVerifyCode> implements ISmsVerifyCodeService {
|
||||||
|
|
||||||
private static final int CODE_VALID_MINUTES = 5;
|
private static final int CODE_VALID_MINUTES = 5;
|
||||||
private static final Random RANDOM = new Random();
|
private static final Random RANDOM = new Random();
|
||||||
|
private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private SmsSender smsSender;
|
private SmsSender smsSender;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SysUserMapper sysUserMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public String sendVerifyCode(String phone, Integer type) {
|
public String sendVerifyCode(String phone, Integer type) {
|
||||||
@ -120,4 +132,69 @@ public class SmsVerifyCodeServiceImpl extends ServiceImpl<SmsVerifyCodeMapper, S
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void batchSendContent(List<String> phoneList, String content) {
|
||||||
|
// for (String phone : phoneList) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
String phone = StrUtil.join(",", phoneList);
|
||||||
|
try {
|
||||||
|
smsSender.send(phone, content);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("批量发送短信失败"+phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int urgeByUserIds(List<String> userIds, String content) {
|
||||||
|
if (userIds == null || userIds.isEmpty()) {
|
||||||
|
log.warn("催促短信发送失败:用户ID列表为空");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
List<String> ids = userIds.stream().distinct().toList();
|
||||||
|
List<SysUser> sysUsers = sysUserMapper.selectList(new LambdaQueryWrapper<SysUser>().in(SysUser::getId, ids)
|
||||||
|
.eq(SysUser::getRegStatus, "APPROVED").eq(SysUser::getStatus,1).select(SysUser::getPhone));
|
||||||
|
|
||||||
|
if (sysUsers.isEmpty()) {
|
||||||
|
log.warn("催促短信发送失败:未找到有效的手机号,userIds: {}", ids);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
List<String> validPhones = sysUsers.stream().map(SysUser::getPhone).filter(this::isValidPhone).toList();
|
||||||
|
|
||||||
|
if (validPhones.isEmpty()) {
|
||||||
|
log.warn("催促短信发送失败:没有合规的手机号");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int successCount = 0;
|
||||||
|
// for (String phone : validPhones) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
String phone = StrUtil.join(",", validPhones);
|
||||||
|
try {
|
||||||
|
boolean sent = smsSender.send(phone, content);
|
||||||
|
if (sent) {
|
||||||
|
successCount++;
|
||||||
|
log.info("催促短信发送成功: {}", phone);
|
||||||
|
} else {
|
||||||
|
log.warn("催促短信发送失败: {}", phone);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("催促短信发送异常: {}", phone, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
log.info("催促短信发送完成,总数: {}, 成功: {}", validPhones.size(), successCount);
|
||||||
|
return successCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidPhone(String phone) {
|
||||||
|
if (StrUtil.isBlank(phone)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return PHONE_PATTERN.matcher(phone).matches();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -14,6 +14,7 @@ import com.yfd.platform.system.mapper.SysRoleMapper;
|
|||||||
import com.yfd.platform.system.service.ISysOrganizationService;
|
import com.yfd.platform.system.service.ISysOrganizationService;
|
||||||
import com.yfd.platform.system.service.IUserService;
|
import com.yfd.platform.system.service.IUserService;
|
||||||
import com.yfd.platform.utils.ObjectConverterUtil;
|
import com.yfd.platform.utils.ObjectConverterUtil;
|
||||||
|
import com.yfd.platform.utils.SecurityUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@ -56,21 +57,13 @@ public class SysOrganizationServiceImpl extends ServiceImpl<SysOrganizationMappe
|
|||||||
@Override
|
@Override
|
||||||
public List<Map<String, Object>> getOrgTree(String parentid,
|
public List<Map<String, Object>> getOrgTree(String parentid,
|
||||||
String params) {
|
String params) {
|
||||||
List<SysOrganization> orgList = new ArrayList<>();
|
|
||||||
QueryWrapper<SysOrganization> queryWrapper = new QueryWrapper<>();
|
|
||||||
//根据父级id查询
|
|
||||||
queryWrapper.eq("parentid", parentid);
|
|
||||||
if (StrUtil.isNotEmpty(params)) {
|
|
||||||
queryWrapper.like("orgname", params); // 根据 部门名称
|
|
||||||
}
|
|
||||||
SysUser userInfo = userService.getUserInfo();
|
SysUser userInfo = userService.getUserInfo();
|
||||||
|
|
||||||
|
// 构建权限过滤条件
|
||||||
|
Set<String> allowedOrgIds = new HashSet<>();
|
||||||
if (userInfo.getUsertype() != 0) {
|
if (userInfo.getUsertype() != 0) {
|
||||||
List<SysRole> roleByUserId =
|
List<SysRole> roleByUserId = sysRoleMapper.getRoleByUserId(userInfo.getId());
|
||||||
sysRoleMapper.getRoleByUserId(userInfo.getId());
|
|
||||||
List<String> ids = new ArrayList<>();
|
|
||||||
// 循环当前角色
|
|
||||||
for (SysRole sysRole : roleByUserId) {
|
for (SysRole sysRole : roleByUserId) {
|
||||||
// 获取角色的组织Id
|
|
||||||
String orgscope = sysRole.getOrgscope();
|
String orgscope = sysRole.getOrgscope();
|
||||||
if (StrUtil.isBlank(orgscope)) {
|
if (StrUtil.isBlank(orgscope)) {
|
||||||
continue;
|
continue;
|
||||||
@ -78,21 +71,33 @@ public class SysOrganizationServiceImpl extends ServiceImpl<SysOrganizationMappe
|
|||||||
// 拆分组织Id
|
// 拆分组织Id
|
||||||
String[] split = orgscope.split(",");
|
String[] split = orgscope.split(",");
|
||||||
List<String> stringList = Arrays.asList(split);
|
List<String> stringList = Arrays.asList(split);
|
||||||
Set<String> set = new HashSet<>();
|
if (!stringList.isEmpty()) {
|
||||||
if (stringList.size() > 0) {
|
allowedOrgIds.addAll(stringList);
|
||||||
List<SysOrganization> list =
|
// 查询这些组织的父级ID
|
||||||
sysOrganizationMapper.selectList(new LambdaQueryWrapper<SysOrganization>().in(SysOrganization::getId, stringList));
|
List<SysOrganization> list = sysOrganizationMapper.selectList(
|
||||||
list.forEach(l -> set.add(l.getParentid()));
|
new LambdaQueryWrapper<SysOrganization>().in(SysOrganization::getId, stringList));
|
||||||
|
for (SysOrganization org : list) {
|
||||||
|
if (org.getParentid() != null) {
|
||||||
|
allowedOrgIds.add(org.getParentid());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ids.addAll(stringList);
|
|
||||||
ids.addAll(set);
|
|
||||||
}
|
}
|
||||||
queryWrapper.in("id", ids);
|
|
||||||
}
|
}
|
||||||
orgList = this.list(queryWrapper.orderByAsc("orgcode"));
|
|
||||||
// 将实体对象转换为 Map,确保字段名与实体类一致
|
// 查询所有组织数据
|
||||||
// 将实体对象转换为 Map,确保字段名与实体类一致
|
QueryWrapper<SysOrganization> queryWrapper = new QueryWrapper<>();
|
||||||
List<Map<String,Object>> listMap = orgList.stream().map(org -> {
|
if (!allowedOrgIds.isEmpty()) {
|
||||||
|
queryWrapper.in("id", allowedOrgIds);
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotEmpty(params)) {
|
||||||
|
queryWrapper.like("orgname", params);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SysOrganization> allOrgList = this.list(queryWrapper.orderByAsc("orgcode"));
|
||||||
|
|
||||||
|
// 将所有组织数据转换为Map结构
|
||||||
|
List<Map<String, Object>> allOrgMaps = allOrgList.stream().map(org -> {
|
||||||
Map<String, Object> map = new HashMap<>();
|
Map<String, Object> map = new HashMap<>();
|
||||||
map.put("id", org.getId());
|
map.put("id", org.getId());
|
||||||
map.put("orgtype", org.getOrgtype());
|
map.put("orgtype", org.getOrgtype());
|
||||||
@ -109,12 +114,39 @@ public class SysOrganizationServiceImpl extends ServiceImpl<SysOrganizationMappe
|
|||||||
map.put("custom3", org.getCustom3());
|
map.put("custom3", org.getCustom3());
|
||||||
return map;
|
return map;
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
for (Map<String, Object> map : listMap) {
|
|
||||||
List<Map<String, Object>> childList = child(map.get(
|
// 构建父子关系映射
|
||||||
"id").toString());//查询下一子集
|
Map<String, List<Map<String, Object>>> parentToChildrenMap = new HashMap<>();
|
||||||
map.put("childList", childList); //添加新列 子集
|
for (Map<String, Object> orgMap : allOrgMaps) {
|
||||||
|
String parentId = (String) orgMap.get("parentid");
|
||||||
|
parentToChildrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(orgMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤出指定父级ID的组织作为根节点
|
||||||
|
List<Map<String, Object>> rootOrgs = parentToChildrenMap.getOrDefault(parentid, new ArrayList<>());
|
||||||
|
|
||||||
|
// 构建完整的树形结构
|
||||||
|
buildTreeStructure(rootOrgs, parentToChildrenMap);
|
||||||
|
|
||||||
|
return rootOrgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建树形结构
|
||||||
|
* @param orgList 当前层级的组织列表
|
||||||
|
* @param parentToChildrenMap 父子关系映射
|
||||||
|
*/
|
||||||
|
private void buildTreeStructure(List<Map<String, Object>> orgList,
|
||||||
|
Map<String, List<Map<String, Object>>> parentToChildrenMap) {
|
||||||
|
for (Map<String, Object> orgMap : orgList) {
|
||||||
|
String orgId = (String) orgMap.get("id");
|
||||||
|
List<Map<String, Object>> children = parentToChildrenMap.getOrDefault(orgId, new ArrayList<>());
|
||||||
|
if (!children.isEmpty()) {
|
||||||
|
buildTreeStructure(children, parentToChildrenMap); // 递归构建子树
|
||||||
|
}
|
||||||
|
orgMap.put("childList", children);
|
||||||
}
|
}
|
||||||
return listMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
@ -202,35 +234,42 @@ public class SysOrganizationServiceImpl extends ServiceImpl<SysOrganizationMappe
|
|||||||
@Override
|
@Override
|
||||||
public List<SysOrganization> getOrganizationById(String id,
|
public List<SysOrganization> getOrganizationById(String id,
|
||||||
String orgName) {
|
String orgName) {
|
||||||
|
|
||||||
LambdaQueryWrapper<SysOrganization> queryWrapper =
|
|
||||||
new LambdaQueryWrapper<>();
|
|
||||||
SysUser userInfo = userService.getUserInfo();
|
SysUser userInfo = userService.getUserInfo();
|
||||||
|
|
||||||
|
// 收集所有允许的组织ID
|
||||||
|
Set<String> allowedOrgIds = new HashSet<>();
|
||||||
if (userInfo.getUsertype() != 0) {
|
if (userInfo.getUsertype() != 0) {
|
||||||
List<SysRole> roleByUserId =
|
List<SysRole> roleByUserId = sysRoleMapper.getRoleByUserId(userInfo.getId());
|
||||||
sysRoleMapper.getRoleByUserId(userInfo.getId());
|
|
||||||
List<String> ids = new ArrayList<>();
|
|
||||||
// 循环当前角色
|
|
||||||
for (SysRole sysRole : roleByUserId) {
|
for (SysRole sysRole : roleByUserId) {
|
||||||
// 获取角色的组织Id
|
|
||||||
String orgscope = sysRole.getOrgscope();
|
String orgscope = sysRole.getOrgscope();
|
||||||
if (StrUtil.isBlank(orgscope)) {
|
if (StrUtil.isBlank(orgscope)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 拆分组织Id
|
// 拆分组织Id并添加到集合中(自动去重)
|
||||||
String[] split = orgscope.split(",");
|
String[] split = orgscope.split(",");
|
||||||
List<String> stringList = Arrays.asList(split);
|
for (String orgId : split) {
|
||||||
ids.addAll(stringList);
|
if (StrUtil.isNotBlank(orgId)) {
|
||||||
|
allowedOrgIds.add(orgId.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ObjectUtil.isNotEmpty(ids)) {
|
|
||||||
queryWrapper.in(SysOrganization::getId, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 构建查询条件
|
||||||
|
LambdaQueryWrapper<SysOrganization> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.eq(SysOrganization::getParentid, id);
|
||||||
|
|
||||||
if (StrUtil.isNotBlank(orgName)) {
|
if (StrUtil.isNotBlank(orgName)) {
|
||||||
queryWrapper.like(SysOrganization::getOrgname, orgName);
|
queryWrapper.like(SysOrganization::getOrgname, orgName);
|
||||||
}
|
}
|
||||||
queryWrapper.eq(SysOrganization::getParentid, id).orderByDesc(SysOrganization::getOrgcode);
|
|
||||||
|
// 如果有权限限制,添加IN条件
|
||||||
|
if (!allowedOrgIds.isEmpty()) {
|
||||||
|
queryWrapper.in(SysOrganization::getId, allowedOrgIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
queryWrapper.orderByDesc(SysOrganization::getOrgcode);
|
||||||
|
|
||||||
return this.list(queryWrapper);
|
return this.list(queryWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,20 +282,34 @@ public class SysOrganizationServiceImpl extends ServiceImpl<SysOrganizationMappe
|
|||||||
***********************************/
|
***********************************/
|
||||||
@Override
|
@Override
|
||||||
public List<Map<String, Object>> getOrgScopeTree(String roleId) {
|
public List<Map<String, Object>> getOrgScopeTree(String roleId) {
|
||||||
LambdaQueryWrapper<SysOrganization> queryWrapper =
|
|
||||||
new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<SysOrganization> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
if(!"admin".equals(SecurityUtils.getCurrentUsername())){
|
||||||
|
String userId = SecurityUtils.getUserId();
|
||||||
|
List<SysRole> roles = sysRoleMapper.getRoleByUserId(userId);
|
||||||
|
List<String> ids = new ArrayList<>();
|
||||||
|
for (SysRole role : roles) {
|
||||||
|
String orgscope = role.getOrgscope();
|
||||||
|
if(StrUtil.isNotBlank(orgscope)){
|
||||||
|
ids.addAll(Arrays.asList(orgscope.split(",")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ids.isEmpty()){
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
queryWrapper.in(SysOrganization::getId, ids);
|
||||||
|
}
|
||||||
queryWrapper.eq(SysOrganization::getIsvaild, '1');
|
queryWrapper.eq(SysOrganization::getIsvaild, '1');
|
||||||
queryWrapper.orderByAsc(SysOrganization::getOrgcode);
|
queryWrapper.orderByAsc(SysOrganization::getOrgcode);
|
||||||
List<Map<String, Object>> listMaps = this.listMaps(queryWrapper);
|
List<Map<String, Object>> mapList = this.listMaps(queryWrapper);
|
||||||
|
List<Map<String, Object>> listMaps = ObjectConverterUtil.convertMapFieldsToEntityFormat(SysOrganization.class, mapList);
|
||||||
// 获取当前角色
|
// 获取当前角色
|
||||||
SysRole sysRole = sysRoleMapper.selectById(roleId);
|
SysRole sysRole = sysRoleMapper.selectById(roleId);
|
||||||
String orgscope = sysRole.getOrgscope();
|
String orgscope = sysRole.getOrgscope();
|
||||||
List<String> ids = new ArrayList<>();
|
List<String> ids = new ArrayList<>();
|
||||||
if (StrUtil.isNotBlank(orgscope)) {
|
if (StrUtil.isNotBlank(orgscope)) {
|
||||||
String[] split = orgscope.split(",");
|
ids.addAll(Arrays.asList(orgscope.split(",")));
|
||||||
ids = Arrays.asList(split);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Map<String, Object> map : listMaps) {
|
for (Map<String, Object> map : listMaps) {
|
||||||
String id = (String) map.get("id");
|
String id = (String) map.get("id");
|
||||||
if (ids.contains(id)) {
|
if (ids.contains(id)) {
|
||||||
|
|||||||
@ -10,6 +10,13 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.yfd.platform.config.ResponseResult;
|
import com.yfd.platform.config.ResponseResult;
|
||||||
|
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.SdHbrvDic;
|
||||||
|
import com.yfd.platform.env.domain.StationBasinInfo;
|
||||||
|
import com.yfd.platform.env.mapper.SdEngInfoBHMapper;
|
||||||
|
import com.yfd.platform.env.mapper.SdHbrvDicMapper;
|
||||||
import com.yfd.platform.system.domain.LoginUser;
|
import com.yfd.platform.system.domain.LoginUser;
|
||||||
import com.yfd.platform.system.domain.SysRole;
|
import com.yfd.platform.system.domain.SysRole;
|
||||||
import com.yfd.platform.system.domain.SysUser;
|
import com.yfd.platform.system.domain.SysUser;
|
||||||
@ -55,12 +62,21 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private PasswordEncoder passwordEncoder;
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SdHbrvDicMapper sdHbrvDicMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SdEngInfoBHMapper sdEngInfoBHMapper;
|
||||||
/**
|
/**
|
||||||
* 文件空间配置
|
* 文件空间配置
|
||||||
*/
|
*/
|
||||||
@Resource
|
@Resource
|
||||||
private FileSpaceProperties fileSpaceProperties;
|
private FileSpaceProperties fileSpaceProperties;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SysUserDataScopeMapper sysUserDataScopeMapper;
|
||||||
|
|
||||||
/**********************************
|
/**********************************
|
||||||
* 用途说明:获取当前用户账号及名称
|
* 用途说明:获取当前用户账号及名称
|
||||||
* 参数说明
|
* 参数说明
|
||||||
@ -224,7 +240,6 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
|
|||||||
* 返回值说明: 是否更新成功
|
* 返回值说明: 是否更新成功
|
||||||
************************************/
|
************************************/
|
||||||
// ... existing code ...
|
// ... existing code ...
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Map updateById(SysUser sysUser, String roleids) {
|
public Map updateById(SysUser sysUser, String roleids) {
|
||||||
@ -309,7 +324,8 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理用户角色分配(增量更新)
|
* 处理用户角色分配(增量更新)
|
||||||
* @param userId 用户 ID
|
*
|
||||||
|
* @param userId 用户 ID
|
||||||
* @param roleIds 角色 ID 字符串(逗号分隔)
|
* @param roleIds 角色 ID 字符串(逗号分隔)
|
||||||
*/
|
*/
|
||||||
private void handleUserRoles(String userId, String roleIds) {
|
private void handleUserRoles(String userId, String roleIds) {
|
||||||
@ -430,9 +446,9 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
|
|||||||
//根据当前用户id 查询角色表的级别 currentUser.getUser() 获取当前用户id
|
//根据当前用户id 查询角色表的级别 currentUser.getUser() 获取当前用户id
|
||||||
String level = sysUserMapper.getMaxLevel(id);
|
String level = sysUserMapper.getMaxLevel(id);
|
||||||
//判断是否获取级别
|
//判断是否获取级别
|
||||||
if ("admin".equals(SecurityUtils.getCurrentUsername())||StrUtil.isNotEmpty(level)) {
|
if ("admin".equals(SecurityUtils.getCurrentUsername()) || StrUtil.isNotEmpty(level)) {
|
||||||
//判断当前用户级别 管理员及以上权限
|
//判断当前用户级别 管理员及以上权限
|
||||||
if ("admin".equals(SecurityUtils.getCurrentUsername())||Integer.parseInt(level) <= 2) {
|
if ("admin".equals(SecurityUtils.getCurrentUsername()) || Integer.parseInt(level) <= 2) {
|
||||||
SysUser sysUser = sysUserMapper.selectById(id);
|
SysUser sysUser = sysUserMapper.selectById(id);
|
||||||
UpdateWrapper<SysUser> updateWrapper = new UpdateWrapper<>();
|
UpdateWrapper<SysUser> updateWrapper = new UpdateWrapper<>();
|
||||||
String password = PasswordGenerator.generateRandomPassword(sysUser.getUsername());
|
String password = PasswordGenerator.generateRandomPassword(sysUser.getUsername());
|
||||||
@ -465,7 +481,7 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
|
|||||||
//根据当前用户id 查询角色表的级别 currentUser.getUser() 获取当前用户id
|
//根据当前用户id 查询角色表的级别 currentUser.getUser() 获取当前用户id
|
||||||
String level = sysUserMapper.getMaxLevel(id);
|
String level = sysUserMapper.getMaxLevel(id);
|
||||||
//判断当前用户级别 管理员及以上权限
|
//判断当前用户级别 管理员及以上权限
|
||||||
if ("admin".equals(SecurityUtils.getCurrentUsername())||Integer.parseInt(level) <= 2) {
|
if ("admin".equals(SecurityUtils.getCurrentUsername()) || Integer.parseInt(level) <= 2) {
|
||||||
UpdateWrapper<SysUser> updateWrapper = new UpdateWrapper<>();
|
UpdateWrapper<SysUser> updateWrapper = new UpdateWrapper<>();
|
||||||
//根据id修改用户状态,最近修改人,最近修改时间
|
//根据id修改用户状态,最近修改人,最近修改时间
|
||||||
updateWrapper.eq("id", id).set("status", status).set(
|
updateWrapper.eq("id", id).set("status", status).set(
|
||||||
@ -547,8 +563,8 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<SysUser> queryUsers(String orgid,
|
public Page<SysUser> queryUsers(String orgid,
|
||||||
String username,
|
String username,
|
||||||
Page<SysUser> page) {
|
Page<SysUser> page) {
|
||||||
Page<SysUser> mapPage = sysUserMapper.queryUsers(orgid,
|
Page<SysUser> mapPage = sysUserMapper.queryUsers(orgid,
|
||||||
username, page);
|
username, page);
|
||||||
mapPage.getRecords().forEach(record -> {
|
mapPage.getRecords().forEach(record -> {
|
||||||
@ -632,10 +648,10 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<SysUser> queryPendingAuditUsers(Page<SysUser> page,String name,String regStatus) {
|
public Page<SysUser> queryPendingAuditUsers(Page<SysUser> page, String name, String regStatus) {
|
||||||
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
queryWrapper.in(SysUser::getRegStatus, "PENDING","APPROVED","REJECTED");
|
queryWrapper.in(SysUser::getRegStatus, "PENDING", "APPROVED", "REJECTED");
|
||||||
queryWrapper.eq(ObjectUtil.isNotEmpty(regStatus),SysUser::getRegStatus, regStatus);
|
queryWrapper.eq(ObjectUtil.isNotEmpty(regStatus), SysUser::getRegStatus, regStatus);
|
||||||
queryWrapper.and(StrUtil.isNotBlank(name), wrapper ->
|
queryWrapper.and(StrUtil.isNotBlank(name), wrapper ->
|
||||||
wrapper.like(SysUser::getNickname, name)
|
wrapper.like(SysUser::getNickname, name)
|
||||||
.or()
|
.or()
|
||||||
@ -643,11 +659,85 @@ public class UserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impleme
|
|||||||
);
|
);
|
||||||
queryWrapper.orderByDesc(SysUser::getRegTime);
|
queryWrapper.orderByDesc(SysUser::getRegTime);
|
||||||
Page<SysUser> mapPage = this.page(page, queryWrapper);
|
Page<SysUser> mapPage = this.page(page, queryWrapper);
|
||||||
mapPage.getRecords().forEach(record -> {
|
|
||||||
String id = record.getId();
|
List<SysUser> records = mapPage.getRecords();
|
||||||
List<SysRole> sysRoles = sysRoleMapper.getRoleByUserId(id);
|
if (records == null || records.isEmpty()) {
|
||||||
record.setRoles(sysRoles);
|
return mapPage;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
List<String> userIds = records.stream()
|
||||||
|
.map(SysUser::getId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
Map<String, List<SysRole>> userRoleMap = new HashMap<>();
|
||||||
|
List<Map<String, Object>> userRoles = sysRoleMapper.getUserRolesByUserIds(userIds);
|
||||||
|
if (userRoles != null) {
|
||||||
|
for (Map<String, Object> row : userRoles) {
|
||||||
|
String userId = (String) row.get("userId");
|
||||||
|
SysRole role = new SysRole();
|
||||||
|
role.setId((String) row.get("id"));
|
||||||
|
role.setRolecode((String) row.get("rolecode"));
|
||||||
|
role.setRolename((String) row.get("rolename"));
|
||||||
|
role.setLevel((String) row.get("level"));
|
||||||
|
role.setDescription((String) row.get("description"));
|
||||||
|
role.setIsvaild((String) row.get("isvaild"));
|
||||||
|
role.setOrgscope((String) row.get("orgscope"));
|
||||||
|
role.setOptscope((String) row.get("optscope"));
|
||||||
|
role.setBusscope((String) row.get("busscope"));
|
||||||
|
userRoleMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<SysUserDataScope>> userScopeMap = new HashMap<>();
|
||||||
|
List<SysUserDataScope> allScopes = sysUserDataScopeMapper.selectValidPermissionsWithNameByUserIds(userIds);
|
||||||
|
if (allScopes != null) {
|
||||||
|
for (SysUserDataScope scope : allScopes) {
|
||||||
|
userScopeMap.computeIfAbsent(scope.getUserId(), k -> new ArrayList<>()).add(scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<StationBasinInfo> stationBasinInfos = sdEngInfoBHMapper.selectStationBasinInfo();
|
||||||
|
for (SysUser record : records) {
|
||||||
|
String userId = record.getId();
|
||||||
|
|
||||||
|
List<SysRole> roles = userRoleMap.getOrDefault(userId, Collections.emptyList());
|
||||||
|
record.setRoles(roles);
|
||||||
|
|
||||||
|
List<SysUserDataScope> scopes = userScopeMap.getOrDefault(userId, Collections.emptyList());
|
||||||
|
Set<String> basinNameSet = new LinkedHashSet<>();
|
||||||
|
Set<String> stationNameSet = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
for (SysUserDataScope scope : scopes) {
|
||||||
|
String orgType = scope.getOrgType();
|
||||||
|
String orgId = scope.getOrgId();
|
||||||
|
String orgName = scope.getOrgName();
|
||||||
|
|
||||||
|
if ("HBRVCD".equals(orgType)) {
|
||||||
|
if (orgName != null) {
|
||||||
|
basinNameSet.add(orgName);
|
||||||
|
}
|
||||||
|
if (orgId != null) {
|
||||||
|
if (stationBasinInfos != null) {
|
||||||
|
List<String> stcdList = stationBasinInfos.stream().filter(info -> info.getHbrvcd().equals(orgId)).map(StationBasinInfo::getEnnm).toList();
|
||||||
|
stationNameSet.addAll(stcdList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ("STATION".equals(orgType)) {
|
||||||
|
if (orgName != null) {
|
||||||
|
stationNameSet.add(orgName);
|
||||||
|
}
|
||||||
|
if (orgId != null) {
|
||||||
|
if (stationBasinInfos != null) {
|
||||||
|
String hbrbcd = stationBasinInfos.stream().filter(info -> info.getStcd().equals(orgId)).map(StationBasinInfo::getHbrvnm).findFirst().orElse(null);
|
||||||
|
basinNameSet.add(hbrbcd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record.setBasinNames(basinNameSet.isEmpty() ? null : String.join(",", basinNameSet));
|
||||||
|
record.setStationNames(stationNameSet.isEmpty() ? null : String.join(",", stationNameSet));
|
||||||
|
}
|
||||||
|
|
||||||
return mapPage;
|
return mapPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -391,6 +391,42 @@ public class FileUtil extends cn.hutool.core.io.FileUtil {
|
|||||||
fis.close();
|
fis.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断文件名是否为图片类型
|
||||||
|
*/
|
||||||
|
public static boolean isImageFileName(String fileName) {
|
||||||
|
if (fileName == null || fileName.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String lowerName = fileName.toLowerCase();
|
||||||
|
return lowerName.endsWith(".jpg")
|
||||||
|
|| lowerName.endsWith(".jpeg")
|
||||||
|
|| lowerName.endsWith(".png")
|
||||||
|
|| lowerName.endsWith(".gif")
|
||||||
|
|| lowerName.endsWith(".bmp")
|
||||||
|
|| lowerName.endsWith(".webp")
|
||||||
|
|| lowerName.endsWith(".svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断文件名是否为视频类型
|
||||||
|
*/
|
||||||
|
public static boolean isVideoFileName(String fileName) {
|
||||||
|
if (fileName == null || fileName.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String lowerName = fileName.toLowerCase();
|
||||||
|
return lowerName.endsWith(".mp4")
|
||||||
|
|| lowerName.endsWith(".avi")
|
||||||
|
|| lowerName.endsWith(".mov")
|
||||||
|
|| lowerName.endsWith(".wmv")
|
||||||
|
|| lowerName.endsWith(".flv")
|
||||||
|
|| lowerName.endsWith(".mkv");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getMd5(File file) {
|
public static String getMd5(File file) {
|
||||||
return getMd5(getByte(file));
|
return getMd5(getByte(file));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,334 @@
|
|||||||
|
package com.yfd.platform.utils;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流式 multipart 文件上传解析器。
|
||||||
|
* <p>
|
||||||
|
* 绕过 Spring 的 {@code MultipartResolver}(它会先把整个请求体解析完才调用 Controller),
|
||||||
|
* 直接从 {@link HttpServletRequest#getInputStream()} 边读边写目标文件,
|
||||||
|
* 80MB+ 的大文件也能在 Controller 方法内立即开始接收。
|
||||||
|
* <p>
|
||||||
|
* 前提:{@code spring.servlet.multipart.resolve-lazily=true}
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public final class MultipartStreamParser {
|
||||||
|
|
||||||
|
private static final int BUFFER_SIZE = 8192;
|
||||||
|
|
||||||
|
private MultipartStreamParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 multipart/form-data 请求中流式提取第一个文件并写入目标目录。
|
||||||
|
*
|
||||||
|
* @param request HTTP 请求(Content-Type: multipart/form-data)
|
||||||
|
* @param targetDir 目标目录(文件将保存为 upload.zip)
|
||||||
|
* @return 解析结果(原始文件名、目标文件、文件大小)
|
||||||
|
*/
|
||||||
|
public static StreamedFile streamToFile(HttpServletRequest request, Path targetDir) throws IOException {
|
||||||
|
long t0 = System.currentTimeMillis();
|
||||||
|
|
||||||
|
String contentType = request.getContentType();
|
||||||
|
if (contentType == null || !contentType.startsWith("multipart/form-data")) {
|
||||||
|
throw new IOException("请求不是 multipart/form-data 类型");
|
||||||
|
}
|
||||||
|
|
||||||
|
String boundary = extractBoundary(contentType);
|
||||||
|
byte[] closingDelim = ("\r\n--" + boundary + "--").getBytes(StandardCharsets.ISO_8859_1);
|
||||||
|
|
||||||
|
InputStream rawInput = new BufferedInputStream(request.getInputStream(), BUFFER_SIZE);
|
||||||
|
|
||||||
|
long t1 = System.currentTimeMillis();
|
||||||
|
String originalFileName = readPartHeaders(rawInput, boundary);
|
||||||
|
long t2 = System.currentTimeMillis();
|
||||||
|
|
||||||
|
Files.createDirectories(targetDir);
|
||||||
|
File targetFile = new File(targetDir.toFile(), "upload.zip");
|
||||||
|
long fileSize = streamBinaryToFile(rawInput, targetFile, closingDelim);
|
||||||
|
long t3 = System.currentTimeMillis();
|
||||||
|
|
||||||
|
log.info("流式接收完成: {} -> {} ({} bytes), 头解析{}ms, 体写入{}ms, 总耗时{}ms",
|
||||||
|
originalFileName, targetFile.getAbsolutePath(), fileSize,
|
||||||
|
t2 - t1, t3 - t2, t3 - t0);
|
||||||
|
return new StreamedFile(originalFileName, targetFile, fileSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 阶段1:读 part 头 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳过第一个 boundary 行,读取该 part 的 HTTP 头,从中提取 filename。
|
||||||
|
*/
|
||||||
|
private static String readPartHeaders(InputStream in, String boundary) throws IOException {
|
||||||
|
String firstBoundary = "--" + boundary;
|
||||||
|
String fileName = null;
|
||||||
|
|
||||||
|
// 跳过第一个 boundary 行
|
||||||
|
String line = readLine(in);
|
||||||
|
if (line == null || !line.equals(firstBoundary)) {
|
||||||
|
// 可能前面有空行或 boundary 带了 \r
|
||||||
|
if (line != null && line.startsWith(firstBoundary)) {
|
||||||
|
// ok
|
||||||
|
} else {
|
||||||
|
throw new IOException("未找到 multipart boundary,收到: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取 part 头,直到空行
|
||||||
|
while ((line = readLine(in)) != null) {
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
break; // 空行 = 头结束,接下来是二进制数据
|
||||||
|
}
|
||||||
|
if (line.startsWith("Content-Disposition") || line.startsWith("content-disposition")) {
|
||||||
|
fileName = extractFileName(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName == null) {
|
||||||
|
fileName = "unknown.zip";
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 Content-Disposition 头中提取 filename 值。
|
||||||
|
* 格式:form-data; name="file"; filename="xxx.zip"
|
||||||
|
*/
|
||||||
|
static String extractFileName(String headerLine) {
|
||||||
|
for (String part : headerLine.split(";")) {
|
||||||
|
part = part.trim();
|
||||||
|
if (part.startsWith("filename")) {
|
||||||
|
int eq = part.indexOf('=');
|
||||||
|
if (eq >= 0) {
|
||||||
|
String val = part.substring(eq + 1).trim();
|
||||||
|
if (val.startsWith("\"") && val.endsWith("\"")) {
|
||||||
|
val = val.substring(1, val.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先进行 URL 解码(处理 %E4%B8%AD%E6%96%87 这种编码)
|
||||||
|
try {
|
||||||
|
val = java.net.URLDecoder.decode(val, StandardCharsets.UTF_8.name());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("URL解码失败,使用原始值: {}", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再进行字符编码转换(处理 GBK/UTF-8 乱码)
|
||||||
|
String decodedFileName = decodeFileName(val);
|
||||||
|
return decodedFileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试修复乱码的文件名,支持 GBK 和 UTF-8 编码
|
||||||
|
*/
|
||||||
|
private static String decodeFileName(String rawFileName) {
|
||||||
|
if (rawFileName == null || rawFileName.isEmpty()) {
|
||||||
|
return rawFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] rawBytes = rawFileName.getBytes(StandardCharsets.ISO_8859_1);
|
||||||
|
|
||||||
|
// 优先尝试 UTF-8 解码(现代浏览器常用)
|
||||||
|
String utf8Decoded = new String(rawBytes, StandardCharsets.UTF_8);
|
||||||
|
if (isValidChinese(utf8Decoded)) {
|
||||||
|
return utf8Decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再尝试 GBK 解码(Windows/旧版浏览器常用)
|
||||||
|
String gbkDecoded = new String(rawBytes, "GBK");
|
||||||
|
if (isValidChinese(gbkDecoded)) {
|
||||||
|
return gbkDecoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 都不包含中文,返回 UTF-8 解码结果
|
||||||
|
return utf8Decoded;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("文件名解码失败,使用原始值: {}", rawFileName, e);
|
||||||
|
return rawFileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单判断字符串是否包含有效的中文字符
|
||||||
|
*/
|
||||||
|
private static boolean isValidChinese(String str) {
|
||||||
|
if (str == null || str.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (char c : str.toCharArray()) {
|
||||||
|
// 检查是否在中文 Unicode 范围内
|
||||||
|
if (c >= '\u4e00' && c <= '\u9fa5') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 检查中文标点符号范围
|
||||||
|
if (c >= '\u3000' && c <= '\u303f') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 阶段2:流式写二进制 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从输入流读取二进制数据写入目标文件,遇到 closingDelim 时停止。
|
||||||
|
* <p>
|
||||||
|
* 使用重叠窗口策略:每次读取一个 chunk,保留末尾 (delimLen-1) 字节
|
||||||
|
* 与下一个 chunk 拼接,防止 boundary 跨 chunk 被截断。
|
||||||
|
*/
|
||||||
|
private static long streamBinaryToFile(InputStream in, File targetFile, byte[] closingDelim) throws IOException {
|
||||||
|
int delimLen = closingDelim.length;
|
||||||
|
int overlap = delimLen - 1;
|
||||||
|
|
||||||
|
byte[] buf = new byte[BUFFER_SIZE];
|
||||||
|
byte[] carry = new byte[overlap]; // 上一轮末尾的重叠字节
|
||||||
|
int carryLen = 0;
|
||||||
|
long totalWritten = 0;
|
||||||
|
|
||||||
|
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(targetFile), BUFFER_SIZE)) {
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int n = in.read(buf);
|
||||||
|
if (n == -1) {
|
||||||
|
// 流结束,写出剩余的重叠字节
|
||||||
|
if (carryLen > 0) {
|
||||||
|
out.write(carry, 0, carryLen);
|
||||||
|
totalWritten += carryLen;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将上一轮的重叠字节 + 本轮数据拼接,搜索 closing boundary
|
||||||
|
byte[] searchBuf = new byte[carryLen + n];
|
||||||
|
if (carryLen > 0) {
|
||||||
|
System.arraycopy(carry, 0, searchBuf, 0, carryLen);
|
||||||
|
}
|
||||||
|
System.arraycopy(buf, 0, searchBuf, carryLen, n);
|
||||||
|
|
||||||
|
int boundaryPos = indexOf(searchBuf, closingDelim);
|
||||||
|
if (boundaryPos >= 0) {
|
||||||
|
// 找到 closing boundary,写出之前的数据
|
||||||
|
if (boundaryPos > 0) {
|
||||||
|
out.write(searchBuf, 0, boundaryPos);
|
||||||
|
totalWritten += boundaryPos;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未找到 boundary:写出安全部分(保留末尾 overlap 字节)
|
||||||
|
int safeLen = searchBuf.length - overlap;
|
||||||
|
if (safeLen > 0) {
|
||||||
|
out.write(searchBuf, 0, safeLen);
|
||||||
|
totalWritten += safeLen;
|
||||||
|
// 保留末尾 overlap 字节到 carry
|
||||||
|
System.arraycopy(searchBuf, safeLen, carry, 0, overlap);
|
||||||
|
carryLen = overlap;
|
||||||
|
} else {
|
||||||
|
// 数据量不足 overlap,全部保留
|
||||||
|
System.arraycopy(searchBuf, 0, carry, 0, searchBuf.length);
|
||||||
|
carryLen = searchBuf.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 工具方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 Content-Type 头中提取 boundary 值。
|
||||||
|
* 格式:multipart/form-data; boundary=----WebKitFormBoundary...
|
||||||
|
*/
|
||||||
|
static String extractBoundary(String contentType) {
|
||||||
|
for (String part : contentType.split(";")) {
|
||||||
|
part = part.trim();
|
||||||
|
if (part.startsWith("boundary=")) {
|
||||||
|
return part.substring("boundary=".length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("无法从 Content-Type 提取 boundary: " + contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按行读取(以 \r\n 或 \n 结尾),返回行内容(不含换行符)。
|
||||||
|
*/
|
||||||
|
private static String readLine(InputStream in) throws IOException {
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(256);
|
||||||
|
int prev = -1;
|
||||||
|
int b;
|
||||||
|
while ((b = in.read()) != -1) {
|
||||||
|
if (b == '\n') {
|
||||||
|
// 去掉末尾的 \r
|
||||||
|
byte[] bytes = bos.toByteArray();
|
||||||
|
int len = bytes.length;
|
||||||
|
if (len > 0 && bytes[len - 1] == '\r') {
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
return new String(bytes, 0, len, StandardCharsets.ISO_8859_1);
|
||||||
|
}
|
||||||
|
bos.write(b);
|
||||||
|
prev = b;
|
||||||
|
}
|
||||||
|
// 流结束
|
||||||
|
if (bos.size() > 0) {
|
||||||
|
return bos.toString(StandardCharsets.ISO_8859_1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在 byte 数组中查找子数组的位置(Boyer-Moore 简化版)。
|
||||||
|
*/
|
||||||
|
private static int indexOf(byte[] haystack, byte[] needle) {
|
||||||
|
if (needle.length == 0) return 0;
|
||||||
|
if (haystack.length < needle.length) return -1;
|
||||||
|
|
||||||
|
outer:
|
||||||
|
for (int i = 0; i <= haystack.length - needle.length; i++) {
|
||||||
|
for (int j = 0; j < needle.length; j++) {
|
||||||
|
if (haystack[i + j] != needle[j]) {
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 结果 DTO ====================
|
||||||
|
|
||||||
|
public static class StreamedFile {
|
||||||
|
private final String originalFileName;
|
||||||
|
private final File targetFile;
|
||||||
|
private final long fileSize;
|
||||||
|
|
||||||
|
StreamedFile(String originalFileName, File targetFile, long fileSize) {
|
||||||
|
this.originalFileName = originalFileName;
|
||||||
|
this.targetFile = targetFile;
|
||||||
|
this.fileSize = fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOriginalFileName() {
|
||||||
|
return originalFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getTargetFile() {
|
||||||
|
return targetFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFileSize() {
|
||||||
|
return fileSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -24,8 +24,11 @@ spring:
|
|||||||
matching-strategy: ant_path_matcher
|
matching-strategy: ant_path_matcher
|
||||||
servlet:
|
servlet:
|
||||||
multipart:
|
multipart:
|
||||||
max-file-size: 30MB
|
max-file-size: 300MB
|
||||||
max-request-size: 100MB
|
max-request-size: 500MB
|
||||||
|
file-size-threshold: 1KB
|
||||||
|
location: /tmp/upload
|
||||||
|
resolve-lazily: true
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
file:
|
file:
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
server:
|
server:
|
||||||
port: 8093
|
port: 8093
|
||||||
|
tomcat:
|
||||||
|
connection-timeout: 300000
|
||||||
|
max-swallow-size: 500MB
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
#应用名称
|
#应用名称
|
||||||
@ -13,11 +16,94 @@ spring:
|
|||||||
url: "${DB_MASTER_URL:jdbc:oracle:thin:@172.16.21.134:1521/SDLYZ}"
|
url: "${DB_MASTER_URL:jdbc:oracle:thin:@172.16.21.134:1521/SDLYZ}"
|
||||||
username: "${DB_MASTER_USERNAME:QGC_REFA}"
|
username: "${DB_MASTER_USERNAME:QGC_REFA}"
|
||||||
password: "${DB_MASTER_PASSWORD:Y4M4K1oCkL8U}"
|
password: "${DB_MASTER_PASSWORD:Y4M4K1oCkL8U}"
|
||||||
|
initial-size: 5
|
||||||
|
min-idle: 5
|
||||||
|
max-active: 20
|
||||||
|
max-wait: 30000
|
||||||
|
async-init: true
|
||||||
|
keep-alive-between-time-millis: 120000
|
||||||
|
time-between-eviction-runs-millis: 60000
|
||||||
|
min-evictable-idle-time-millis: 180000
|
||||||
|
max-evictable-idle-time-millis: 300000
|
||||||
|
phy-timeout-millis: 25200000
|
||||||
|
validation-query: SELECT 1 FROM DUAL
|
||||||
|
validation-query-timeout: 3
|
||||||
|
test-while-idle: true
|
||||||
|
test-on-borrow: false
|
||||||
|
test-on-return: false
|
||||||
|
keep-alive: true
|
||||||
|
remove-abandoned: true
|
||||||
|
remove-abandoned-timeout: 1800
|
||||||
|
log-abandoned: true
|
||||||
|
break-after-acquire-failure: true
|
||||||
|
time-between-connect-error-millis: 300000
|
||||||
|
pool-prepared-statements: true
|
||||||
|
max-open-prepared-statements: 100
|
||||||
|
max-pool-prepared-statement-per-connection-size: 100
|
||||||
|
connection-properties: oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000;oracle.net.READ_TIMEOUT=60000
|
||||||
slave:
|
slave:
|
||||||
driverClassName: oracle.jdbc.OracleDriver
|
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.21.134:1521/SDLYZ}"
|
||||||
username: "${DB_SLAVE_USERNAME:QGC_REFA}"
|
username: "${DB_SLAVE_USERNAME:QGC_REFA}"
|
||||||
password: "${DB_SLAVE_PASSWORD:Y4M4K1oCkL8U}"
|
password: "${DB_SLAVE_PASSWORD:Y4M4K1oCkL8U}"
|
||||||
|
initial-size: 5
|
||||||
|
min-idle: 5
|
||||||
|
max-active: 20
|
||||||
|
max-wait: 30000
|
||||||
|
async-init: true
|
||||||
|
keep-alive-between-time-millis: 120000
|
||||||
|
time-between-eviction-runs-millis: 60000
|
||||||
|
min-evictable-idle-time-millis: 180000
|
||||||
|
max-evictable-idle-time-millis: 300000
|
||||||
|
phy-timeout-millis: 25200000
|
||||||
|
validation-query: SELECT 1 FROM DUAL
|
||||||
|
validation-query-timeout: 3
|
||||||
|
test-while-idle: true
|
||||||
|
test-on-borrow: false
|
||||||
|
test-on-return: false
|
||||||
|
keep-alive: true
|
||||||
|
remove-abandoned: true
|
||||||
|
remove-abandoned-timeout: 1800
|
||||||
|
log-abandoned: true
|
||||||
|
break-after-acquire-failure: true
|
||||||
|
time-between-connect-error-millis: 300000
|
||||||
|
pool-prepared-statements: true
|
||||||
|
max-open-prepared-statements: 100
|
||||||
|
max-pool-prepared-statement-per-connection-size: 100
|
||||||
|
connection-properties: oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000;oracle.net.READ_TIMEOUT=60000
|
||||||
|
filter:
|
||||||
|
stat:
|
||||||
|
enabled: true
|
||||||
|
log-slow-sql: true
|
||||||
|
slow-sql-millis: 3000
|
||||||
|
merge-sql: true
|
||||||
|
slf4j:
|
||||||
|
enabled: true
|
||||||
|
wall:
|
||||||
|
enabled: true
|
||||||
|
log-violation: true
|
||||||
|
throw-exception: true
|
||||||
|
config:
|
||||||
|
select-where-alway-true-check: true
|
||||||
|
select-having-alway-true-check: true
|
||||||
|
delete-where-alway-true-check: true
|
||||||
|
update-where-alay-true-check: true
|
||||||
|
update-where-alway-true-check: true
|
||||||
|
update-where-none-check: true
|
||||||
|
multi-statement-allow: false
|
||||||
|
web-stat-filter:
|
||||||
|
enabled: true
|
||||||
|
url-pattern: /*
|
||||||
|
exclusions: '*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*'
|
||||||
|
session-stat-enable: true
|
||||||
|
principal-session-name: admin
|
||||||
|
profile-enable: true
|
||||||
|
stat-view-servlet:
|
||||||
|
enabled: true
|
||||||
|
url-pattern: /druid/*
|
||||||
|
login-username: admin
|
||||||
|
login-password: admin
|
||||||
|
reset-enable: false
|
||||||
|
|
||||||
jackson:
|
jackson:
|
||||||
date-format: yyyy-MM-dd HH:mm:ss
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
@ -29,6 +115,9 @@ spring:
|
|||||||
multipart:
|
multipart:
|
||||||
max-file-size: 300MB
|
max-file-size: 300MB
|
||||||
max-request-size: 500MB
|
max-request-size: 500MB
|
||||||
|
file-size-threshold: 1KB
|
||||||
|
location: /tmp/upload
|
||||||
|
resolve-lazily: true
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
file:
|
file:
|
||||||
@ -122,6 +211,6 @@ attachment:
|
|||||||
token: ${ATTACHMENT_TOKEN:qgcBkod25ngBa4wu8BtfCPYsJ7lQGVDoexH}
|
token: ${ATTACHMENT_TOKEN:qgcBkod25ngBa4wu8BtfCPYsJ7lQGVDoexH}
|
||||||
upload-url: ${ATTACHMENT_UPLOAD_URL:http://172.16.31.185:18200/upload}
|
upload-url: ${ATTACHMENT_UPLOAD_URL:http://172.16.31.185:18200/upload}
|
||||||
video-url: ${ATTACHMENT_VIDEO_URL:http://172.16.31.185:18200/upload}
|
video-url: ${ATTACHMENT_VIDEO_URL:http://172.16.31.185:18200/upload}
|
||||||
delete-url: ${ATTACHMENT_DELETE_URL:http://172.16.31.185:18200/delete}
|
delete-url: ${ATTACHMENT_DELETE_URL:http://172.16.31.185:18200/FileDelete}
|
||||||
# upload-url: ${ATTACHMENT_UPLOAD_URL:https://211.99.26.225:12125/upload}
|
# upload-url: ${ATTACHMENT_UPLOAD_URL:https://211.99.26.225:12125/upload}
|
||||||
# video-url: ${ATTACHMENT_VIDEO_URL:https://211.99.26.225:12125/upload}
|
# video-url: ${ATTACHMENT_VIDEO_URL:https://211.99.26.225:12125/upload}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
server:
|
server:
|
||||||
port: 8093
|
port: 8093
|
||||||
|
tomcat:
|
||||||
|
connection-timeout: 300000
|
||||||
|
max-swallow-size: 500MB
|
||||||
spring:
|
spring:
|
||||||
#应用名称
|
#应用名称
|
||||||
application:
|
application:
|
||||||
@ -13,12 +15,94 @@ spring:
|
|||||||
url: "${DB_MASTER_URL:jdbc:oracle:thin:@172.16.31.190:1521/SDLYZ}"
|
url: "${DB_MASTER_URL:jdbc:oracle:thin:@172.16.31.190:1521/SDLYZ}"
|
||||||
username: "${DB_MASTER_USERNAME:QGC_REFA}"
|
username: "${DB_MASTER_USERNAME:QGC_REFA}"
|
||||||
password: "${DB_MASTER_PASSWORD:Y4M4K1oCkL8U}"
|
password: "${DB_MASTER_PASSWORD:Y4M4K1oCkL8U}"
|
||||||
|
initial-size: 5
|
||||||
|
min-idle: 5
|
||||||
|
max-active: 20
|
||||||
|
max-wait: 30000
|
||||||
|
async-init: true
|
||||||
|
keep-alive-between-time-millis: 120000
|
||||||
|
time-between-eviction-runs-millis: 60000
|
||||||
|
min-evictable-idle-time-millis: 180000
|
||||||
|
max-evictable-idle-time-millis: 300000
|
||||||
|
phy-timeout-millis: 25200000
|
||||||
|
validation-query: SELECT 1 FROM DUAL
|
||||||
|
validation-query-timeout: 3
|
||||||
|
test-while-idle: true
|
||||||
|
test-on-borrow: false
|
||||||
|
test-on-return: false
|
||||||
|
keep-alive: true
|
||||||
|
remove-abandoned: true
|
||||||
|
remove-abandoned-timeout: 1800
|
||||||
|
log-abandoned: true
|
||||||
|
break-after-acquire-failure: true
|
||||||
|
time-between-connect-error-millis: 300000
|
||||||
|
pool-prepared-statements: true
|
||||||
|
max-open-prepared-statements: 100
|
||||||
|
max-pool-prepared-statement-per-connection-size: 100
|
||||||
|
connection-properties: oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000;oracle.net.READ_TIMEOUT=60000
|
||||||
slave:
|
slave:
|
||||||
driverClassName: oracle.jdbc.OracleDriver
|
driverClassName: oracle.jdbc.OracleDriver
|
||||||
url: "${DB_SLAVE_URL:jdbc:oracle:thin:@172.16.31.190:1521/SDLYZ}"
|
url: "${DB_SLAVE_URL:jdbc:oracle:thin:@172.16.31.190:1521/SDLYZ}"
|
||||||
username: "${DB_SLAVE_USERNAME:QGC_REFA}"
|
username: "${DB_SLAVE_USERNAME:QGC_REFA}"
|
||||||
password: "${DB_SLAVE_PASSWORD:Y4M4K1oCkL8U}"
|
password: "${DB_SLAVE_PASSWORD:Y4M4K1oCkL8U}"
|
||||||
|
initial-size: 5
|
||||||
|
min-idle: 5
|
||||||
|
max-active: 20
|
||||||
|
max-wait: 30000
|
||||||
|
async-init: true
|
||||||
|
keep-alive-between-time-millis: 120000
|
||||||
|
time-between-eviction-runs-millis: 60000
|
||||||
|
min-evictable-idle-time-millis: 180000
|
||||||
|
max-evictable-idle-time-millis: 300000
|
||||||
|
phy-timeout-millis: 25200000
|
||||||
|
validation-query: SELECT 1 FROM DUAL
|
||||||
|
validation-query-timeout: 3
|
||||||
|
test-while-idle: true
|
||||||
|
test-on-borrow: false
|
||||||
|
test-on-return: false
|
||||||
|
keep-alive: true
|
||||||
|
remove-abandoned: true
|
||||||
|
remove-abandoned-timeout: 1800
|
||||||
|
log-abandoned: true
|
||||||
|
break-after-acquire-failure: true
|
||||||
|
time-between-connect-error-millis: 300000
|
||||||
|
pool-prepared-statements: true
|
||||||
|
max-open-prepared-statements: 100
|
||||||
|
max-pool-prepared-statement-per-connection-size: 100
|
||||||
|
connection-properties: oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000;oracle.net.READ_TIMEOUT=60000
|
||||||
|
filter:
|
||||||
|
stat:
|
||||||
|
enabled: true
|
||||||
|
log-slow-sql: true
|
||||||
|
slow-sql-millis: 3000
|
||||||
|
merge-sql: true
|
||||||
|
slf4j:
|
||||||
|
enabled: true
|
||||||
|
wall:
|
||||||
|
enabled: true
|
||||||
|
log-violation: true
|
||||||
|
throw-exception: true
|
||||||
|
config:
|
||||||
|
select-where-alway-true-check: true
|
||||||
|
select-having-alway-true-check: true
|
||||||
|
delete-where-alway-true-check: true
|
||||||
|
update-where-alay-true-check: true
|
||||||
|
update-where-alway-true-check: true
|
||||||
|
update-where-none-check: true
|
||||||
|
multi-statement-allow: false
|
||||||
|
web-stat-filter:
|
||||||
|
enabled: true
|
||||||
|
url-pattern: /*
|
||||||
|
exclusions: '*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*'
|
||||||
|
session-stat-enable: true
|
||||||
|
principal-session-name: admin
|
||||||
|
profile-enable: true
|
||||||
|
stat-view-servlet:
|
||||||
|
enabled: true
|
||||||
|
url-pattern: /druid/*
|
||||||
|
login-username: admin
|
||||||
|
login-password: admin
|
||||||
|
reset-enable: false
|
||||||
jackson:
|
jackson:
|
||||||
date-format: yyyy-MM-dd HH:mm:ss
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
time-zone: GMT+8
|
time-zone: GMT+8
|
||||||
@ -29,7 +113,9 @@ spring:
|
|||||||
multipart:
|
multipart:
|
||||||
max-file-size: 300MB
|
max-file-size: 300MB
|
||||||
max-request-size: 500MB
|
max-request-size: 500MB
|
||||||
|
file-size-threshold: 1KB
|
||||||
|
location: /tmp/upload
|
||||||
|
resolve-lazily: true
|
||||||
logging:
|
logging:
|
||||||
file:
|
file:
|
||||||
name: logs/platform-dev.log
|
name: logs/platform-dev.log
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
server:
|
server:
|
||||||
port: 8090
|
port: 8093
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
#应用名称
|
#应用名称
|
||||||
@ -9,44 +9,120 @@ spring:
|
|||||||
type: com.alibaba.druid.pool.DruidDataSource
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
druid:
|
druid:
|
||||||
master:
|
master:
|
||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
driverClassName: oracle.jdbc.OracleDriver
|
||||||
url: "${DB_MASTER_URL:jdbc:mysql://43.138.168.68:3306/frameworkdb2025?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true}"
|
url: "${DB_MASTER_URL:jdbc:oracle:thin:@172.16.21.134:1521/SDLYZ}"
|
||||||
username: "${DB_MASTER_USERNAME:root}"
|
username: "${DB_MASTER_USERNAME:QGC_REFA}"
|
||||||
password: "${DB_MASTER_PASSWORD:}"
|
password: "${DB_MASTER_PASSWORD:Y4M4K1oCkL8U}"
|
||||||
|
slave:
|
||||||
|
driverClassName: oracle.jdbc.OracleDriver
|
||||||
|
url: "${DB_SLAVE_URL:jdbc:oracle:thin:@172.16.21.134:1521/SDLYZ}"
|
||||||
|
username: "${DB_SLAVE_USERNAME:QGC_REFA}"
|
||||||
|
password: "${DB_SLAVE_PASSWORD:Y4M4K1oCkL8U}"
|
||||||
|
|
||||||
|
jackson:
|
||||||
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
|
time-zone: GMT+8
|
||||||
mvc:
|
mvc:
|
||||||
pathmatch:
|
pathmatch:
|
||||||
matching-strategy: ant_path_matcher
|
matching-strategy: ant_path_matcher
|
||||||
servlet:
|
servlet:
|
||||||
multipart:
|
multipart:
|
||||||
max-file-size: 30MB
|
max-file-size: 300MB
|
||||||
max-request-size: 100MB
|
max-request-size: 500MB
|
||||||
|
file-size-threshold: 1KB
|
||||||
|
location: /tmp/upload
|
||||||
|
resolve-lazily: true
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
file:
|
file:
|
||||||
name: logs/projectname.log
|
name: logs/platform-dev.log
|
||||||
|
|
||||||
level:
|
level:
|
||||||
com.genersoft.iot: debug
|
root: info
|
||||||
com.genersoft.iot.vmp.storager.dao: info
|
com.yfd.platform: info
|
||||||
com.genersoft.iot.vmp.gb28181: info
|
# com.yfd.platform.*.mapper: trace
|
||||||
|
|
||||||
# 在线文档: swagger-ui(生产环境建议关闭)
|
# 在线文档: swagger-ui(生产环境建议关闭)
|
||||||
swagger-ui:
|
swagger-ui:
|
||||||
enabled: false
|
enabled: true
|
||||||
|
|
||||||
file-space: #项目文档空间
|
mybatis-plus:
|
||||||
files: D:\demoproject\files\ #单独上传的文件附件
|
# mapper-locations: classpath*:**/mapper/*Mapper.xml,classpath*:**/mapping/*Mapper.xml
|
||||||
useravatar: D:\demoproject\useravatar\ #用户头像
|
global-config:
|
||||||
system: D:\demoproject\system\ #系统文档根目录,用于头像等静态资源
|
banner: false
|
||||||
|
db-config:
|
||||||
|
id-type: ASSIGN_ID
|
||||||
|
insert-strategy: not_null
|
||||||
|
update-strategy: not_null
|
||||||
|
select-strategy: not_empty
|
||||||
|
table-underline: true
|
||||||
|
logic-delete-value: 1
|
||||||
|
logic-not-delete-value: 0
|
||||||
|
logic-delete-field: isDeleted
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
cache-enabled: false
|
||||||
|
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
|
||||||
|
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
|
||||||
|
|
||||||
|
# 登录相关配置
|
||||||
|
login:
|
||||||
|
# 登录缓存
|
||||||
|
cache-enable: true
|
||||||
|
# 是否限制单用户登录
|
||||||
|
single-login: false
|
||||||
|
# 验证码
|
||||||
|
login-code:
|
||||||
|
# 验证码类型配置 查看 LoginProperties 类
|
||||||
|
code-type: arithmetic
|
||||||
|
|
||||||
# 启动自动数据库初始化(仅 dev/server):
|
# 启动自动数据库初始化(仅 dev/server):
|
||||||
app:
|
app:
|
||||||
|
# ZIP导入临时目录配置
|
||||||
|
zip-import:
|
||||||
|
temp-dir: ${ZIP_IMPORT_TEMP_DIR:/qgc-platform/tmp/zip_import_temp}
|
||||||
init:
|
init:
|
||||||
enabled: true
|
enabled: false
|
||||||
schema: classpath:db-init/sql/min-schema.sql
|
schema: classpath:db-init/sql/min-schema.sql
|
||||||
# data 文件可选;为避免复杂 dump 解析问题,先不导入
|
# data 文件可选;为避免复杂 dump 解析问题,先不导入
|
||||||
# data:
|
# data:
|
||||||
marker-table: sys_user
|
marker-table: sys_user
|
||||||
marker-version: v1.0.0
|
marker-version: v1.0.0
|
||||||
|
# 登录图形验证码有效时间/分钟
|
||||||
|
expiration: 2
|
||||||
|
# 验证码高度
|
||||||
|
width: 111
|
||||||
|
# 验证码宽度
|
||||||
|
heigth: 36
|
||||||
|
# 内容长度
|
||||||
|
length: 2
|
||||||
|
# 字体名称,为空则使用默认字体
|
||||||
|
font-name:
|
||||||
|
# 字体大小
|
||||||
|
font-size: 25
|
||||||
|
|
||||||
|
# IP 本地解析
|
||||||
|
ip:
|
||||||
|
local-parsing: true
|
||||||
|
|
||||||
|
|
||||||
|
file-space: #项目文档空间
|
||||||
|
files: /qgc-platform/files/ #单独上传的文件附件
|
||||||
|
system: /qgc-platform/system/ #单独上传的文件
|
||||||
|
|
||||||
|
task:
|
||||||
|
pool:
|
||||||
|
# 核心线程池大小
|
||||||
|
core-pool-size: 10
|
||||||
|
# 最大线程数
|
||||||
|
max-pool-size: 30
|
||||||
|
# 活跃时间
|
||||||
|
keep-alive-seconds: 60
|
||||||
|
# 队列容量
|
||||||
|
queue-capacity: 50
|
||||||
|
|
||||||
|
attachment:
|
||||||
|
token: ${ATTACHMENT_TOKEN:qgcBkod25ngBa4wu8BtfCPYsJ7lQGVDoexH}
|
||||||
|
upload-url: ${ATTACHMENT_UPLOAD_URL:http://172.16.31.185:18200/upload}
|
||||||
|
video-url: ${ATTACHMENT_VIDEO_URL:http://172.16.31.185:18200/upload}
|
||||||
|
delete-url: ${ATTACHMENT_DELETE_URL:http://172.16.31.185:18200/delete}
|
||||||
@ -38,5 +38,5 @@ springdoc:
|
|||||||
swagger-ui:
|
swagger-ui:
|
||||||
enabled: true
|
enabled: true
|
||||||
path: /swagger-ui.html
|
path: /swagger-ui.html
|
||||||
packages-to-scan: com.yfd.platform
|
# packages-to-scan: com.yfd.platform
|
||||||
|
|
||||||
|
|||||||
195
backend/src/main/resources/mapper/data/FishStatisticsMapper.xml
Normal file
195
backend/src/main/resources/mapper/data/FishStatisticsMapper.xml
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.yfd.platform.data.mapper.FishStatisticsMapper">
|
||||||
|
|
||||||
|
<resultMap id="StatisticsResultMap" type="com.yfd.platform.data.domain.vo.FishStatisticsVO">
|
||||||
|
<result column="USER_ID" property="userId"/>
|
||||||
|
<result column="REAL_NAME" property="realName"/>
|
||||||
|
<result column="PHONE" property="phone"/>
|
||||||
|
<result column="CONTACT" property="contact"/>
|
||||||
|
<result column="BASIN_NAMES" property="basinNames"/>
|
||||||
|
<result column="STATION_NAMES" property="stationNames"/>
|
||||||
|
<result column="BASIN_CODE" property="basinCode"/>
|
||||||
|
<result column="STATION_CODE" property="stationCode"/>
|
||||||
|
<result column="REPORT_MONTH" property="reportMonth"/>
|
||||||
|
<result column="MIN_STRDT" property="minStrdt"/>
|
||||||
|
<result column="MAX_ENDDT" property="maxEnddt"/>
|
||||||
|
<result column="TOTAL_FCNT" property="totalFcnt"/>
|
||||||
|
<result column="HAS_DATA" property="hasData"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="statisticsQuery">
|
||||||
|
WITH
|
||||||
|
user_scope_detail AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
su.ID AS USER_ID,
|
||||||
|
e.STCD AS STATION_CODE,
|
||||||
|
h.HBRVCD AS BASIN_CODE,
|
||||||
|
h.HBRVNM AS BASIN_NAME,
|
||||||
|
e.ENNM AS STATION_NAME
|
||||||
|
FROM QGC_REFA.SYS_USER su
|
||||||
|
JOIN QGC_REFA.SYS_USER_DATA_SCOPE sud
|
||||||
|
ON su.ID = sud.USER_ID
|
||||||
|
AND sud.STATUS = 1
|
||||||
|
AND sud.ORG_TYPE = 'HBRVCD'
|
||||||
|
JOIN QGC_REFA.SD_HBRV_DIC h
|
||||||
|
ON sud.ORG_ID = h.HBRVCD
|
||||||
|
AND h.ENABLED = 1
|
||||||
|
AND h.IS_DELETED = 0
|
||||||
|
JOIN QGC_REFA.SD_ENGINFO_B_H e
|
||||||
|
ON e.HBRVCD = h.HBRVCD
|
||||||
|
AND e.USFL = 1
|
||||||
|
WHERE su.STATUS = 1
|
||||||
|
AND su.REG_STATUS IN ('PENDING', 'APPROVED', 'REJECTED')
|
||||||
|
|
||||||
|
UNION
|
||||||
|
|
||||||
|
SELECT DISTINCT
|
||||||
|
su.ID AS USER_ID,
|
||||||
|
e.STCD AS STATION_CODE,
|
||||||
|
h.HBRVCD AS BASIN_CODE,
|
||||||
|
h.HBRVNM AS BASIN_NAME,
|
||||||
|
e.ENNM AS STATION_NAME
|
||||||
|
FROM QGC_REFA.SYS_USER su
|
||||||
|
JOIN QGC_REFA.SYS_USER_DATA_SCOPE sud
|
||||||
|
ON su.ID = sud.USER_ID
|
||||||
|
AND sud.STATUS = 1
|
||||||
|
AND sud.ORG_TYPE = 'STATION'
|
||||||
|
JOIN QGC_REFA.SD_ENGINFO_B_H e
|
||||||
|
ON sud.ORG_ID = e.STCD
|
||||||
|
AND e.USFL = 1
|
||||||
|
JOIN QGC_REFA.SD_HBRV_DIC h
|
||||||
|
ON e.HBRVCD = h.HBRVCD
|
||||||
|
AND h.ENABLED = 1
|
||||||
|
AND h.IS_DELETED = 0
|
||||||
|
WHERE su.STATUS = 1
|
||||||
|
AND su.REG_STATUS IN ('PENDING', 'APPROVED', 'REJECTED')
|
||||||
|
),
|
||||||
|
|
||||||
|
filtered_users AS (
|
||||||
|
SELECT DISTINCT USER_ID
|
||||||
|
FROM user_scope_detail
|
||||||
|
<where>
|
||||||
|
<if test="basinCode != null and basinCode != ''">
|
||||||
|
AND BASIN_CODE = #{basinCode}
|
||||||
|
</if>
|
||||||
|
<if test="stationCode != null and stationCode != ''">
|
||||||
|
AND STATION_CODE = #{stationCode}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
),
|
||||||
|
|
||||||
|
distinct_basins AS (
|
||||||
|
SELECT DISTINCT USER_ID,BASIN_CODE, BASIN_NAME
|
||||||
|
FROM user_scope_detail
|
||||||
|
WHERE USER_ID IN (SELECT USER_ID FROM filtered_users)
|
||||||
|
),
|
||||||
|
|
||||||
|
distinct_stations AS (
|
||||||
|
SELECT DISTINCT USER_ID, STATION_CODE, STATION_NAME
|
||||||
|
FROM user_scope_detail
|
||||||
|
WHERE USER_ID IN (SELECT USER_ID FROM filtered_users)
|
||||||
|
),
|
||||||
|
|
||||||
|
basin_agg AS (
|
||||||
|
SELECT
|
||||||
|
USER_ID,
|
||||||
|
RTRIM(
|
||||||
|
XMLAGG(XMLELEMENT(E, BASIN_NAME || ',') ORDER BY BASIN_NAME)
|
||||||
|
.EXTRACT('//text()').GETCLOBVAL(),
|
||||||
|
','
|
||||||
|
) AS BASIN_NAMES,
|
||||||
|
MIN(BASIN_CODE) AS MIN_BASIN_CODE -- 辅助排序字段(VARCHAR2)
|
||||||
|
FROM distinct_basins
|
||||||
|
GROUP BY USER_ID
|
||||||
|
),
|
||||||
|
|
||||||
|
station_agg AS (
|
||||||
|
SELECT
|
||||||
|
USER_ID,
|
||||||
|
RTRIM(
|
||||||
|
XMLAGG(XMLELEMENT(E, STATION_NAME || ',') ORDER BY STATION_NAME)
|
||||||
|
.EXTRACT('//text()').GETCLOBVAL(),
|
||||||
|
','
|
||||||
|
) AS STATION_NAMES,
|
||||||
|
MIN(STATION_CODE) AS MIN_STATION_CODE -- 辅助排序字段(VARCHAR2)
|
||||||
|
FROM distinct_stations
|
||||||
|
GROUP BY USER_ID
|
||||||
|
),
|
||||||
|
|
||||||
|
fish_monthly AS (
|
||||||
|
SELECT
|
||||||
|
CREATED_BY,
|
||||||
|
TO_CHAR(STRDT, 'YYYY-MM') AS REPORT_MONTH,
|
||||||
|
MIN(STRDT) AS MIN_STRDT,
|
||||||
|
MAX(STRDT) AS MAX_ENDDT,
|
||||||
|
SUM(FCNT) AS TOTAL_FCNT
|
||||||
|
FROM QGC_REFA.FISH_DRAFT_DATA
|
||||||
|
WHERE DELETED_FLAG = 0
|
||||||
|
AND STATUS IN ('PENDING','APPROVED')
|
||||||
|
GROUP BY CREATED_BY, TO_CHAR(STRDT, 'YYYY-MM')
|
||||||
|
),
|
||||||
|
|
||||||
|
all_users AS (
|
||||||
|
SELECT USER_ID FROM filtered_users
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
u.USER_ID,
|
||||||
|
su.REAL_NAME,
|
||||||
|
su.PHONE,
|
||||||
|
su.NICKNAME || '/' || su.PHONE AS CONTACT,
|
||||||
|
ba.MIN_BASIN_CODE,
|
||||||
|
sa.MIN_STATION_CODE,
|
||||||
|
ba.BASIN_NAMES,
|
||||||
|
sa.STATION_NAMES,
|
||||||
|
fm.REPORT_MONTH,
|
||||||
|
fm.MIN_STRDT,
|
||||||
|
fm.MAX_ENDDT,
|
||||||
|
fm.TOTAL_FCNT,
|
||||||
|
CASE WHEN fm.CREATED_BY IS NOT NULL THEN 1 ELSE 0 END AS HAS_DATA
|
||||||
|
FROM all_users u
|
||||||
|
JOIN QGC_REFA.SYS_USER su
|
||||||
|
ON u.USER_ID = su.ID
|
||||||
|
LEFT JOIN basin_agg ba
|
||||||
|
ON u.USER_ID = ba.USER_ID
|
||||||
|
LEFT JOIN station_agg sa
|
||||||
|
ON u.USER_ID = sa.USER_ID
|
||||||
|
LEFT JOIN fish_monthly fm
|
||||||
|
ON u.USER_ID = fm.CREATED_BY
|
||||||
|
|
||||||
|
<!-- 可选排序 -->
|
||||||
|
<!-- <if test="basinNames != null and basinNames.size() > 0">-->
|
||||||
|
<!-- AND (-->
|
||||||
|
<!-- <foreach collection="basinNames" item="name" separator=" OR ">-->
|
||||||
|
<!-- INSTR(ba.BASIN_NAMES, #{name}) > 0-->
|
||||||
|
<!-- </foreach>-->
|
||||||
|
<!-- )-->
|
||||||
|
<!-- </if>-->
|
||||||
|
<!-- <if test="stationNames != null and stationNames.size() > 0">-->
|
||||||
|
<!-- AND (-->
|
||||||
|
<!-- <foreach collection="stationNames" item="name" separator=" OR ">-->
|
||||||
|
<!-- INSTR(sa.STATION_NAMES, #{name}) > 0-->
|
||||||
|
<!-- </foreach>-->
|
||||||
|
<!-- )-->
|
||||||
|
<!-- </if>-->
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="queryStatistics" resultMap="StatisticsResultMap">
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT
|
||||||
|
t.*,
|
||||||
|
ROW_NUMBER() OVER (ORDER BY t.MIN_BASIN_CODE,t.MIN_STATION_CODE,t.REPORT_MONTH,t.MIN_STRDT) AS rn
|
||||||
|
FROM (
|
||||||
|
<include refid="statisticsQuery"/>
|
||||||
|
) t ORDER BY t.MIN_BASIN_CODE,t.MIN_STATION_CODE,t.REPORT_MONTH,t.MIN_STRDT
|
||||||
|
) WHERE rn BETWEEN #{startRow}+1 AND #{endRow}
|
||||||
|
</select>
|
||||||
|
<select id="countStatistics" resultType="int">
|
||||||
|
SELECT COUNT(*) FROM (
|
||||||
|
<include refid="statisticsQuery"/>
|
||||||
|
)
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.yfd.platform.data.mapper.ImportTaskRowMapper">
|
||||||
|
|
||||||
|
<resultMap id="BaseResultMap" type="com.yfd.platform.data.domain.ImportTaskRow">
|
||||||
|
<id column="ID" property="id"/>
|
||||||
|
<result column="TASK_ID" property="taskId"/>
|
||||||
|
<result column="ROW_INDEX" property="rowIndex"/>
|
||||||
|
<result column="STATUS" property="status"/>
|
||||||
|
<result column="DATA_ID" property="dataId"/>
|
||||||
|
<result column="STCD" property="stcd"/>
|
||||||
|
<result column="STNM" property="stnm"/>
|
||||||
|
<result column="RSTCD" property="rstcd"/>
|
||||||
|
<result column="ENNM" property="ennm"/>
|
||||||
|
<result column="HBRVCD" property="hbrvcd"/>
|
||||||
|
<result column="HBRVNM" property="hbrvnm"/>
|
||||||
|
<result column="RVCD" property="rvcd"/>
|
||||||
|
<result column="BASE_ID" property="baseId"/>
|
||||||
|
<result column="BASE_NAME" property="baseName"/>
|
||||||
|
<result column="STRDT" property="strdt"/>
|
||||||
|
<result column="STRDT_STR" property="strdtStr"/>
|
||||||
|
<result column="ENDDT" property="enddt"/>
|
||||||
|
<result column="FTP" property="ftp"/>
|
||||||
|
<result column="FTP_NAME" property="ftpName"/>
|
||||||
|
<result column="ISFS" property="isfs"/>
|
||||||
|
<result column="DIRECTION" property="direction"/>
|
||||||
|
<result column="FCNT" property="fcnt"/>
|
||||||
|
<result column="FSZ" property="fsz"/>
|
||||||
|
<result column="FWET" property="fwet"/>
|
||||||
|
<result column="WT" property="wt"/>
|
||||||
|
<result column="PICPTH" property="picpth"/>
|
||||||
|
<result column="VDPTH" property="vdpth"/>
|
||||||
|
<result column="TM" property="tm"/>
|
||||||
|
<result column="SOURCE_TYPE" property="sourceType"/>
|
||||||
|
<result column="MOUTH" property="mouth"/>
|
||||||
|
<result column="YR" property="yr"/>
|
||||||
|
<result column="WARNINGS" property="warnings"/>
|
||||||
|
<result column="UNRECOGNIZED_FIELDS" property="unrecognizedFields"/>
|
||||||
|
<result column="VDPTH_LIST_JSON" property="vdpthListJson"/>
|
||||||
|
<result column="PICPTH_LIST_JSON" property="picpthListJson"/>
|
||||||
|
<result column="VDPTH_WARNINGS" property="vdpthWarnings"/>
|
||||||
|
<result column="PICPTH_WARNINGS" property="picpthWarnings"/>
|
||||||
|
<result column="CREATED_AT" property="createdAt"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="selectByTaskId" resultMap="BaseResultMap">
|
||||||
|
SELECT * FROM IMPORT_TASK_ROW
|
||||||
|
WHERE TASK_ID = #{taskId}
|
||||||
|
ORDER BY ROW_INDEX
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectByTaskIdAndStatus" resultMap="BaseResultMap">
|
||||||
|
SELECT * FROM IMPORT_TASK_ROW
|
||||||
|
WHERE TASK_ID = #{taskId}
|
||||||
|
AND STATUS = #{status}
|
||||||
|
ORDER BY ROW_INDEX
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<delete id="deleteByTaskId">
|
||||||
|
DELETE FROM IMPORT_TASK_ROW WHERE TASK_ID = #{taskId}
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@ -72,4 +72,67 @@
|
|||||||
ORDER BY CREATED_AT DESC
|
ORDER BY CREATED_AT DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectValidPermissionsWithName" resultMap="BaseResultMap">
|
||||||
|
SELECT
|
||||||
|
s.ID,
|
||||||
|
s.USER_ID,
|
||||||
|
s.ORG_TYPE,
|
||||||
|
s.ORG_ID,
|
||||||
|
s.PARENT_ID,
|
||||||
|
s.ORG_LEVEL,
|
||||||
|
s.PATH,
|
||||||
|
s.PERMISSION_TYPE,
|
||||||
|
s.ROLE_CODE,
|
||||||
|
s.DATA_SCOPE,
|
||||||
|
s.STATUS,
|
||||||
|
s.START_TIME,
|
||||||
|
s.END_TIME,
|
||||||
|
s.CREATED_AT,
|
||||||
|
s.CREATED_BY,
|
||||||
|
s.UPDATED_AT,
|
||||||
|
s.UPDATED_BY,
|
||||||
|
s.REMARK,
|
||||||
|
COALESCE(e.ennm, h.hbrvnm, b.basename, r.rvnm) AS orgName
|
||||||
|
FROM SYS_USER_DATA_SCOPE s
|
||||||
|
LEFT JOIN SD_ENGINFO_B_H e ON s.ORG_TYPE = 'STATION' AND s.ORG_ID = e.stcd
|
||||||
|
LEFT JOIN SD_HBRV_DIC h ON s.ORG_TYPE = 'HBRVCD' AND s.ORG_ID = h.hbrvcd
|
||||||
|
LEFT JOIN SD_HYDROBASE b ON s.ORG_TYPE = 'BASE' AND s.ORG_ID = b.baseid
|
||||||
|
LEFT JOIN SD_RVCD_DIC r ON s.ORG_TYPE = 'RVCD' AND s.ORG_ID = r.rvcd
|
||||||
|
WHERE s.USER_ID = #{userId}
|
||||||
|
AND s.STATUS = 1
|
||||||
|
ORDER BY s.CREATED_AT DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectValidPermissionsWithNameByUserIds" resultMap="BaseResultMap">
|
||||||
|
SELECT
|
||||||
|
s.ID,
|
||||||
|
s.USER_ID,
|
||||||
|
s.ORG_TYPE,
|
||||||
|
s.ORG_ID,
|
||||||
|
s.PARENT_ID,
|
||||||
|
s.ORG_LEVEL,
|
||||||
|
s.PATH,
|
||||||
|
s.PERMISSION_TYPE,
|
||||||
|
s.ROLE_CODE,
|
||||||
|
s.DATA_SCOPE,
|
||||||
|
s.STATUS,
|
||||||
|
s.START_TIME,
|
||||||
|
s.END_TIME,
|
||||||
|
s.CREATED_AT,
|
||||||
|
s.CREATED_BY,
|
||||||
|
s.UPDATED_AT,
|
||||||
|
s.UPDATED_BY,
|
||||||
|
s.REMARK,
|
||||||
|
COALESCE(e.ENNM, h.HBRVNM) AS orgName
|
||||||
|
FROM SYS_USER_DATA_SCOPE s
|
||||||
|
LEFT JOIN SD_ENGINFO_B_H e ON s.ORG_TYPE = 'STATION' AND s.ORG_ID = e.stcd
|
||||||
|
LEFT JOIN SD_HBRV_DIC h ON s.ORG_TYPE = 'HBRVCD' AND s.ORG_ID = h.hbrvcd
|
||||||
|
WHERE s.USER_ID IN
|
||||||
|
<foreach collection="userIds" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
AND s.STATUS = 1
|
||||||
|
ORDER BY s.CREATED_AT DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@ -231,6 +231,23 @@
|
|||||||
<result column="VLSRTM" property="vlsrTm"/>
|
<result column="VLSRTM" property="vlsrTm"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
<resultMap id="StationBasinInfoResultMap" type="com.yfd.platform.env.domain.StationBasinInfo">
|
||||||
|
<result column="STCD" property="stcd"/>
|
||||||
|
<result column="ENNM" property="ennm"/>
|
||||||
|
<result column="HBRVCD" property="hbrvcd"/>
|
||||||
|
<result column="HBRVNM" property="hbrvnm"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="selectStationBasinInfo" resultMap="StationBasinInfoResultMap">
|
||||||
|
SELECT
|
||||||
|
e.STCD,
|
||||||
|
e.ENNM,
|
||||||
|
e.HBRVCD,
|
||||||
|
h.HBRVNM
|
||||||
|
FROM SD_ENGINFO_B_H e
|
||||||
|
LEFT JOIN SD_HBRV_DIC h ON e.HBRVCD = h.HBRVCD
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="selectByBaseId" resultMap="BaseResultMap">
|
<select id="selectByBaseId" resultMap="BaseResultMap">
|
||||||
SELECT * FROM SD_ENGINFO_B_H
|
SELECT * FROM SD_ENGINFO_B_H
|
||||||
WHERE BASE_ID = #{baseId}
|
WHERE BASE_ID = #{baseId}
|
||||||
|
|||||||
@ -133,6 +133,37 @@
|
|||||||
</where>
|
</where>
|
||||||
ORDER BY r."LEVEL" ASC, lastmodifydate ASC
|
ORDER BY r."LEVEL" ASC, lastmodifydate ASC
|
||||||
</select>
|
</select>
|
||||||
|
<!-- SysRoleMapper.xml -->
|
||||||
|
<select id="getRolesByUserIds" resultType="com.yfd.platform.system.domain.SysRole">
|
||||||
|
SELECT r.*
|
||||||
|
FROM SYS_ROLE r
|
||||||
|
INNER JOIN SYS_USER_ROLE ur ON r.ID = ur.ROLE_ID
|
||||||
|
WHERE ur.USER_ID IN
|
||||||
|
<foreach collection="userIds" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="getUserRolesByUserIds" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
ur.USERID AS "userId",
|
||||||
|
r.ID AS "id",
|
||||||
|
r.ROLECODE AS "rolecode",
|
||||||
|
r.ROLENAME AS "rolename",
|
||||||
|
r."LEVEL" AS "level",
|
||||||
|
r.DESCRIPTION AS "description",
|
||||||
|
r.ISVAILD AS "isvaild",
|
||||||
|
r.ORGSCOPE AS "orgscope",
|
||||||
|
r.OPTSCOPE AS "optscope",
|
||||||
|
r.BUSSCOPE AS "busscope"
|
||||||
|
FROM
|
||||||
|
SYS_ROLE r
|
||||||
|
INNER JOIN SYS_ROLE_USERS ur ON r.ID = ur.ROLEID
|
||||||
|
WHERE ur.USERID IN
|
||||||
|
<foreach collection="userIds" item="id" open="(" separator="," close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
<!--根据 角色id和用户id 删除系统角色用户对照 (admin除外)-->
|
<!--根据 角色id和用户id 删除系统角色用户对照 (admin除外)-->
|
||||||
<delete id="deleteRoleUsers">
|
<delete id="deleteRoleUsers">
|
||||||
delete from sys_role_users where userid !=(select u.id from sys_user u where u.account="admin") and roleid=#{roleid} and userid=#{urserid}
|
delete from sys_role_users where userid !=(select u.id from sys_user u where u.account="admin") and roleid=#{roleid} and userid=#{urserid}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user