问题修改

This commit is contained in:
cwchen 2025-05-08 16:22:59 +08:00
parent 93747055f2
commit f58368f3c8
32 changed files with 847 additions and 1161 deletions

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bonus</groupId>
<artifactId>bonus-modules</artifactId>
<version>24.12.0-SNAPSHOT</version>
</parent>
<groupId>com.bonus.czl</groupId>
<artifactId>bonus-czl</artifactId>
<dependencies>
<!--加密依赖包-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>${jasypt-spring-boot-starter.version}</version>
</dependency>
<!-- SpringCloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringBoot Actuator -->
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>-->
<!-- Mysql Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version> <!-- 建议使用最新稳定版 -->
<scope>runtime</scope> <!-- 通常只需运行时依赖 -->
</dependency>
<!-- bonus Common DataSource -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-datasource</artifactId>
</dependency>
<!-- bonus Common DataScope -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-datascope</artifactId>
</dependency>
<!-- bonus Common Log -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-log</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.0-jre</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<!-- <version>6.0.18</version>-->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,32 @@
package com.bonus.czl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 承载力模块
*
* @author bonus
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
public class BonusCzlApplication
{
public static void main(String[] args)
{
SpringApplication.run(BonusCzlApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 承载力模块启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \n" +
" | ( ' ) | \\ _. / ' \n" +
" |(_ o _) / _( )_ .' \n" +
" | (_,_).' __ ___(_ o _)' \n" +
" | |\\ \\ | || |(_,_)' \n" +
" | | \\ `' /| `-' / \n" +
" | | \\ / \\ / \n" +
" ''-' `'-' `-..-' ");
}
}

View File

@ -0,0 +1,2 @@
Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}

View File

@ -0,0 +1,28 @@
# Tomcat
server:
port: 18086
# Spring
spring:
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 192.168.87.190:8848
namespace: e9abf140-675a-4d55-af3f-1f28a6186904
username: nacos
password: nacos
config:
# 配置中心地址
server-addr: 192.168.87.190:8848
namespace: e9abf140-675a-4d55-af3f-1f28a6186904
username: nacos
password: nacos
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
feign:
hystrix:
enabled: true

View File

@ -0,0 +1,17 @@
# Spring
spring:
application:
# 应用名称
name: bonus-czl
profiles:
# 环境配置
active: dev
#加密组件
jasypt:
encryptor:
password: Encrypt
feign:
hystrix:
enabled: true

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/bonus-czl" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.bonus" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
</configuration>

View File

@ -57,12 +57,6 @@
<artifactId>bonus-api-system</artifactId>
</dependency>
<!-- bonus Common Swagger -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-swagger</artifactId>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
@ -109,6 +103,10 @@
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>${jasypt-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -3,15 +3,15 @@ package com.bonus.file;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import com.bonus.common.swagger.annotation.EnableCustomSwagger2;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* 文件服务
*
* @author bonus
*/
@EnableCustomSwagger2
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
@EnableDiscoveryClient
public class BonusFileApplication
{
public static void main(String[] args)

View File

@ -8,6 +8,7 @@ import com.bonus.file.service.ISysFileService;
import com.bonus.system.api.domain.SysFile;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -29,142 +30,8 @@ import java.net.URLDecoder;
*/
@RestController
@Api("服务文件存储")
@Slf4j
public class SysFileController
{
private static final Logger log = LoggerFactory.getLogger(SysFileController.class);
private final ISysFileService sysFileService;
@Autowired
public SysFileController(ISysFileService fileService) {
this.sysFileService = fileService;
}
/**
* 单文件上传
* @param file 单个文件
* @return 文件信息包括文件名和文件路径
*/
@PostMapping("upload")
@ApiOperation("上传本地文件到服务器")
public AjaxResult upload(MultipartFile file)
{
try
{
// 上传并返回访问地址
if (ObjectUtil.isEmpty(file)) { return AjaxResult.error("上传文件不能为空");}
SysFile sysFile = sysFileService.uploadFile(file);
return AjaxResult.success(sysFile);
}
catch (Exception e)
{
log.error("上传文件失败", e);
return AjaxResult.error(e.getMessage());
}
}
/**
* 多文件上传
* @param files 多个文件流
* @return 文件信息
*/
@PostMapping("/uploadFiles")
public AjaxResult uploadFile(MultipartFile[] files) {
try {
if (ObjectUtil.isEmpty(files)) { return AjaxResult.error("上传文件不能为空");}
return AjaxResult.success(sysFileService.uploadFiles(files));
} catch (Exception e)
{
return AjaxResult.error(e.getMessage());
}
}
/**
* 文件下载
* @param response 请求响应
* @param objectKey除mongodb 存fileid之外其他均存上传文件的网络路径
*/
@GetMapping("/download")
public void downloadFile(HttpServletResponse response, @RequestParam String objectKey) throws IOException {
try {
String fileUrl = Base64Utils.decodeUrl(URLDecoder.decode(objectKey));
sysFileService.downloadFile(response, fileUrl);
} catch (Exception e) {
log.error("downloadFile error:{}", e.getMessage());
response.setStatus(HttpStatus.ERROR);
// 设置响应的 Content-Type 为文本
response.setContentType("application/json;charset=UTF-8");
// 将错误信息写入响应体
PrintWriter writer = response.getWriter();
writer.write("{\"error\": \"File download failed: " + e.getMessage() + "\"}");
writer.flush();
writer.close();
}
}
/**
* 文件删除
* 从各个存储平台删除文件
* @param objectKey * @param objectKey除mongodb 存fileid之外其他均存上传文件的网络路径
*/
@PostMapping("/deleteFile")
public AjaxResult deleteFile(@RequestParam("objectKey") String objectKey) {
try {
String fileUrl = Base64Utils.decodeUrl(URLDecoder.decode(objectKey));
sysFileService.deleteFile(fileUrl);
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
return AjaxResult.success("删除文件成功");
}
/**
* 单文件上传到指定文件夹
* @param file 单个文件
* @return 文件信息包括文件名和文件路径
*/
@PostMapping("uploadToFolder")
@ApiOperation("上传本地文件到服务器")
public AjaxResult upload(MultipartFile file, String folderName)
{
return AjaxResult.success("单文件上传成功");
}
/**
* 创建文件夹
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
* @return 文件夹网络路径
*/
@PostMapping("/createFolder")
@ApiOperation("创建文件夹")
public AjaxResult createFolder(@RequestParam("folderName") String folderPath)
{
sysFileService.createFolder(folderPath);
return AjaxResult.success("创建文件夹");
}
/**
* list 指定目录下所有文件信息文件名文件大小上传时间是否目录包括子文件夹
* @param folderPath 要获取文件夹和文件的父文件夹路径例如 "folder1/"
* @return 文件夹网络路径
*/
@GetMapping("/listFiles")
public AjaxResult listFiles(@RequestParam("folderName") String folderPath) {
return AjaxResult.success(sysFileService.getFilesAndSubfolders(folderPath));
}
/**
* 文件夹删除
* 从各个存储平台删除文件
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
* @return 文件夹网络路径
*/
@PostMapping("/deleteFolder")
public AjaxResult deleteFolder(@RequestParam("folderName") String folderPath) {
sysFileService.deleteFolder(folderPath);
return AjaxResult.success("文件夹删除成功");
}
}

View File

@ -1,181 +0,0 @@
package com.bonus.file.service.impl;
import com.bonus.common.core.utils.file.FileTypeUtils;
import com.bonus.common.core.utils.file.MimeTypeUtils;
import com.bonus.file.entity.FileDetails;
import com.bonus.file.service.ISysFileService;
import com.bonus.file.utils.FileUploadUtils;
import com.bonus.system.api.domain.SysFile;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* FastDFS 文件存储
*
* @author bonus
*/
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "fdfs")
public class FastDfsSysFileServiceImpl implements ISysFileService
{
/**
* 域名或本机访问地址
*/
@Value("${fdfs.domain}")
public String domain;
@Autowired
private FastFileStorageClient storageClient;
/**
* FastDfs文件上传接口
*
* @param file 上传的文件
* @return 访问地址
* @throws Exception
*/
@Override
public SysFile uploadFile(MultipartFile file) throws Exception
{
//验证文件扩展名是否合法
FileUploadUtils.assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
String fileName = FileUploadUtils.extractFilename(file);
InputStream inputStream = file.getInputStream();
StorePath storePath = storageClient.uploadFile(inputStream, file.getSize(),
FileTypeUtils.getExtension(file), null);
IoUtils.closeQuietly(inputStream);
return SysFile.builder().url(domain + "/" + storePath.getFullPath()).name(fileName).build();
}
@Override
public List<SysFile> uploadFiles(MultipartFile[] files) throws Exception {
List<SysFile> sysFiles = new ArrayList<>();
for (MultipartFile file : files) {
SysFile sysFile = uploadFile(file);
sysFiles.add(sysFile);
}
return sysFiles;
}
/**
* FastDfs文件下载接口待测试
*
* @param response 响应对象
* @param urlStr 文件URL地址
* @return 是否下载成功
*/
@Override
public void downloadFile(HttpServletResponse response, String urlStr) throws Exception
{
try {
String path = urlStr.replace(domain + "/", "");
StorePath storePath = StorePath.parseFromUrl(path);
// 下载文件并返回输入流
byte[] fileData = storageClient.downloadFile(storePath.getGroup(), storePath.getPath(), IOUtils::toByteArray);
// 设置响应头
String encodedFileName = URLEncoder.encode(storePath.getPath(), StandardCharsets.UTF_8.toString());
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=\"" + storePath.getPath() + "\"");
// 将文件内容写入响应
IOUtils.write(fileData, response.getOutputStream());
response.flushBuffer();
} catch (Exception e) {
throw new Exception("Error occurred while downloading file from FastDfs" + urlStr, e);
}
}
/**
* 从给定的URL下载文件并将其保存到指定的目标位置
*
* @param response 请求响应
* @param urlStr 要下载文件的URL地址
* @throws Exception 如果在下载过程中遇到任何异常例如网络问题文件写入问题等
*/
@Override
public void downloadFilesAsZip(HttpServletResponse response, List<String> urlStr) throws Exception {
}
/**
* FastDfs文件删除接口待测试
*
* @param urlStr 文件URL地址
* @return 是否删除成功
*/
@Override
public void deleteFile(String urlStr) throws Exception
{
try {
// URL 中提取文件路径
String path = urlStr.replace(domain + "/", "");
StorePath storePath = StorePath.parseFromUrl(path);
// 删除文件
storageClient.deleteFile(storePath.getGroup(), storePath.getPath());
} catch (Exception e) {
throw new Exception("Error occurred while deleting file from FastDfs" + urlStr, e);
}
}
/**
* 删除指定URL所指向的文件
*
* @param urlStr 要删除文件的URL地址
* @return 布尔值指示文件删除是否成功如果文件被成功删除则返回true否则返回false
* @throws Exception 如果在删除文件的过程中遇到错误例如网络问题文件不存在或没有删除权限等
*/
@Override
public void deleteFiles(List<String> urlStr) throws Exception {
}
/**
* 获取指定文件夹下的所有文件夹和文件
*
* @param folderPath 要获取文件夹和文件的父文件夹路径例如 "folder1/"
* @return 返回一个包含所有文件夹和文件路径的集合
*/
@Override
public Set<FileDetails> getFilesAndSubfolders(String folderPath) {
return Collections.emptySet();
}
/**
* 创建文件夹通过上传带有路径的文件
*
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
*/
@Override
public void createFolder(String folderPath) {
}
/**
* 删除指定路径的所有对象从而删除文件夹
*
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
*/
@Override
public void deleteFolder(String folderPath) {
}
}

View File

@ -1,154 +0,0 @@
package com.bonus.file.service.impl;
import com.bonus.file.entity.FileDetails;
import com.bonus.file.service.ISysFileService;
import com.bonus.file.utils.FileDownloadUtils;
import com.bonus.file.utils.FileUploadUtils;
import com.bonus.system.api.domain.SysFile;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* 本地文件存储
*
* @author bonus
*/
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "file", matchIfMissing = true)
public class LocalSysFileServiceImpl implements ISysFileService
{
/**
* 资源映射路径 前缀
*/
@Value("${file.prefix}")
public String localFilePrefix;
/**
* 域名或本机访问地址
*/
@Value("${file.domain}")
public String domain;
/**
* 上传文件存储在本地的根路径
*/
@Value("${file.path}")
private String localFilePath;
/**
* 本地文件上传接口
*
* @param file 上传的文件
* @return 访问地址
* @throws Exception
*/
@Override
public SysFile uploadFile(MultipartFile file) throws Exception
{
String name = FileUploadUtils.upload(localFilePath, file);
String url = domain + localFilePrefix + name;
return SysFile.builder().url(url).name(name).build();
}
@Override
public List<SysFile> uploadFiles(MultipartFile[] files) throws Exception {
List<SysFile> sysFiles = new ArrayList<>();
for (MultipartFile file : files) {
SysFile sysFile = uploadFile(file);
sysFiles.add(sysFile);
}
return sysFiles;
}
@Override
public void downloadFile(HttpServletResponse response, String urlStr) throws Exception
{
FileDownloadUtils.downloadFile(response, urlStr);
}
/**
* 从给定的URL下载文件并将其保存到指定的目标位置
*
* @param response 请求响应
* @param urlStr 要下载文件的URL地址
* @throws Exception 如果在下载过程中遇到任何异常例如网络问题文件写入问题等
*/
@Override
public void downloadFilesAsZip(HttpServletResponse response, List<String> urlStr) throws Exception {
}
@Override
public void deleteFile(String urlStr) throws Exception
{
String regex = String.format("^(.*?%s)", localFilePrefix);
String updatePath = urlStr.replaceFirst(regex, localFilePath);
Path path = Paths.get(updatePath);
if (Files.exists(path)){
try {
Files.deleteIfExists(path);
}catch (IOException e){
throw new Exception(e.getMessage(), e);
}
}else {
throw new Exception("删除文件时文件不存在");
}
}
/**
* 删除指定URL所指向的文件
*
* @param urlStr 要删除文件的URL地址
* @return 布尔值指示文件删除是否成功如果文件被成功删除则返回true否则返回false
* @throws Exception 如果在删除文件的过程中遇到错误例如网络问题文件不存在或没有删除权限等
*/
@Override
public void deleteFiles(List<String> urlStr) throws Exception {
for (String url : urlStr) {
deleteFile(url);
}
}
/**
* 获取指定文件夹下的所有文件夹和文件
*
* @param folderPath 要获取文件夹和文件的父文件夹路径例如 "folder1/"
* @return 返回一个包含所有文件夹和文件路径的集合
*/
@Override
public Set<FileDetails> getFilesAndSubfolders(String folderPath) {
return Collections.emptySet();
}
/**
* 创建文件夹通过上传带有路径的文件
*
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
*/
@Override
public void createFolder(String folderPath) {
}
/**
* 删除指定路径的所有对象从而删除文件夹
*
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
*/
@Override
public void deleteFolder(String folderPath) {
}
}

View File

@ -1,222 +0,0 @@
package com.bonus.file.service.impl;
import com.alibaba.nacos.common.utils.UuidUtils;
import com.bonus.common.core.utils.file.FileUtils;
import com.bonus.common.core.utils.file.MimeTypeUtils;
import com.bonus.file.entity.FileDetails;
import com.bonus.file.service.ISysFileService;
import com.bonus.file.utils.FileUploadUtils;
import com.bonus.file.utils.MinioUtil;
import com.bonus.system.api.domain.SysFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* Minio 文件存储
*/
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "minio")
public class MinioServiceImpl implements ISysFileService {
private static final Logger logger = LoggerFactory.getLogger(MinioServiceImpl.class);
@Resource
private MinioUtil minioUtil;
/**
* Minio 文件上传接口
*
* @param file 上传的文件
* @return SysFile对象或null
*/
@Override
public SysFile uploadFile(MultipartFile file) {
try {
FileUploadUtils.assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
String originalFilename = Objects.requireNonNull(file.getOriginalFilename(), "文件名不能为空").replace(" ", "");
String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
String objectKey = FileUtils.generateObjectName(UuidUtils.generateUuid() + extension);
return minioUtil.uploadFile(file, objectKey);
} catch (Exception e) {
logger.error("文件上传失败: {}", file.getOriginalFilename(), e);
return null;
}
}
/**
* Minio 多文件上传接口
*
* @param files 上传的文件数组
* @return 上传成功的 SysFile 列表
*/
@Override
public List<SysFile> uploadFiles(MultipartFile[] files) throws Exception {
// 将每个文件的上传操作作为一个 CompletableFuture 任务
List<CompletableFuture<SysFile>> futures = new ArrayList<>();
for (MultipartFile file : files) {
CompletableFuture<SysFile> future = CompletableFuture.supplyAsync(() -> {
try {
return uploadFile(file);
} catch (Exception e) {
throw new RuntimeException("文件上传失败: " + file.getOriginalFilename(), e);
}
});
futures.add(future);
}
// 使用 allOf 等待所有任务完成并收集结果
CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
// 在所有上传任务完成后获取每个文件的上传结果
return allOf.thenApply(v -> futures.stream()
.map(CompletableFuture::join) // 获取每个 CompletableFuture 的结果
.collect(Collectors.toList())
).get(); // .get() 会阻塞直到所有任务完成
}
/**
* MinIO 下载文件
*
* @param response 响应对象
* @param urlStr 文件 URL 地址
*/
@Override
public void downloadFile(HttpServletResponse response, String urlStr) throws Exception {
try (InputStream inputStream = minioUtil.downloadFile(urlStr)) {
com.bonus.file.utils.FileUtils.setResponseHeaderByUrl(response, urlStr);
try (OutputStream outputStream = response.getOutputStream()) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
} catch (IOException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
logger.error("下载文件失败: {}", urlStr, e);
throw new Exception("下载失败");
}
}
/**
* 从给定的URL下载文件并将其保存到指定的目标位置
*
* @param response 请求响应
* @param urlStr 要下载文件的URL地址
* @throws Exception 如果在下载过程中遇到任何异常例如网络问题文件写入问题等
*/
@Override
public void downloadFilesAsZip(HttpServletResponse response, List<String> urlStr) throws Exception {
String zipFileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".zip";
// 设置响应头
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=" + zipFileName);
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
// 遍历文件列表并压缩
for (String objectName : urlStr) {
try (InputStream inputStream = minioUtil.downloadFile(objectName)) {
// 添加 ZipEntry
zos.putNextEntry(new ZipEntry(objectName));
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
zos.closeEntry();
} catch (Exception e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
throw new Exception("文件压缩或写入失败: " + objectName, e);
}
}
zos.finish();
} catch (Exception e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
throw new Exception("压缩文件下载失败", e);
}
}
/**
* 删除文件
*
* @param urlStr 文件 URL 地址
*/
@Override
public void deleteFile(String urlStr) throws Exception {
try {
minioUtil.deleteObject(urlStr);
logger.info("文件删除成功: {}", urlStr);
} catch (Exception e) {
logger.error("文件删除失败: {}", urlStr, e);
throw e;
}
}
/**
* 删除指定URL所指向的文件
*
* @param urlStr 要删除文件的URL地址
* @return 布尔值指示文件删除是否成功如果文件被成功删除则返回true否则返回false
* @throws Exception 如果在删除文件的过程中遇到错误例如网络问题文件不存在或没有删除权限等
*/
@Override
public void deleteFiles(List<String> urlStr){
for (String objectName : urlStr) {
try {
deleteFile(objectName);
} catch (Exception e) {
logger.error("文件删除失败: {} 错误: {}", objectName, e.getMessage());
}
}
}
/**
* 获取指定文件夹下的所有文件夹和文件
*
* @param folderPath 要获取文件夹和文件的父文件夹路径例如 "folder1/"
* @return 返回一个包含所有文件夹和文件路径的集合
*/
@Override
public Set<FileDetails> getFilesAndSubfolders(String folderPath) {
return minioUtil.getFilesAndSubfolders(folderPath);
}
/**
* 创建文件夹通过上传带有路径的文件
*
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
*/
@Override
public void createFolder(String folderPath) {
minioUtil.createFolder(folderPath);
}
/**
* 删除指定路径的所有对象从而删除文件夹
*
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
*/
@Override
public void deleteFolder(String folderPath) {
minioUtil.deleteFolder(folderPath);
}
}

View File

@ -1,248 +0,0 @@
package com.bonus.file.service.impl;
import com.bonus.common.core.utils.file.MimeTypeUtils;
import com.bonus.file.entity.FileDetails;
import com.bonus.file.service.ISysFileService;
import com.bonus.file.utils.FileUploadUtils;
import com.bonus.system.api.domain.SysFile;
import com.mongodb.client.gridfs.model.GridFSFile;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.IOUtils;
import org.bson.Document;
import org.bson.types.Binary;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @author wangvivi
*/
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "mongodb")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MongodbServiceImpl implements ISysFileService {
@Value("${spring.data.mongodb.gridfs-size-threshold}")
private long gridFsSizeThreshold;
final static String COLLECTION_NAME = "smallFiles";
/**
* GridFsTemplate 用于大文件存储
*/
private final GridFsTemplate gridFsTemplate;
/**
* MongoTemplate 用于小文件存储
*/
private final MongoTemplate mongoTemplate;
@Override
public SysFile uploadFile(MultipartFile file) throws Exception {
//验证文件扩展名和大小
FileUploadUtils.assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
String fileName = Objects.requireNonNull(file.getOriginalFilename(), "文件名不能为空");
fileName = fileName.replace(" ", "");
long fileSize = file.getSize();
// 判断是否使用 GridFS 存储
if (fileSize >= gridFsSizeThreshold) {
// 使用 GridFS 存储大文件
ObjectId fileId = gridFsTemplate.store(file.getInputStream(), fileName, file.getContentType());
return SysFile.builder().name(fileName).url(fileId.toHexString()).build();
} else {
// 小文件直接存储为二进制数据
// 创建一个Map存储文件信息
Map<String, Object> fileData = new HashMap<>();
fileData.put("fileName", fileName);
fileData.put("fileSize", file.getSize());
fileData.put("contentType", file.getContentType());
// 将文件内容以byte[]存储
fileData.put("fileData", file.getBytes());
// 插入文件信息到MongoDB的集合中可以使用指定的集合名
Document insertedFile = mongoTemplate.insert(new Document(fileData), COLLECTION_NAME);
// 返回文件的唯一标识符MongoDB的ObjectId
return SysFile.builder().name(fileName).url(insertedFile.getObjectId("_id").toString()).build();
}
}
@Override
public List<SysFile> uploadFiles(MultipartFile[] files) throws Exception {
List<SysFile> uploadedFiles = new ArrayList<>();
for (MultipartFile file : files) {
uploadedFiles.add(uploadFile(file));
}
return uploadedFiles;
}
@Override
public void downloadFile(HttpServletResponse response, String fileId) throws Exception {
// 先尝试从 GridFS 中读取
GridFSFile gridFSFile = gridFsTemplate.findOne(new org.springframework.data.mongodb.core.query.Query()
.addCriteria(org.springframework.data.mongodb.core.query.Criteria.where("_id").is(new ObjectId(fileId))));
if (gridFSFile != null) {
// 设置响应头告知客户端文件下载信息
String encodedFileName = URLEncoder.encode(gridFSFile.getFilename(), StandardCharsets.UTF_8.toString());
response.setContentType(gridFSFile.getMetadata().getString("_contentType"));
response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"");
// GridFS 文件下载
InputStream inputStream = gridFsTemplate.getResource(gridFSFile).getInputStream();
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
}
else {
downloadFromCollection(response, fileId);
}
}
/**
* 从给定的URL下载文件并将其保存到指定的目标位置
*
* @param response 请求响应
* @param fileIds 要下载文件的URL地址
* @throws Exception 如果在下载过程中遇到任何异常例如网络问题文件写入问题等
*/
@Override
public void downloadFilesAsZip(HttpServletResponse response, List<String> fileIds) throws Exception {
String zipFileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".zip";
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=" + zipFileName);
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
byte[] buffer = new byte[8192];
for (String fileId : fileIds) {
// 先尝试从 GridFS 中读取
GridFSFile gridFSFile = gridFsTemplate.findOne(new org.springframework.data.mongodb.core.query.Query()
.addCriteria(org.springframework.data.mongodb.core.query.Criteria.where("_id").is(new ObjectId(fileId))));
if (gridFSFile != null) {
try (InputStream inputStream = gridFsTemplate.getResource(gridFSFile).getInputStream()) {
// 创建一个新的 ZipEntry并使用文件名称
zos.putNextEntry(new ZipEntry(gridFSFile.getFilename()));
int len;
while ((len = inputStream.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
zos.closeEntry();
} catch (Exception e) {
System.err.println("无法压缩文件: " + fileId + " 错误: " + e.getMessage());
}
} else {
System.err.println("文件不存在,文件 ID: " + fileId);
}
}
zos.finish();
}
}
@Override
public void deleteFile(String fileId) throws Exception {
try {
// 尝试从 GridFS 中删除
gridFsTemplate.delete(new org.springframework.data.mongodb.core.query.Query()
.addCriteria(org.springframework.data.mongodb.core.query.Criteria.where("_id").is(new ObjectId(fileId))));
// 尝试删除普通集合中的文件
mongoTemplate.findAllAndRemove(new org.springframework.data.mongodb.core.query.Query().addCriteria(org.springframework.data.mongodb.core.query.Criteria.where("_id").is(fileId)), Document.class, COLLECTION_NAME);
} catch (Exception e) {
throw new Exception("删除文件失败" );
}
}
/**
* 删除指定URL所指向的文件
*
* @param fileId 要删除文件的URL地址
* @return 布尔值指示文件删除是否成功如果文件被成功删除则返回true否则返回false
* @throws Exception 如果在删除文件的过程中遇到错误例如网络问题文件不存在或没有删除权限等
*/
@Override
public void deleteFiles(List<String> fileId) throws Exception {
for (String url : fileId) {
}
}
/**
* 获取指定文件夹下的所有文件夹和文件
*
* @param folderPath 要获取文件夹和文件的父文件夹路径例如 "folder1/"
* @return 返回一个包含所有文件夹和文件路径的集合
*/
@Override
public Set<FileDetails> getFilesAndSubfolders(String folderPath) {
return Collections.emptySet();
}
/**
* 创建文件夹通过上传带有路径的文件
*
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
*/
@Override
public void createFolder(String folderPath) {
}
/**
* 删除指定路径的所有对象从而删除文件夹
*
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
*/
@Override
public void deleteFolder(String folderPath) {
}
private void downloadFromCollection(HttpServletResponse response, String fileId) throws IOException {
// 尝试从普通集合中获取二进制文件
Document fileDocument = mongoTemplate.findById(fileId, Document.class, COLLECTION_NAME);
if (fileDocument == null) {
// 如果文件不存在返回错误信息
throw new IOException("文件不存在");
}
// 获取 Binary 对象并转换为 byte[]
Binary binaryData = fileDocument.get("fileData", Binary.class);
// Binary 中提取 byte[]
byte[] fileData = binaryData.getData();
// 假设文件名保存在 fileName 字段
String fileName = fileDocument.getString("fileName");
// 假设文件类型保存在 contentType 字段
String contentType = fileDocument.getString("contentType");
// 如果 contentType 为空默认设置为 application/octet-stream
if (contentType == null || contentType.isEmpty()) {
contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
}
// 设置响应头包含文件名和内容类型
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodedFileName);
response.setContentType(contentType);
response.setContentLength(fileData.length);
// 将文件的二进制数据写入到响应输出流中
try (OutputStream os = response.getOutputStream()) {
os.write(fileData);
os.flush();
} catch (IOException e) {
// 如果写入过程出现异常返回错误信息
throw new IOException("Failed to download file");
}
}
}

View File

@ -1,191 +0,0 @@
package com.bonus.file.service.impl;
import com.alibaba.nacos.common.utils.UuidUtils;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.utils.file.FileUtils;
import com.bonus.common.core.utils.file.MimeTypeUtils;
import com.bonus.file.entity.FileDetails;
import com.bonus.file.service.ISysFileService;
import com.bonus.file.utils.FileUploadUtils;
import com.bonus.file.utils.ObsUtils;
import com.bonus.system.api.domain.SysFile;
import com.obs.services.model.ObsObject;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* Minio 文件存储
*
* @author bonus
*/
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "obs")
public class ObsServiceImpl implements ISysFileService {
@Resource
private ObsUtils obsUtils;
/**
* 文件上传
*
* @param file 文件流
* @return 文件信息
*/
@Override
public SysFile uploadFile(MultipartFile file) throws Exception {
try {
//验证文件扩展名和大小
FileUploadUtils.assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
String originalFilename = Objects.requireNonNull(file.getOriginalFilename(), "文件名不能为空");
originalFilename = originalFilename.replace(" ", "");
String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
String objectKey = UuidUtils.generateUuid() + extension;
objectKey = FileUtils.generateObjectName(objectKey);
SysFile sysFile = obsUtils.uploadFile(objectKey, Objects.requireNonNull(FileUtils.multipartFileToFile(file)));
sysFile.setName(originalFilename);
return sysFile;
} catch (Exception e) {
throw new Exception(e);
}
}
@Override
public List<SysFile> uploadFiles(MultipartFile[] files) throws Exception {
try {
List<SysFile> sysFiles = new ArrayList<>();
for (MultipartFile file : files) {
SysFile sysFile = uploadFile(file);
sysFiles.add(sysFile);
}
return sysFiles;
} catch (Exception e) {
throw new Exception(e);
}
}
@Override
public void downloadFile(HttpServletResponse response, String urlStr) throws Exception {
R<ObsObject> obsObjectR = obsUtils.downloadFile(urlStr);
if (R.isError(obsObjectR)) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
throw new Exception("文件不存在");
}
if (obsObjectR.getData() == null) {
response.setStatus(HttpStatus.NOT_FOUND.value());
throw new Exception("文件不存在");
}
InputStream inputStream = obsObjectR.getData().getObjectContent();
com.bonus.file.utils.FileUtils.setResponseHeaderByUrl(response, urlStr);
try (OutputStream outputStream = response.getOutputStream()) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
} catch (IOException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
throw new Exception("下载失败");
}
}
/**
* 从给定的URL下载文件并将其保存到指定的目标位置
*
* @param response 请求响应
* @param urlStr 要下载文件的URL地址
* @throws Exception 如果在下载过程中遇到任何异常例如网络问题文件写入问题等
*/
@Override
public void downloadFilesAsZip(HttpServletResponse response, List<String> urlStr) throws Exception {
String zipFileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".zip";
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=" + zipFileName);
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
byte[] buffer = new byte[8192];
for (String objectName : urlStr) {
R<ObsObject> obsObjectR = obsUtils.downloadFile(objectName);
if (R.isError(obsObjectR)) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
throw new Exception("文件不存在");
}
if (obsObjectR.getData() == null) {
response.setStatus(HttpStatus.NOT_FOUND.value());
throw new Exception("文件不存在");
}
try (InputStream inputStream = obsObjectR.getData().getObjectContent()) {
zos.putNextEntry(new ZipEntry(objectName));
int len;
while ((len = inputStream.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
zos.closeEntry();
} catch (Exception e) {
System.err.println("无法压缩文件: " + objectName + " 错误: " + e.getMessage());
}
}
zos.finish();
}
}
@Override
public void deleteFile(String urlStr) throws Exception {
obsUtils.deleteFile(urlStr);
}
/**
* 删除指定URL所指向的文件
*
* @param urlStr 要删除文件的URL地址
* @return 布尔值指示文件删除是否成功如果文件被成功删除则返回true否则返回false
* @throws Exception 如果在删除文件的过程中遇到错误例如网络问题文件不存在或没有删除权限等
*/
@Override
public void deleteFiles(List<String> urlStr) throws Exception {
}
/**
* 获取指定文件夹下的所有文件夹和文件
*
* @param folderPath 要获取文件夹和文件的父文件夹路径例如 "folder1/"
* @return 返回一个包含所有文件夹和文件路径的集合
*/
@Override
public Set<FileDetails> getFilesAndSubfolders(String folderPath) {
return obsUtils.getFilesAndFolders(folderPath);
}
/**
* 创建文件夹通过上传带有路径的文件
*
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
*/
@Override
public void createFolder(String folderPath) {
obsUtils.createFolder(folderPath);
}
/**
* 删除指定路径的所有对象从而删除文件夹
*
* @param folderPath 文件夹路径例如 "folder1/subfolder/"
*/
@Override
public void deleteFolder(String folderPath) {
obsUtils.deleteFolderAndContents(folderPath);
}
}

View File

@ -1,6 +1,6 @@
package com.bonus.file.service.impl;
import com.alibaba.nacos.common.utils.UuidUtils;
import com.alibaba.nacos.common.util.UuidUtils;
import com.aliyun.oss.model.OSSObject;
import com.bonus.common.core.domain.R;
import com.bonus.common.core.utils.file.FileUtils;

View File

@ -1,6 +1,6 @@
# Tomcat
server:
port: 9300
port: 18088
# Spring
spring:
@ -21,3 +21,6 @@ spring:
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
feign:
hystrix:
enabled: true

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bonus</groupId>
<artifactId>bonus-modules</artifactId>
<version>24.12.0-SNAPSHOT</version>
</parent>
<groupId>com.bonus.ldlz</groupId>
<artifactId>bonus-ldlz</artifactId>
<dependencies>
<!--加密依赖包-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>${jasypt-spring-boot-starter.version}</version>
</dependency>
<!-- SpringCloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringBoot Actuator -->
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>-->
<!-- Mysql Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version> <!-- 建议使用最新稳定版 -->
<scope>runtime</scope> <!-- 通常只需运行时依赖 -->
</dependency>
<!-- bonus Common DataSource -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-datasource</artifactId>
</dependency>
<!-- bonus Common DataScope -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-datascope</artifactId>
</dependency>
<!-- bonus Common Log -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-log</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.0-jre</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<!-- <version>6.0.18</version>-->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,39 @@
package com.bonus.ldlz;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 领导履职模块
*
* @author bonus
*/
/*@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class },scanBasePackages = {
"com.bonus.system",
"com.bonus.common.redis", // 必须包含RedisService所在包
"com.bonus.system.api",
"com.bonus.common.datasource.annotation",
"com.bonus.common.datascope",
})*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
public class BonusLdlzApplication
{
public static void main(String[] args)
{
SpringApplication.run(BonusLdlzApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 领导履职模块启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \n" +
" | ( ' ) | \\ _. / ' \n" +
" |(_ o _) / _( )_ .' \n" +
" | (_,_).' __ ___(_ o _)' \n" +
" | |\\ \\ | || |(_,_)' \n" +
" | | \\ `' /| `-' / \n" +
" | | \\ / \\ / \n" +
" ''-' `'-' `-..-' ");
}
}

View File

@ -0,0 +1,2 @@
Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}

View File

@ -0,0 +1,28 @@
# Tomcat
server:
port: 18085
# Spring
spring:
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 192.168.87.190:8848
namespace: e9abf140-675a-4d55-af3f-1f28a6186904
username: nacos
password: nacos
config:
# 配置中心地址
server-addr: 192.168.87.190:8848
namespace: e9abf140-675a-4d55-af3f-1f28a6186904
username: nacos
password: nacos
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
feign:
hystrix:
enabled: true

View File

@ -0,0 +1,17 @@
# Spring
spring:
application:
# 应用名称
name: bonus-ldlz
profiles:
# 环境配置
active: dev
#加密组件
jasypt:
encryptor:
password: Encrypt
feign:
hystrix:
enabled: true

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/bonus-ldlz" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.bonus" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
</configuration>

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bonus</groupId>
<artifactId>bonus-modules</artifactId>
<version>24.12.0-SNAPSHOT</version>
</parent>
<groupId>com.bonus.realname</groupId>
<artifactId>bonus-realname</artifactId>
<dependencies>
<!--加密依赖包-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>${jasypt-spring-boot-starter.version}</version>
</dependency>
<!-- SpringCloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- SpringBoot Actuator -->
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>-->
<!-- Mysql Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version> <!-- 建议使用最新稳定版 -->
<scope>runtime</scope> <!-- 通常只需运行时依赖 -->
</dependency>
<!-- bonus Common DataSource -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-datasource</artifactId>
</dependency>
<!-- bonus Common DataScope -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-datascope</artifactId>
</dependency>
<!-- bonus Common Log -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-log</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.0-jre</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<!-- <version>6.0.18</version>-->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,32 @@
package com.bonus.realname;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 实名制模块
*
* @author bonus
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
public class BonusRealNameApplication
{
public static void main(String[] args)
{
SpringApplication.run(BonusRealNameApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 实名制模块启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \n" +
" | ( ' ) | \\ _. / ' \n" +
" |(_ o _) / _( )_ .' \n" +
" | (_,_).' __ ___(_ o _)' \n" +
" | |\\ \\ | || |(_,_)' \n" +
" | | \\ `' /| `-' / \n" +
" | | \\ / \\ / \n" +
" ''-' `'-' `-..-' ");
}
}

View File

@ -0,0 +1,2 @@
Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}

View File

@ -0,0 +1,28 @@
# Tomcat
server:
port: 18087
# Spring
spring:
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 192.168.87.190:8848
namespace: e9abf140-675a-4d55-af3f-1f28a6186904
username: nacos
password: nacos
config:
# 配置中心地址
server-addr: 192.168.87.190:8848
namespace: e9abf140-675a-4d55-af3f-1f28a6186904
username: nacos
password: nacos
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
feign:
hystrix:
enabled: true

View File

@ -0,0 +1,17 @@
# Spring
spring:
application:
# 应用名称
name: bonus-realname
profiles:
# 环境配置
active: dev
#加密组件
jasypt:
encryptor:
password: Encrypt
feign:
hystrix:
enabled: true

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/bonus-realname" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.bonus" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
</configuration>

View File

@ -47,13 +47,6 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>-->
<!-- Swagger UI -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.fox.version}</version>
</dependency>
<!-- Mysql Connector -->
<dependency>
<groupId>mysql</groupId>
@ -81,12 +74,6 @@
</dependency>
<!-- bonus Common Swagger -->
<dependency>
<groupId>com.bonus</groupId>
<artifactId>bonus-common-swagger</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>

View File

@ -4,7 +4,6 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.bonus.common.security.annotation.EnableCustomConfig;
import com.bonus.common.security.annotation.EnableRyFeignClients;
import com.bonus.common.swagger.annotation.EnableCustomSwagger2;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.scheduling.annotation.EnableScheduling;
@ -14,9 +13,14 @@ import org.springframework.scheduling.annotation.EnableScheduling;
* @author bonus
*/
@EnableCustomConfig
@EnableCustomSwagger2
@EnableRyFeignClients
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class },scanBasePackages = {
"com.bonus.system",
"com.bonus.common.redis", // 必须包含RedisService所在包
"com.bonus.system.api",
"com.bonus.common.datasource.annotation",
"com.bonus.common.datascope",
})
@EnableScheduling
public class BonusSystemApplication
{

View File

@ -25,7 +25,7 @@ import com.bonus.system.service.ISysConfigService;
@Service
public class SysConfigServiceImpl implements ISysConfigService
{
@Autowired
@Resource
private SysConfigMapper configMapper;
@Resource
@ -96,7 +96,8 @@ public class SysConfigServiceImpl implements ISysConfigService
String str = config.getConfigKey().replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_");
config.setConfigKey(str);
}
return configMapper.selectConfigList(config);
return null;
// return configMapper.selectConfigList(config);
}
/**
@ -165,11 +166,11 @@ public class SysConfigServiceImpl implements ISysConfigService
@Override
public void loadingConfigCache()
{
List<SysConfig> configsList = configMapper.selectConfigList(new SysConfig());
/*List<SysConfig> configsList = configMapper.selectConfigList(new SysConfig());
for (SysConfig config : configsList)
{
redisService.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue());
}
}*/
}
/**

View File

@ -11,6 +11,9 @@
<modules>
<module>bonus-system</module>
<module>bonus-file</module>
<module>bonus-ldlz</module>
<module>bonus-czl</module>
<module>bonus-realname</module>
</modules>
<artifactId>bonus-modules</artifactId>