From 8cc321bf14756638d226619dd6264d33c7fc69d8 Mon Sep 17 00:00:00 2001 From: gaoyutao Date: Tue, 10 Feb 2026 18:19:22 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=A2=9E=E5=8A=A0=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E5=88=9D=E5=A7=8B=E6=A8=A1=E5=9D=97=202?= =?UTF-8?q?=E3=80=81=E4=BC=98=E5=8C=96=E5=87=BA=E7=9C=81=E6=B5=81=E9=87=8F?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E5=B1=95=E7=A4=BA=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tongran-modules/pom.xml | 1 + tongran-modules/tongran-storage/pom.xml | 92 +++++ .../storage/TongRanStorageApplication.java | 20 ++ .../com/tongran/storage/config/S3Config.java | 43 +++ .../storage/config/StorageProperties.java | 21 ++ .../storage/controller/FileController.java | 67 ++++ .../com/tongran/storage/dto/FileInfo.java | 12 + .../com/tongran/storage/dto/UploadResult.java | 29 ++ .../storage/service/StorageService.java | 16 + .../service/impl/S3StorageServiceImpl.java | 325 ++++++++++++++++++ .../src/main/resources/banner.txt | 2 + .../src/main/resources/bootstrap.yml | 33 ++ .../src/main/resources/logback.xml | 74 ++++ .../RmResourceRegistrationServiceImpl.java | 2 + ...RmOutboundTrafficStatisticsController.java | 11 +- .../domain/RmOutboundTrafficStatistics.java | 8 + .../rocketmq/handler/MessageHandler.java | 15 +- .../RmOutboundTrafficStatisticsMapper.xml | 33 +- 18 files changed, 780 insertions(+), 24 deletions(-) create mode 100644 tongran-modules/tongran-storage/pom.xml create mode 100644 tongran-modules/tongran-storage/src/main/java/com/tongran/storage/TongRanStorageApplication.java create mode 100644 tongran-modules/tongran-storage/src/main/java/com/tongran/storage/config/S3Config.java create mode 100644 tongran-modules/tongran-storage/src/main/java/com/tongran/storage/config/StorageProperties.java create mode 100644 tongran-modules/tongran-storage/src/main/java/com/tongran/storage/controller/FileController.java create mode 100644 tongran-modules/tongran-storage/src/main/java/com/tongran/storage/dto/FileInfo.java create mode 100644 tongran-modules/tongran-storage/src/main/java/com/tongran/storage/dto/UploadResult.java create mode 100644 tongran-modules/tongran-storage/src/main/java/com/tongran/storage/service/StorageService.java create mode 100644 tongran-modules/tongran-storage/src/main/java/com/tongran/storage/service/impl/S3StorageServiceImpl.java create mode 100644 tongran-modules/tongran-storage/src/main/resources/banner.txt create mode 100644 tongran-modules/tongran-storage/src/main/resources/bootstrap.yml create mode 100644 tongran-modules/tongran-storage/src/main/resources/logback.xml diff --git a/tongran-modules/pom.xml b/tongran-modules/pom.xml index 30b5e99..60057b1 100644 --- a/tongran-modules/pom.xml +++ b/tongran-modules/pom.xml @@ -14,6 +14,7 @@ tongran-job tongran-file tongran-mtragent + tongran-storage tongran-modules diff --git a/tongran-modules/tongran-storage/pom.xml b/tongran-modules/tongran-storage/pom.xml new file mode 100644 index 0000000..d820776 --- /dev/null +++ b/tongran-modules/tongran-storage/pom.xml @@ -0,0 +1,92 @@ + + + + com.tongran + tongran-modules + 3.6.6 + + 4.0.0 + + tongran-modules-storage + + + tongran-modules-storage对象存储 + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + org.springframework.boot + spring-boot-starter-web + + + + + com.github.tobato + fastdfs-client + + + + + io.minio + minio + ${minio.version} + + + + + com.tongran + tongran-api-system + + + software.amazon.awssdk + s3 + 2.25.27 + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + \ No newline at end of file diff --git a/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/TongRanStorageApplication.java b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/TongRanStorageApplication.java new file mode 100644 index 0000000..3b3fefa --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/TongRanStorageApplication.java @@ -0,0 +1,20 @@ +package com.tongran.storage; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 对象存储服务 + * + * @author tongran + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) +public class TongRanStorageApplication +{ + public static void main(String[] args) + { + SpringApplication.run(TongRanStorageApplication.class, args); + System.out.println("对象存储服务模块启动成功"); + } +} diff --git a/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/config/S3Config.java b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/config/S3Config.java new file mode 100644 index 0000000..0ce7267 --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/config/S3Config.java @@ -0,0 +1,43 @@ +package com.tongran.storage.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; + +import java.net.URI; + +@Configuration +@RequiredArgsConstructor +public class S3Config { + + private final StorageProperties storageProperties; + + @Bean + public S3Client s3Client() { + return S3Client.builder() + .endpointOverride(URI.create(storageProperties.getEndpoint())) + .credentialsProvider(StaticCredentialsProvider.create( + AwsBasicCredentials.create( + storageProperties.getAccessKey(), + storageProperties.getSecretKey() + ) + )) + .region(Region.of(storageProperties.getRegion())) + .build(); + } + // 在您的配置类中添加 + @Bean + public S3Presigner s3Presigner(StorageProperties properties) { + return S3Presigner.builder() + .endpointOverride(URI.create(properties.getEndpoint())) + .region(Region.of(storageProperties.getRegion())) + .credentialsProvider(StaticCredentialsProvider.create( + AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey()))) + .build(); + } +} \ No newline at end of file diff --git a/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/config/StorageProperties.java b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/config/StorageProperties.java new file mode 100644 index 0000000..42c2d49 --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/config/StorageProperties.java @@ -0,0 +1,21 @@ +package com.tongran.storage.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.util.unit.DataSize; + +@Data +@Component +@ConfigurationProperties(prefix = "storage.rustfs") +public class StorageProperties { + private String endpoint = "http://localhost:9000"; + private String accessKey = "minioadmin"; + private String secretKey = "minioadmin"; + private String bucketName = "tongran-bucket"; + private String region = "us-east-1"; + // 新增配置 + private DataSize maxTotalSize = DataSize.ofGigabytes(40); // 总空间限制 + private DataSize chunkSize = DataSize.ofMegabytes(10); // 分片大小 + private int maxConcurrentParts = 4; // 并发上传分片数 +} \ No newline at end of file diff --git a/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/controller/FileController.java b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/controller/FileController.java new file mode 100644 index 0000000..f0af4c4 --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/controller/FileController.java @@ -0,0 +1,67 @@ +package com.tongran.storage.controller; + +import com.tongran.storage.dto.FileInfo; +import com.tongran.storage.dto.UploadResult; +import com.tongran.storage.service.StorageService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@RestController +@RequestMapping("/storage") +@RequiredArgsConstructor +public class FileController { + + private final StorageService storageService; + + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity uploadFile(@RequestParam("file") MultipartFile file) { + UploadResult result = storageService.uploadFile(file); + if (result.isSuccess()) { + return ResponseEntity.ok(result); + } else { + return ResponseEntity.badRequest().body(result); + } + } + + @GetMapping("/download/{fileName}") + public ResponseEntity downloadFile(@PathVariable String fileName) { + byte[] fileBytes = storageService.downloadFile(fileName); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename=\"" + fileName + "\"") + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(fileBytes); + } + + @DeleteMapping("/{fileName}") + public ResponseEntity deleteFile(@PathVariable String fileName) { + storageService.deleteFile(fileName); + return ResponseEntity.ok("文件删除成功: " + fileName); + } + + @GetMapping("/list") + public ResponseEntity> listFiles() { + List files = storageService.listFiles(); + return ResponseEntity.ok(files); + } + @GetMapping("/createBucket") + public ResponseEntity createBucket(String bucketName) { + boolean success = storageService.createBucket(bucketName); + if(success){ + return ResponseEntity.ok("创建成功"); + } + return ResponseEntity.ok("存储桶已存在"); + } + @GetMapping("/generateTempUrl") + public ResponseEntity generateTempUrl(String fileName) { + String url = storageService.generateTempUrl(fileName); + return ResponseEntity.ok(url); + } +} \ No newline at end of file diff --git a/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/dto/FileInfo.java b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/dto/FileInfo.java new file mode 100644 index 0000000..55f3394 --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/dto/FileInfo.java @@ -0,0 +1,12 @@ +package com.tongran.storage.dto; + +import lombok.Data; +import java.time.Instant; + +@Data +public class FileInfo { + private String fileName; + private Long fileSize; + private Instant lastModified; + private String etag; +} \ No newline at end of file diff --git a/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/dto/UploadResult.java b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/dto/UploadResult.java new file mode 100644 index 0000000..01f4e6f --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/dto/UploadResult.java @@ -0,0 +1,29 @@ +package com.tongran.storage.dto; + +import lombok.Data; + +@Data +public class UploadResult { + private boolean success; + private String fileName; + private String fileUrl; + private Long fileSize; + private String message; + + public static UploadResult success(String fileName, String fileUrl, Long fileSize) { + UploadResult result = new UploadResult(); + result.setSuccess(true); + result.setFileName(fileName); + result.setFileUrl(fileUrl); + result.setFileSize(fileSize); + result.setMessage("上传成功"); + return result; + } + + public static UploadResult error(String message) { + UploadResult result = new UploadResult(); + result.setSuccess(false); + result.setMessage(message); + return result; + } +} \ No newline at end of file diff --git a/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/service/StorageService.java b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/service/StorageService.java new file mode 100644 index 0000000..6c7b958 --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/service/StorageService.java @@ -0,0 +1,16 @@ +package com.tongran.storage.service; + +import com.tongran.storage.dto.FileInfo; +import com.tongran.storage.dto.UploadResult; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +public interface StorageService { + UploadResult uploadFile(MultipartFile file); + byte[] downloadFile(String fileName); + void deleteFile(String fileName); + List listFiles(); + public String generateTempUrl(String fileName); + public boolean createBucket(String bucketName); +} \ No newline at end of file diff --git a/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/service/impl/S3StorageServiceImpl.java b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/service/impl/S3StorageServiceImpl.java new file mode 100644 index 0000000..a5f2a67 --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/java/com/tongran/storage/service/impl/S3StorageServiceImpl.java @@ -0,0 +1,325 @@ +// S3StorageServiceImpl.java +package com.tongran.storage.service.impl; + +import com.tongran.storage.config.StorageProperties; +import com.tongran.storage.dto.FileInfo; +import com.tongran.storage.dto.UploadResult; +import com.tongran.storage.service.StorageService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.*; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; + +import java.io.IOException; +import java.io.InputStream; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class S3StorageServiceImpl implements StorageService { + + private final S3Client s3Client; + private final StorageProperties storageProperties; + private final S3Presigner s3Presigner; + + @Override + public UploadResult uploadFile(MultipartFile file) { + try { + // 1. 生成文件名 + String originalName = file.getOriginalFilename(); + String extension = getFileExtension(originalName); + String fileName = UUID.randomUUID().toString() + + (extension != null ? "." + extension : ""); + + // 2. 自动判断:小文件直接传,大文件自动分片 + if (file.getSize() <= storageProperties.getChunkSize().toBytes()) { + // 小文件:直接上传 + return uploadDirect(file, fileName); + } else { + // 大文件:自动分片上传 + return uploadWithChunks(file, fileName); + } + + } catch (Exception e) { + log.error("文件上传失败", e); + return UploadResult.error("上传失败: " + e.getMessage()); + } + } + + // 小文件直接上传 + private UploadResult uploadDirect(MultipartFile file, String fileName) throws IOException { + s3Client.putObject(b -> b + .bucket(storageProperties.getBucketName()) + .key(fileName) + .contentType(file.getContentType()), + RequestBody.fromBytes(file.getBytes()) + ); + + String fileUrl = buildFileUrl(fileName); + log.info("小文件直接上传成功: {} ({} bytes)", fileName, file.getSize()); + + return UploadResult.success(fileName, fileUrl, file.getSize()); + } + + // 大文件自动分片上传 + private UploadResult uploadWithChunks(MultipartFile file, String fileName) throws IOException { + long fileSize = file.getSize(); + long chunkSize = storageProperties.getChunkSize().toBytes(); // 比如10MB + String contentType = file.getContentType(); + String bucket = storageProperties.getBucketName(); + + // 1. 自动初始化分片上传 + String uploadId = s3Client.createMultipartUpload(b -> b + .bucket(bucket) + .key(fileName) + .contentType(contentType) + ).uploadId(); + + log.info("开始分片上传: {} ({} bytes, 分片大小: {} bytes)", + fileName, fileSize, chunkSize); + + try (InputStream inputStream = file.getInputStream()) { + List completedParts = new ArrayList<>(); + int partNumber = 1; + long totalUploaded = 0; + + // 2. 自动读取分片并上传 + byte[] buffer = new byte[(int) chunkSize]; + int bytesRead; + + while ((bytesRead = inputStream.read(buffer)) > 0) { + byte[] chunkBytes = Arrays.copyOf(buffer, bytesRead); + + // 上传单个分片 + String etag = uploadPart(uploadId, fileName, partNumber, chunkBytes, bucket); + + completedParts.add(CompletedPart.builder() + .partNumber(partNumber) + .eTag(etag) + .build()); + + totalUploaded += bytesRead; + partNumber++; + + // 可以在这里记录进度 + log.debug("上传进度: {}/{} bytes ({}%)", + totalUploaded, fileSize, + (totalUploaded * 100 / fileSize)); + } + + // 3. 自动完成上传 + s3Client.completeMultipartUpload(b -> b + .bucket(bucket) + .key(fileName) + .uploadId(uploadId) + .multipartUpload(CompletedMultipartUpload.builder() + .parts(completedParts) + .build()) + ); + + String fileUrl = buildFileUrl(fileName); + log.info("分片上传完成: {} (共{}个分片)", fileName, completedParts.size()); + + return UploadResult.success(fileName, fileUrl, fileSize); + + } catch (Exception e) { + // 出错时自动取消上传 + try { + s3Client.abortMultipartUpload(b -> b + .bucket(bucket) + .key(fileName) + .uploadId(uploadId) + ); + log.warn("上传失败,已取消分片上传: {}", uploadId); + } catch (Exception ex) { + log.error("取消分片上传失败", ex); + } + throw e; + } + } + + // 上传单个分片(辅助方法) + private String uploadPart(String uploadId, String fileName, + int partNumber, byte[] chunkData, String bucket) { + UploadPartRequest uploadRequest = UploadPartRequest.builder() + .bucket(bucket) + .key(fileName) + .uploadId(uploadId) + .partNumber(partNumber) + .build(); + + UploadPartResponse response = s3Client.uploadPart( + uploadRequest, + RequestBody.fromBytes(chunkData) + ); + + return response.eTag(); + } + + @Override + public byte[] downloadFile(String fileName) { + try { + GetObjectRequest getObjectRequest = GetObjectRequest.builder() + .bucket(storageProperties.getBucketName()) + .key(fileName) + .build(); + + return s3Client.getObjectAsBytes(getObjectRequest).asByteArray(); + + } catch (S3Exception e) { + log.error("下载文件失败: {}", fileName, e); + throw new RuntimeException("下载文件失败: " + e.getMessage()); + } + } + + @Override + public void deleteFile(String fileName) { + try { + DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder() + .bucket(storageProperties.getBucketName()) + .key(fileName) + .build(); + + s3Client.deleteObject(deleteObjectRequest); + log.info("文件删除成功: {}", fileName); + + } catch (S3Exception e) { + log.error("删除文件失败: {}", fileName, e); + throw new RuntimeException("删除文件失败: " + e.getMessage()); + } + } + + @Override + public List listFiles() { + try { + ListObjectsV2Request request = ListObjectsV2Request.builder() + .bucket(storageProperties.getBucketName()) + .build(); + + ListObjectsV2Response response = s3Client.listObjectsV2(request); + + return response.contents().stream() + .map(s3Object -> { + FileInfo fileInfo = new FileInfo(); + fileInfo.setFileName(s3Object.key()); + fileInfo.setFileSize(s3Object.size()); + fileInfo.setLastModified(s3Object.lastModified()); + fileInfo.setEtag(s3Object.eTag()); + return fileInfo; + }) + .collect(Collectors.toList()); + + } catch (S3Exception e) { + log.error("获取文件列表失败", e); + throw new RuntimeException("获取文件列表失败: " + e.getMessage()); + } + } + + private String getFileExtension(String fileName) { + if (fileName == null || !fileName.contains(".")) { + return null; + } + return fileName.substring(fileName.lastIndexOf(".") + 1); + } + private String buildFileUrl(String fileName) { + return String.format("%s/%s/%s", + storageProperties.getEndpoint(), + storageProperties.getBucketName(), + fileName); + } + /** + * 生成24小时有效的临时下载链接 + * @param fileName 文件名 + * @return 临时访问URL + */ + @Override + public String generateTempUrl(String fileName) { + try { + GetObjectRequest getObjectRequest = GetObjectRequest.builder() + .bucket(storageProperties.getBucketName()) + .key(fileName) + .build(); + + GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder() + .getObjectRequest(getObjectRequest) + .signatureDuration(Duration.ofHours(24)) // 24小时有效 + .build(); + + PresignedGetObjectRequest presignedRequest = s3Presigner.presignGetObject(presignRequest); + String url = presignedRequest.url().toString(); + + log.info("生成临时访问链接: {} (24小时有效)", fileName); + return url; + + } catch (S3Exception e) { + log.error("生成临时链接失败: {}", fileName, e); + throw new RuntimeException("生成临时链接失败: " + e.getMessage()); + } + } + + /** + * 创建新的存储桶 + * @param bucketName 存储桶名称 + * @return 是否创建成功 + */ + @Override + public boolean createBucket(String bucketName) { + try { + // 检查存储桶是否已存在 + if (isBucketExist(bucketName)) { + log.warn("存储桶已存在: {}", bucketName); + return false; + } + + // 创建存储桶 + CreateBucketRequest createBucketRequest = CreateBucketRequest.builder() + .bucket(bucketName) + .createBucketConfiguration(b -> b + .locationConstraint(Region.of(storageProperties.getRegion()).id()) + ) + .build(); + + s3Client.createBucket(createBucketRequest); + log.info("存储桶创建成功: {}", bucketName); + return true; + + } catch (S3Exception e) { + log.error("创建存储桶失败: {}", bucketName, e); + throw new RuntimeException("创建存储桶失败: " + e.getMessage()); + } + } + + /** + * 检查存储桶是否存在 + * @param bucketName 存储桶名称 + * @return 是否存在 + */ + private boolean isBucketExist(String bucketName) { + try { + HeadBucketRequest headBucketRequest = HeadBucketRequest.builder() + .bucket(bucketName) + .build(); + + s3Client.headBucket(headBucketRequest); + return true; + } catch (NoSuchBucketException e) { + return false; + } catch (S3Exception e) { + log.error("检查存储桶状态失败: {}", bucketName, e); + throw new RuntimeException("检查存储桶状态失败: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/tongran-modules/tongran-storage/src/main/resources/banner.txt b/tongran-modules/tongran-storage/src/main/resources/banner.txt new file mode 100644 index 0000000..591c451 --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/resources/banner.txt @@ -0,0 +1,2 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} \ No newline at end of file diff --git a/tongran-modules/tongran-storage/src/main/resources/bootstrap.yml b/tongran-modules/tongran-storage/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..15d53ef --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/resources/bootstrap.yml @@ -0,0 +1,33 @@ +# Tomcat +server: + port: 9305 + +# Spring +spring: + application: + # 应用名称 + name: tongran-storage + profiles: + # 环境配置 + active: dev + cloud: + nacos: + discovery: + # 服务注册地址 + server-addr: ${spring.cloud.nacos.config.server-addr} + namespace: ${spring.cloud.nacos.config.namespace} + username: ${spring.cloud.nacos.config.username} + password: ${spring.cloud.nacos.config.password} + config: + # 配置中心地址 + server-addr: 172.16.15.52:8848 +# server-addr: 172.16.15.103:8848 +# namespace: public + namespace: saas-prod + username: nacos + password: nacos + # 配置文件格式 + file-extension: yml + # 共享配置 + shared-configs: + - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} diff --git a/tongran-modules/tongran-storage/src/main/resources/logback.xml b/tongran-modules/tongran-storage/src/main/resources/logback.xml new file mode 100644 index 0000000..5c12882 --- /dev/null +++ b/tongran-modules/tongran-storage/src/main/resources/logback.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/info.log + + + + ${log.path}/info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/error.log + + + + ${log.path}/error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tongran-modules/tongran-system/src/main/java/com/tongran/system/service/impl/RmResourceRegistrationServiceImpl.java b/tongran-modules/tongran-system/src/main/java/com/tongran/system/service/impl/RmResourceRegistrationServiceImpl.java index 4f1e9ef..1f33862 100644 --- a/tongran-modules/tongran-system/src/main/java/com/tongran/system/service/impl/RmResourceRegistrationServiceImpl.java +++ b/tongran-modules/tongran-system/src/main/java/com/tongran/system/service/impl/RmResourceRegistrationServiceImpl.java @@ -142,6 +142,7 @@ public class RmResourceRegistrationServiceImpl implements IRmResourceRegistratio .collect(Collectors.toList()); } if(!childData.isEmpty()){ + batchSetNetWorkMsg(childData); filteredList.addAll(childData); } // 按 clientId 去重(保留第一个出现的元素) @@ -237,6 +238,7 @@ public class RmResourceRegistrationServiceImpl implements IRmResourceRegistratio .collect(Collectors.toList()); } if(!childData.isEmpty()){ + batchSetNetWorkMsg(childData); filteredList.addAll(childData); } // 按 clientId 去重(保留第一个出现的元素) diff --git a/tongran-rocketmq/src/main/java/com/tongran/rocketmq/controller/RmOutboundTrafficStatisticsController.java b/tongran-rocketmq/src/main/java/com/tongran/rocketmq/controller/RmOutboundTrafficStatisticsController.java index a5d0629..f9f6094 100644 --- a/tongran-rocketmq/src/main/java/com/tongran/rocketmq/controller/RmOutboundTrafficStatisticsController.java +++ b/tongran-rocketmq/src/main/java/com/tongran/rocketmq/controller/RmOutboundTrafficStatisticsController.java @@ -1,7 +1,8 @@ package com.tongran.rocketmq.controller; import com.tongran.common.core.web.controller.BaseController; -import com.tongran.common.core.web.domain.AjaxResult; +import com.tongran.common.core.web.page.PageDomain; +import com.tongran.common.core.web.page.TableDataInfo; import com.tongran.common.security.annotation.RequiresPermissions; import com.tongran.rocketmq.domain.RmOutboundTrafficStatistics; import com.tongran.rocketmq.service.IRmOutboundTrafficStatisticsService; @@ -31,10 +32,14 @@ public class RmOutboundTrafficStatisticsController extends BaseController * 查询出省流量统计列表 */ @PostMapping("/list") - public AjaxResult list(@RequestBody RmOutboundTrafficStatistics rmOutboundTrafficStatistics) + public TableDataInfo list(@RequestBody RmOutboundTrafficStatistics rmOutboundTrafficStatistics) { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(rmOutboundTrafficStatistics.getPageNum()); + pageDomain.setPageSize(rmOutboundTrafficStatistics.getPageSize()); + startPage(pageDomain); List list = rmOutboundTrafficStatisticsService.selectRmOutboundTrafficStatisticsList(rmOutboundTrafficStatistics); - return success(list); + return getDataTable(list); } } diff --git a/tongran-rocketmq/src/main/java/com/tongran/rocketmq/domain/RmOutboundTrafficStatistics.java b/tongran-rocketmq/src/main/java/com/tongran/rocketmq/domain/RmOutboundTrafficStatistics.java index 8564d98..8b97701 100644 --- a/tongran-rocketmq/src/main/java/com/tongran/rocketmq/domain/RmOutboundTrafficStatistics.java +++ b/tongran-rocketmq/src/main/java/com/tongran/rocketmq/domain/RmOutboundTrafficStatistics.java @@ -4,6 +4,8 @@ import com.tongran.common.core.annotation.Excel; import com.tongran.common.core.web.domain.BaseEntity; import lombok.Data; +import java.math.BigDecimal; + /** * 出省流量统计对象 rm_outbound_traffic_statistics * @@ -25,5 +27,11 @@ public class RmOutboundTrafficStatistics extends BaseEntity /** 出省流量统计详情 */ @Excel(name = "出省流量统计详情") private String description; + /** 总占比 */ + private BigDecimal totalRate; + /** ipv4占比 */ + private BigDecimal ipv4Rate; + /** ipv6占比 */ + private BigDecimal ipv6Rate; } diff --git a/tongran-rocketmq/src/main/java/com/tongran/rocketmq/handler/MessageHandler.java b/tongran-rocketmq/src/main/java/com/tongran/rocketmq/handler/MessageHandler.java index ffcc5c1..fc68392 100644 --- a/tongran-rocketmq/src/main/java/com/tongran/rocketmq/handler/MessageHandler.java +++ b/tongran-rocketmq/src/main/java/com/tongran/rocketmq/handler/MessageHandler.java @@ -43,7 +43,6 @@ import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.math.BigDecimal; import java.math.RoundingMode; -import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.*; import java.util.function.Consumer; @@ -251,11 +250,6 @@ public class MessageHandler { BigDecimal v4TotalRate = BigDecimal.ZERO; BigDecimal v6TotalRate = BigDecimal.ZERO; BigDecimal totalRate = BigDecimal.ZERO; - // 添加时间戳 - content.append("时间:").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append("\n"); - int insertPosition = content.length(); - int insertPositionV6 = content.length(); - // 处理IPv4统计 if (!ipTypeMap.get("IPv4").isEmpty()) { content.append("##### V4 统计 ##### ##\n"); @@ -296,8 +290,6 @@ public class MessageHandler { v4TotalRate = v4TotalRate.add(percentage); } // 在记录的位置插入总计信息 - content.insert(insertPosition, "IPv4总计占比: " + v4TotalRate + "%\n"); - insertPositionV6 += ("IPv4总计占比: " + v4TotalRate + "%\n").length(); // 更新插入位置 totalRate = totalRate.add(v4TotalRate); } @@ -340,12 +332,8 @@ public class MessageHandler { content.append(entry.getKey()).append(": ").append(percentage).append("%\n"); v6TotalRate = v6TotalRate.add(percentage); } - // 在更新后的位置插入IPv6总计 - content.insert(insertPositionV6, "IPv6总计占比: " + v6TotalRate + "%\n"); totalRate = totalRate.add(v6TotalRate); } - // 在更新后的位置插入IPv6总计 - content.insert(insertPosition, "总占比: " + totalRate + "%\n"); // 添加总占比 RmResourceRegistrationRemote updateData = new RmResourceRegistrationRemote(); updateData.setClientId(message.getClientId()); @@ -353,6 +341,9 @@ public class MessageHandler { remoteRevenueConfigService.innerUpdateRegist(updateData, SecurityConstants.INNER); RmOutboundTrafficStatistics insertData = new RmOutboundTrafficStatistics(); insertData.setClientId(message.getClientId()); + insertData.setTotalRate(totalRate); + insertData.setIpv4Rate(v4TotalRate); + insertData.setIpv6Rate(v6TotalRate); insertData.setDescription(content.toString()); rmOutboundTrafficStatisticsService.insertRmOutboundTrafficStatistics(insertData); // 查询告警阈值 diff --git a/tongran-rocketmq/src/main/resources/mapper/rocketmq/RmOutboundTrafficStatisticsMapper.xml b/tongran-rocketmq/src/main/resources/mapper/rocketmq/RmOutboundTrafficStatisticsMapper.xml index e1900fd..90402ac 100644 --- a/tongran-rocketmq/src/main/resources/mapper/rocketmq/RmOutboundTrafficStatisticsMapper.xml +++ b/tongran-rocketmq/src/main/resources/mapper/rocketmq/RmOutboundTrafficStatisticsMapper.xml @@ -1,13 +1,16 @@ + PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + + + + @@ -15,18 +18,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - select id, client_id, description, create_time, update_time, create_by, update_by from rm_outbound_traffic_statistics + select id, client_id, description, total_rate, ipv4_rate, ipv6_rate, create_time, update_time, create_by, update_by from rm_outbound_traffic_statistics - +