mirror of
				https://github.com/suyiiyii/SIMS.git
				synced 2025-11-04 15:54:52 +08:00 
			
		
		
		
	Merge remote-tracking branch 'origin/main' into wr
This commit is contained in:
		
						commit
						cf4bf35d66
					
				
							
								
								
									
										20
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								pom.xml
									
									
									
									
									
								
							@ -95,16 +95,34 @@
 | 
				
			|||||||
            <artifactId>spring-restdocs-mockmvc</artifactId>
 | 
					            <artifactId>spring-restdocs-mockmvc</artifactId>
 | 
				
			||||||
            <scope>test</scope>
 | 
					            <scope>test</scope>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
 | 
					        <!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui -->
 | 
				
			||||||
        <dependency>
 | 
					        <dependency>
 | 
				
			||||||
            <groupId>org.springdoc</groupId>
 | 
					            <groupId>org.springdoc</groupId>
 | 
				
			||||||
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
 | 
					            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
 | 
				
			||||||
            <version>2.3.0</version>
 | 
					            <version>2.6.0</version>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <dependency>
 | 
					        <dependency>
 | 
				
			||||||
            <groupId>org.xerial</groupId>
 | 
					            <groupId>org.xerial</groupId>
 | 
				
			||||||
            <artifactId>sqlite-jdbc</artifactId>
 | 
					            <artifactId>sqlite-jdbc</artifactId>
 | 
				
			||||||
            <scope>test</scope>
 | 
					            <scope>test</scope>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>com.amazonaws</groupId>
 | 
				
			||||||
 | 
					            <artifactId>aws-java-sdk-s3</artifactId>
 | 
				
			||||||
 | 
					            <version>1.12.706</version>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>commons-io</groupId>
 | 
				
			||||||
 | 
					            <artifactId>commons-io</artifactId>
 | 
				
			||||||
 | 
					            <version>2.11.0</version>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>org.mockito</groupId>
 | 
				
			||||||
 | 
					            <artifactId>mockito-core</artifactId>
 | 
				
			||||||
 | 
					            <version>4.0.0</version>
 | 
				
			||||||
 | 
					            <scope>test</scope>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
    </dependencies>
 | 
					    </dependencies>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <build>
 | 
					    <build>
 | 
				
			||||||
 | 
				
			|||||||
@ -48,6 +48,8 @@ public class JwtInterceptor implements HandlerInterceptor {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (TokenExpiredException e) {
 | 
					        } catch (TokenExpiredException e) {
 | 
				
			||||||
            throw new ServiceException("401", "登录已过期,请重新登录");
 | 
					            throw new ServiceException("401", "登录已过期,请重新登录");
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            throw new ServiceException("401", "token验证失败,请重新登录");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // 获取 token 中的 user id
 | 
					        // 获取 token 中的 user id
 | 
				
			||||||
        Integer userId = Integer.parseInt(Objects.requireNonNull(JwtUtils.extractUserId(token)));
 | 
					        Integer userId = Integer.parseInt(Objects.requireNonNull(JwtUtils.extractUserId(token)));
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										17
									
								
								src/main/java/top/suyiiyii/sims/common/S3Config.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/main/java/top/suyiiyii/sims/common/S3Config.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					package top.suyiiyii.sims.common;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.beans.factory.annotation.Value;
 | 
				
			||||||
 | 
					import org.springframework.context.annotation.Bean;
 | 
				
			||||||
 | 
					import org.springframework.context.annotation.Configuration;
 | 
				
			||||||
 | 
					import top.suyiiyii.sims.utils.S3Client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Configuration
 | 
				
			||||||
 | 
					public class S3Config {
 | 
				
			||||||
 | 
					    @Bean
 | 
				
			||||||
 | 
					    public static S3Client Config(@Value("${S3.ENDPOINT}") String endpoint,
 | 
				
			||||||
 | 
					                                  @Value("${S3.ACCESS_KEY}") String accessKey,
 | 
				
			||||||
 | 
					                                  @Value("${S3.SECRET_KEY}") String secretKey,
 | 
				
			||||||
 | 
					                                  @Value("${S3.BUCKET}") String bucket) {
 | 
				
			||||||
 | 
					        return new S3Client(endpoint, accessKey, secretKey, bucket);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					package top.suyiiyii.sims.controller;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import io.swagger.v3.oas.annotations.Operation;
 | 
				
			||||||
 | 
					import io.swagger.v3.oas.annotations.Parameter;
 | 
				
			||||||
 | 
					import io.swagger.v3.oas.annotations.media.Content;
 | 
				
			||||||
 | 
					import io.swagger.v3.oas.annotations.media.Schema;
 | 
				
			||||||
 | 
					import io.swagger.v3.oas.annotations.parameters.RequestBody;
 | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
 | 
					import org.springframework.beans.factory.annotation.Autowired;
 | 
				
			||||||
 | 
					import org.springframework.web.bind.annotation.PostMapping;
 | 
				
			||||||
 | 
					import org.springframework.web.bind.annotation.RestController;
 | 
				
			||||||
 | 
					import org.springframework.web.multipart.MultipartFile;
 | 
				
			||||||
 | 
					import top.suyiiyii.sims.common.AuthAccess;
 | 
				
			||||||
 | 
					import top.suyiiyii.sims.service.FileService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Slf4j
 | 
				
			||||||
 | 
					@RestController
 | 
				
			||||||
 | 
					public class FileController {
 | 
				
			||||||
 | 
					    @Autowired
 | 
				
			||||||
 | 
					    FileService fileService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @AuthAccess(allowRoles = {"user"})
 | 
				
			||||||
 | 
					    @Operation(summary = "上传文件", description = "使用form-data格式\nfile:文件\nfilename: 文件名\n返回可访问的路径")
 | 
				
			||||||
 | 
					    @PostMapping("/upload")
 | 
				
			||||||
 | 
					    public String uploadFile(
 | 
				
			||||||
 | 
					            @Parameter String filename,
 | 
				
			||||||
 | 
					            @RequestBody(content = @Content(mediaType = "multipart/form-data",
 | 
				
			||||||
 | 
					                    schema = @Schema(type = "string", format = "binary"))) MultipartFile file) {
 | 
				
			||||||
 | 
					        try (InputStream in = file.getInputStream()) {
 | 
				
			||||||
 | 
					            log.info("文件上传,文件名:{},描述:{}", file.getOriginalFilename(), filename);
 | 
				
			||||||
 | 
					            return fileService.uploadFile(in, filename);
 | 
				
			||||||
 | 
					        } catch (IOException e) {
 | 
				
			||||||
 | 
					            log.warn("文件上传失败", e);
 | 
				
			||||||
 | 
					            throw new RuntimeException(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								src/main/java/top/suyiiyii/sims/service/FileService.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/main/java/top/suyiiyii/sims/service/FileService.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					package top.suyiiyii.sims.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.beans.factory.annotation.Autowired;
 | 
				
			||||||
 | 
					import org.springframework.stereotype.Service;
 | 
				
			||||||
 | 
					import top.suyiiyii.sims.utils.S3Client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Service
 | 
				
			||||||
 | 
					public class FileService {
 | 
				
			||||||
 | 
					    @Autowired
 | 
				
			||||||
 | 
					    private S3Client s3Client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String uploadFile(InputStream input,String fileName) {
 | 
				
			||||||
 | 
					        String extension = fileName.substring(fileName.lastIndexOf("."));
 | 
				
			||||||
 | 
					        return s3Client.uploadFile(input, extension);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										123
									
								
								src/main/java/top/suyiiyii/sims/utils/S3Client.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								src/main/java/top/suyiiyii/sims/utils/S3Client.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,123 @@
 | 
				
			|||||||
 | 
					package top.suyiiyii.sims.utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.amazonaws.ClientConfiguration;
 | 
				
			||||||
 | 
					import com.amazonaws.Protocol;
 | 
				
			||||||
 | 
					import com.amazonaws.auth.AWSCredentials;
 | 
				
			||||||
 | 
					import com.amazonaws.auth.BasicAWSCredentials;
 | 
				
			||||||
 | 
					import com.amazonaws.services.s3.AmazonS3;
 | 
				
			||||||
 | 
					import com.amazonaws.services.s3.AmazonS3Client;
 | 
				
			||||||
 | 
					import com.amazonaws.services.s3.S3ClientOptions;
 | 
				
			||||||
 | 
					import com.amazonaws.services.s3.model.ObjectMetadata;
 | 
				
			||||||
 | 
					import com.amazonaws.services.s3.model.S3Object;
 | 
				
			||||||
 | 
					import org.apache.commons.io.IOUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					import java.io.OutputStream;
 | 
				
			||||||
 | 
					import java.net.MalformedURLException;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class S3Client {
 | 
				
			||||||
 | 
					    private final String endpoint;
 | 
				
			||||||
 | 
					    private final String bucket;
 | 
				
			||||||
 | 
					    private final AmazonS3 s3client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public S3Client(String endpoint, String accessKey, String secretKey, String bucket) {
 | 
				
			||||||
 | 
					        this.endpoint = endpoint;
 | 
				
			||||||
 | 
					        this.bucket = bucket;
 | 
				
			||||||
 | 
					        URL endpointUrl;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            endpointUrl = new URL(endpoint);
 | 
				
			||||||
 | 
					        } catch (MalformedURLException e) {
 | 
				
			||||||
 | 
					            throw new RuntimeException(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        String protocol = endpointUrl.getProtocol();
 | 
				
			||||||
 | 
					        int port = endpointUrl.getPort() == -1 ? endpointUrl.getDefaultPort() : endpointUrl.getPort();
 | 
				
			||||||
 | 
					        ClientConfiguration clientConfig = new ClientConfiguration();
 | 
				
			||||||
 | 
					        clientConfig.setSignerOverride("S3SignerType");
 | 
				
			||||||
 | 
					        clientConfig.setProtocol(Protocol.valueOf(protocol.toUpperCase()));
 | 
				
			||||||
 | 
					        // 禁用证书检查,避免https自签证书校验失败
 | 
				
			||||||
 | 
					        System.setProperty("com.amazonaws.sdk.disableCertChecking", "true");
 | 
				
			||||||
 | 
					        // 屏蔽 AWS 的 MD5 校验,避免校验导致的下载抛出异常问题
 | 
				
			||||||
 | 
					        System.setProperty("com.amazonaws.services.s3.disableGetObjectMD5Validation", "true");
 | 
				
			||||||
 | 
					        AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
 | 
				
			||||||
 | 
					        // 创建 S3Client 实例
 | 
				
			||||||
 | 
					        AmazonS3 s3client = new AmazonS3Client(awsCredentials, clientConfig);
 | 
				
			||||||
 | 
					        s3client.setEndpoint(endpointUrl.getHost() + ":" + port);
 | 
				
			||||||
 | 
					        s3client.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build());
 | 
				
			||||||
 | 
					        this.s3client = s3client;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean bucketExists(String bucket) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return s3client.doesBucketExist(bucket);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean existObject(String bucket, String objectId) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return s3client.doesObjectExist(bucket, objectId);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public InputStream download(String bucket, String objectId) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            S3Object o = s3client.getObject(bucket, objectId);
 | 
				
			||||||
 | 
					            return o.getObjectContent();
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void download(String bucket, String objectId, OutputStream out) {
 | 
				
			||||||
 | 
					        S3Object o = s3client.getObject(bucket, objectId);
 | 
				
			||||||
 | 
					        try (InputStream in = o.getObjectContent()) {
 | 
				
			||||||
 | 
					            IOUtils.copyLarge(in, out);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void upload(String bucket, String objectId, InputStream input) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // 创建文件上传的元数据
 | 
				
			||||||
 | 
					            ObjectMetadata meta = new ObjectMetadata();
 | 
				
			||||||
 | 
					            // 设置文件上传长度
 | 
				
			||||||
 | 
					            meta.setContentLength(input.available());
 | 
				
			||||||
 | 
					            // 上传
 | 
				
			||||||
 | 
					            s3client.putObject(bucket, objectId, input, meta);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String uploadFile(InputStream input) {
 | 
				
			||||||
 | 
					        String objectID = UUID.randomUUID().toString();
 | 
				
			||||||
 | 
					        upload(bucket, objectID, input);
 | 
				
			||||||
 | 
					        return endpoint + "/" + bucket + "/" + objectID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 接收文件流,自动使用随机uuid命名并保留扩展名
 | 
				
			||||||
 | 
					     * 返回公网上可以直接访问的URL
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param input         文件流
 | 
				
			||||||
 | 
					     * @param extensionName 扩展名
 | 
				
			||||||
 | 
					     * @return 文件的URL
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String uploadFile(InputStream input, String extensionName) {
 | 
				
			||||||
 | 
					        String objectID = UUID.randomUUID() + extensionName;
 | 
				
			||||||
 | 
					        upload(bucket, objectID, input);
 | 
				
			||||||
 | 
					        return endpoint + "/" + bucket + "/" + objectID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -2,6 +2,10 @@
 | 
				
			|||||||
spring:
 | 
					spring:
 | 
				
			||||||
  profiles:
 | 
					  profiles:
 | 
				
			||||||
    active: prod
 | 
					    active: prod
 | 
				
			||||||
 | 
					  servlet:
 | 
				
			||||||
 | 
					    multipart:
 | 
				
			||||||
 | 
					      max-file-size: 100MB
 | 
				
			||||||
 | 
					      max-request-size: 100MB
 | 
				
			||||||
  datasource:
 | 
					  datasource:
 | 
				
			||||||
    url: ${DATASOURCE_URL}
 | 
					    url: ${DATASOURCE_URL}
 | 
				
			||||||
    username: ${DATASOURCE_USERNAME}
 | 
					    username: ${DATASOURCE_USERNAME}
 | 
				
			||||||
@ -14,4 +18,10 @@ auto-table:
 | 
				
			|||||||
  model-package: top.suyiiyii.sims.entity
 | 
					  model-package: top.suyiiyii.sims.entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jwt:
 | 
					jwt:
 | 
				
			||||||
  secret: ${JWT_SECRET}
 | 
					  secret: ${JWT_SECRET}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					S3:
 | 
				
			||||||
 | 
					  ENDPOINT: ${S3_ENDPOINT}
 | 
				
			||||||
 | 
					  BUCKET: ${S3_BUCKET}
 | 
				
			||||||
 | 
					  ACCESS_KEY: ${S3_ACCESS_KEY}
 | 
				
			||||||
 | 
					  SECRET_KEY: ${S3_SECRET_KEY}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,14 @@
 | 
				
			|||||||
package top.suyiiyii.sims.service;
 | 
					package top.suyiiyii.sims.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.junit.jupiter.api.Test;
 | 
					import org.junit.jupiter.api.Test;
 | 
				
			||||||
 | 
					import org.junit.jupiter.api.extension.ExtendWith;
 | 
				
			||||||
 | 
					import org.mockito.junit.jupiter.MockitoExtension;
 | 
				
			||||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
					import org.springframework.beans.factory.annotation.Autowired;
 | 
				
			||||||
import org.springframework.boot.test.context.SpringBootTest;
 | 
					import org.springframework.boot.test.context.SpringBootTest;
 | 
				
			||||||
 | 
					import org.springframework.boot.test.mock.mockito.MockBean;
 | 
				
			||||||
import org.springframework.test.context.ActiveProfiles;
 | 
					import org.springframework.test.context.ActiveProfiles;
 | 
				
			||||||
import top.suyiiyii.sims.entity.Role;
 | 
					import top.suyiiyii.sims.entity.Role;
 | 
				
			||||||
 | 
					import top.suyiiyii.sims.utils.S3Client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -12,11 +16,15 @@ import static org.junit.jupiter.api.Assertions.*;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@SpringBootTest
 | 
					@SpringBootTest
 | 
				
			||||||
@ActiveProfiles("test")
 | 
					@ActiveProfiles("test")
 | 
				
			||||||
 | 
					@ExtendWith(MockitoExtension.class)
 | 
				
			||||||
class RbacServiceTest {
 | 
					class RbacServiceTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Autowired
 | 
					    @Autowired
 | 
				
			||||||
    private RbacService rbacService;
 | 
					    private RbacService rbacService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @MockBean
 | 
				
			||||||
 | 
					    private S3Client s3Client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    void addRoleWithUserId() {
 | 
					    void addRoleWithUserId() {
 | 
				
			||||||
        int userId = 1; // mock userId
 | 
					        int userId = 1; // mock userId
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user