Initial commit

Made-with: Cursor
This commit is contained in:
lyf
2026-03-12 17:51:30 +08:00
commit 79102a18fb
1507 changed files with 480854 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
/mvnw text eol=lf
*.cmd text eol=crlf

33
astral-service/astral-common/.gitignore vendored Normal file
View File

@@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

View File

@@ -0,0 +1,77 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.astral</groupId>
<artifactId>astral-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>astral-common</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Java 9+ no longer bundles javax.activation; required by MimeTypeUtil -->
<dependency>
<groupId>com.sun.activation</groupId>
<artifactId>javax.activation</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<dependency>
<groupId>com.upyun</groupId>
<artifactId>java-sdk</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.12.6</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.18.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.17.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,30 @@
package com.astral.common.base;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 树节点
*/
@Data
public class TreeNode {
private String id;
private String key;
private String pkey;
private String label;
private Integer sortNum;
private String type;
@TableField(exist = false)
private List<TreeNode> children = new ArrayList<>();
}

View File

@@ -0,0 +1,30 @@
package com.astral.common.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "astral")
public class AstralConfig {
private static String uploadType;
private static String uploadDir;
public static String getUploadType() {
return uploadType;
}
public void setUploadType(String uploadType) {
AstralConfig.uploadType = uploadType;
}
public static String getUploadDir() {
return uploadDir;
}
public void setUploadDir(String uploadDir) {
AstralConfig.uploadDir = uploadDir;
}
}

View File

@@ -0,0 +1,31 @@
package com.astral.common.config;
import com.astral.common.utils.UpYunUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UpyunConfig {
@Value("${upyun.bucket}")
private String bucket;
@Value("${upyun.operator}")
private String operator;
@Value("${upyun.password}")
private String password;
@Value("${upyun.domain}")
private String domain;
@Bean
public void initUpyun(){
UpYunUtil.setBucket(bucket);
UpYunUtil.setOperator(operator);
UpYunUtil.setPassword(password);
UpYunUtil.setDomain(domain);
}
}

View File

@@ -0,0 +1,9 @@
package com.astral.common.constant;
/**
* 业务枚举
*/
public class BusinessEmnu {
}

View File

@@ -0,0 +1,23 @@
package com.astral.common.constant;
public class CommonConstant {
public static final Integer SC_OK_200 = 200;
public static final Integer SC_ERROR_500 = 500;
public static final Integer SC_ERROR_401 = 401;
/**
* 无权限访问返回码
*/
public static final Integer SC_JEECG_NO_AUTHZ = 510;
/**
* 文件上传类型本地local,upyun,minio
*/
public static final String UPLOAD_TYPE_LOCAL = "local";
public static final String UPLOAD_TYPE_UPYUN = "upyun";
public static final String UPLOAD_TYPE_MINIO = "minio";
}

View File

@@ -0,0 +1,212 @@
package com.astral.common.controller;
import com.astral.common.config.AstralConfig;
import com.astral.common.config.UpyunConfig;
import com.astral.common.constant.CommonConstant;
import com.astral.common.result.Result;
import com.astral.common.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
@RestController
@RequestMapping("/common")
public class CommonController {
@GetMapping("/download")
public void download(String fileUrl, HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
if (!FileUtils.checkAllowDownload(fileUrl)) {
throw new RuntimeException("资源文件(" + fileUrl + ")非法,不允许下载。 ");
}
// 本地资源路径
String localPath = AstralConfig.getUploadDir();
// 数据库资源地址
String downloadPath = localPath + "/" + fileUrl;
// 下载名称
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
// downloadName = URLDecoder.decode(downloadName, "UTF-8");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, downloadName);
FileUtils.writeBytes(downloadPath, response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
@GetMapping(value = "/static/**")
public void getFile(HttpServletRequest request, HttpServletResponse response) {
String imgPath = extractPathFromPattern(request);
if(StringUtils.isBlank(imgPath) || "null".equals(imgPath)){
return;
}
if (CommonConstant.UPLOAD_TYPE_UPYUN.equals(AstralConfig.getUploadType())) {
String url = UpYunUtil.getDomain() + "/" + imgPath;
try {
response.sendRedirect(url);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
imgPath = imgPath.replace("..", "").replace("../","");
if (imgPath.endsWith(",")) {
imgPath = imgPath.substring(0, imgPath.length() - 1);
}
String filePath = AstralConfig.getUploadDir() + "/" + imgPath;
// 先将加号替换为URL编码形式避免被转为空格
filePath = filePath.replaceAll("\\+", "%2B");
filePath = URLDecoder.decode(filePath, "UTF-8");
File file = new File(filePath);
if(!file.exists()){
// Missing resource should be a normal 404, not a server error (500).
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 获取文件扩展名
String fileName = file.getName();
String extension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
// 自动获取MIME类型
String mimeType = MimeTypeUtil.getContentType(fileName);
response.setContentType(mimeType);
// 根据文件类型确定处理方式
if (shouldDisplayInline(extension)) {
// 可预览文件 - 内联显示
response.setHeader("Content-Disposition", "inline; filename=\"" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1") + "\"");
} else {
// 不可预览文件 - 下载
response.setHeader("Content-Disposition", "attachment; filename=\"" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1") + "\"");
}
// response.addHeader("Content-Disposition", "attachment;fileName=" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1"));
// 强制缓存
response.setHeader("Cache-Control", "max-age=31536000");
inputStream = new BufferedInputStream(new FileInputStream(filePath));
outputStream = response.getOutputStream();
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, len);
}
response.flushBuffer();
} catch (IOException e) {
e.printStackTrace();
response.setStatus(404);
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// 判断是否可内联显示
private boolean shouldDisplayInline(String extension) {
switch (extension) {
// 图片格式
case "jpg": case "jpeg": case "png":
case "gif": case "bmp": case "svg":
// 文本格式
case "txt": case "csv":
// 网页格式
case "html": case "htm":
return true;
// 默认不可内联显示
default:
return false;
}
}
private static String extractPathFromPattern(final HttpServletRequest request) {
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
}
@PostMapping("/upload")
public Result<?> uploadFile(HttpServletRequest request, MultipartFile file) throws Exception {
try {
String bizPath = request.getParameter("biz");
String type = request.getParameter("type");
// 是否为文件名添加时间戳后缀
boolean needTimestampSuffix = !(bizPath != null && bizPath.contains("upload/3DEditor"));
if ("Tiles".equals(type)) {
String originalFilename = file.getOriginalFilename();
int subIndex = originalFilename.lastIndexOf(".");
String fileExtension = originalFilename.substring(subIndex);
if (".zip".equals(fileExtension)) {
String zipDirectory = originalFilename.substring(0, subIndex) + "-" + System.currentTimeMillis();
if (StringUtils.isNotBlank(bizPath)) {
zipDirectory = bizPath + "/" + zipDirectory;
}
String targetDirectory = AstralConfig.getUploadDir() + "/" + zipDirectory;
boolean flag = ZipUtil.extractZipCheckFile(file, targetDirectory, "tileset.json");
if (!flag) {
CommonUtils.deleteFile(zipDirectory);
throw new RuntimeException("文件异常请检查上传的zip文件是否包含tileset.json文件");
}
return Result.success("上传成功", zipDirectory);
} else {
String fileName = CommonUtils.upload(bizPath, file, needTimestampSuffix);
return Result.success("上传成功",fileName);
}
} else {
// 上传并返回新文件名称
String fileName = CommonUtils.upload(bizPath, file, needTimestampSuffix);
return Result.success("上传成功",fileName);
}
} catch (Exception e) {
e.printStackTrace();
return Result.error(e.getMessage());
}
}
@GetMapping("/deleteFile")
public Result<?> deleteFile(String fileUrl) {
return Result.toAjax(CommonUtils.deleteFile(fileUrl));
}
@PostMapping("/zipUploadExtraction")
public Result<?> zipUploadExtraction(HttpServletRequest request, MultipartFile file) throws Exception {
try {
String bizPath = request.getParameter("biz");
// 上传并返回新文件名称
ZipUtil.extractZip(file, bizPath);
return Result.success("上传成功");
} catch (Exception e) {
e.printStackTrace();
return Result.error(e.getMessage());
}
}
}

View File

@@ -0,0 +1,138 @@
package com.astral.common.result;
import com.astral.common.constant.CommonConstant;
import lombok.Data;
import java.io.Serializable;
@Data
public class Result<T> implements Serializable {
private static final long serialVersionUID = -6098323041897792633L;
private Integer code;
private boolean success;
private String message;
private T result;
public Result() {
}
public Result(Integer code, String message) {
this.code = code;
this.message = message;
}
public static<T> Result<T> success() {
Result<T> result = new Result<T>();
result.setCode(CommonConstant.SC_OK_200);
result.setSuccess(true);
return result;
}
public static<T> Result<T> success(String message) {
Result<T> result = new Result<T>();
result.setCode(CommonConstant.SC_OK_200);
result.setSuccess(true);
result.setMessage(message);
return result;
}
public static<T> Result<T> success(T data) {
Result<T> result = new Result<T>();
result.setCode(CommonConstant.SC_OK_200);
result.setSuccess(true);
result.setResult(data);
return result;
}
public static<T> Result<T> success(Boolean flag) {
Result<T> result = new Result<T>();
result.setCode(CommonConstant.SC_OK_200);
result.setSuccess(flag);
return result;
}
public static<T> Result<T> success(String message, T data) {
Result<T> result = new Result<T>();
result.setCode(CommonConstant.SC_OK_200);
result.setSuccess(true);
result.setMessage(message);
result.setResult(data);
return result;
}
public static<T> Result<T> error(String message) {
Result<T> result = new Result<T>();
result.setCode(CommonConstant.SC_ERROR_500);
result.setSuccess(false);
result.setMessage(message);
return result;
}
public static<T> Result<T> error(Integer code, String message) {
Result<T> result = new Result<T>();
result.setCode(code);
result.setSuccess(false);
result.setMessage(message);
return result;
}
public static<T> Result<T> error(String message, T data) {
Result<T> result = new Result<T>();
result.setCode(CommonConstant.SC_ERROR_500);
result.setSuccess(false);
result.setMessage(message);
result.setResult(data);
return result;
}
public static<T> Result<T> error401(String message) {
Result<T> result = new Result<T>();
result.setCode(CommonConstant.SC_ERROR_401);
result.setSuccess(false);
result.setMessage(message);
return result;
}
public static<T> Result<T> error401(String message, T data) {
Result<T> result = new Result<T>();
result.setCode(CommonConstant.SC_ERROR_401);
result.setSuccess(false);
result.setMessage(message);
result.setResult(data);
return result;
}
/**
* 无权限访问返回结果
*/
public static<T> Result<T> noauth(String msg) {
return error(CommonConstant.SC_JEECG_NO_AUTHZ, msg);
}
public static<T> Result<T> toAjax(Boolean flag) {
Result<T> result = new Result<T>();
if (flag) {
result.setCode(CommonConstant.SC_OK_200);
result.setSuccess(true);
} else {
result.setCode(CommonConstant.SC_ERROR_500);
result.setSuccess(false);
}
return result;
}
public static<T> Result<T> toAjax(int row) {
Result<T> result = new Result<T>();
if (row > 0) {
result.setCode(CommonConstant.SC_OK_200);
result.setSuccess(true);
} else {
result.setCode(CommonConstant.SC_ERROR_500);
result.setSuccess(false);
}
return result;
}
}

View File

@@ -0,0 +1,90 @@
package com.astral.common.utils;
import com.astral.common.config.AstralConfig;
import com.astral.common.constant.CommonConstant;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
public class CommonUtils {
private static String defaultUploadType = AstralConfig.getUploadType();
// 重载方法,保持原签名(默认需要时间戳后缀)
public static String upload(String bizPath, MultipartFile file) {
return upload(bizPath, file, true);
}
public static String upload(String bizPath, MultipartFile file, boolean needTimestampSuffix) {
String result = "";
try {
if (CommonConstant.UPLOAD_TYPE_UPYUN.equals(defaultUploadType)) {
result = UpYunUtil.upload(bizPath, FileUtils.convertFile(file,needTimestampSuffix));
} else {
result = FileUploadUtils.upload(bizPath, file,needTimestampSuffix);
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return result;
}
public static String upload(String bizPath, File file) {
return upload(bizPath, file, true);
}
public static String upload(String bizPath, File file, boolean needTimestampSuffix) {
String result = "";
try {
if (CommonConstant.UPLOAD_TYPE_UPYUN.equals(defaultUploadType)) {
result = UpYunUtil.upload(bizPath, file);
} else {
result = FileUploadUtils.upload(bizPath, file, needTimestampSuffix);
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return result;
}
/**
* 通用文件删除(慎用)
* @param dirPath
* @return
*/
public static Boolean deleteFile(String dirPath) {
if (StringUtils.isBlank(dirPath)) {
return false;
}
dirPath = AstralConfig.getUploadDir() + "/" + dirPath;
try {
if (CommonConstant.UPLOAD_TYPE_UPYUN.equals(defaultUploadType)) {
return UpYunUtil.deleteFile(dirPath);
} else {
File file = new File(dirPath);
org.apache.commons.io.FileUtils.forceDelete(file);
return true;
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("文件删除失败:" + e.getMessage());
}
}
/**
* 验证是否是json字符串
*/
public static Boolean isJsonValid(String jsonStr) {
try {
ObjectMapper mapper = new ObjectMapper();
mapper.readTree(jsonStr);
return true;
} catch (Exception e) {
return false;
}
}
}

View File

@@ -0,0 +1,99 @@
package com.astral.common.utils;
import com.astral.common.config.AstralConfig;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
/**
* 文件上传工具类
*/
public class FileUploadUtils {
public static final int DEFAULT_FILE_NAME_LENGTH = 100;
public static final long DEFAULT_MAX_SIZE = 10L * 1024 * 1024 * 1024;
private static String defaultBaseDir = AstralConfig.getUploadDir();
public static String getDefaultBaseDir() {
return defaultBaseDir;
}
public static final String upload(String baseDir, MultipartFile file) throws IOException{
return upload(baseDir, file,true);
}
public static final String upload(String baseDir, MultipartFile file, boolean needTimestampSuffix) throws IOException{
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
throw new RuntimeException("文件名长度超过限制");
}
String fileName = "";
if(needTimestampSuffix){
String originalFilename = file.getOriginalFilename();
int subIndex = originalFilename.lastIndexOf(".");
String fileExtension = originalFilename.substring(subIndex);
fileName = originalFilename.substring(0, subIndex) + "_" + System.currentTimeMillis() + fileExtension;
}else{
fileName = file.getOriginalFilename();
}
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
file.transferTo(Paths.get(absPath));
return getPathFileName(baseDir, fileName);
}
public static final String upload(String baseDir, File file) throws IOException{
return upload(baseDir, file,true);
}
public static final String upload(String baseDir, File file, boolean needTimestampSuffix) throws IOException {
int fileNamelength = Objects.requireNonNull(file.getName()).length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
throw new RuntimeException("文件名长度超过限制");
}
String fileName = "";
if(needTimestampSuffix){
String originalFilename = file.getName();
int subIndex = originalFilename.lastIndexOf(".");
String fileExtension = originalFilename.substring(subIndex);
fileName = originalFilename.substring(0, subIndex) + "_" + System.currentTimeMillis() + fileExtension;
}else{
fileName = file.getName();
}
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
Files.copy(file.toPath(), Paths.get(absPath), StandardCopyOption.REPLACE_EXISTING);
return getPathFileName(baseDir, fileName);
}
public static final File getAbsoluteFile(String uploadDir, String fileName) {
File desc = new File(defaultBaseDir + "/" + getPathFileName(uploadDir, fileName));
if (!desc.exists()) {
if (!desc.getParentFile().exists()) {
desc.getParentFile().mkdirs();
}
}
return desc;
}
public static final String getPathFileName(String uploadDir, String fileName) {
if (StringUtils.hasLength(uploadDir)) {
return uploadDir + "/" + fileName;
} else {
return fileName;
}
}
}

View File

@@ -0,0 +1,115 @@
package com.astral.common.utils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
public class FileUtils {
public static File convertFile(MultipartFile file) throws IOException {
return convertFile(file,true);
}
public static File convertFile(MultipartFile file, boolean needTimestampSuffix) throws IOException {
String fileName = "";
if(needTimestampSuffix){
String originalFilename = file.getOriginalFilename();
int subIndex = originalFilename.lastIndexOf(".");
String fileExtension = originalFilename.substring(subIndex);
fileName = originalFilename.substring(0, subIndex) + "_" + System.currentTimeMillis() + fileExtension;
}else{
fileName = file.getOriginalFilename();
}
File convFile = new File(fileName);
convFile.createNewFile();
try (FileOutputStream fos = new FileOutputStream(convFile)) {
fos.write(file.getBytes());
}
return convFile;
}
public static boolean checkAllowDownload(String resource) {
// 禁止目录上跳级别
if (StringUtils.contains(resource, "..")) {
return false;
}
// todo 检查允许下载的文件规则
return true;
}
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
String percentEncodedFileName = percentEncode(realFileName);
StringBuilder contentDispositionValue = new StringBuilder();
contentDispositionValue.append("attachment; filename=")
.append(percentEncodedFileName)
.append(";")
.append("filename*=")
.append("utf-8''")
.append(percentEncodedFileName);
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
response.setHeader("Content-disposition", contentDispositionValue.toString());
response.setHeader("download-filename", percentEncodedFileName);
}
public static String percentEncode(String s) throws UnsupportedEncodingException {
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
return encode.replaceAll("\\+", "%20");
}
public static void writeBytes(String filePath, OutputStream os) throws IOException {
FileInputStream fis = null;
try {
File file = new File(filePath);
if (!file.exists()) {
throw new FileNotFoundException(filePath);
}
fis = new FileInputStream(file);
byte[] b = new byte[1024];
int length;
while ((length = fis.read(b)) > 0) {
os.write(b, 0, length);
}
} catch (IOException e) {
throw e;
} finally {
IOUtils.close(os);
IOUtils.close(fis);
}
}
public static void deleteFolder(String folderPath) throws IOException {
Path path = Paths.get(folderPath);
if (Files.exists(path)) {
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
if (exc == null) {
Files.delete(dir);
return FileVisitResult.CONTINUE;
} else {
throw exc;
}
}
});
}
}
}

View File

@@ -0,0 +1,172 @@
package com.astral.common.utils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
private static final String SECRET_KEY = "JWT-Secret-Key";
private static final int DEFAULT_EXPIRE_SECONDS = 120 * 60;
private static final int PASSWORD_HASH_BYTES = 16;
private static final SecretKey key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
public static String generateToken(String userName, Long userId, int expiredSeconds) {
if (expiredSeconds == 0) {
expiredSeconds = DEFAULT_EXPIRE_SECONDS;
}
Date expireAt = Date.from(Instant.now().plusSeconds(expiredSeconds));
Map<String, Object> claims = new HashMap<>();
claims.put("UserID", userId);
return Jwts.builder()
.setClaims(claims)
.setIssuer(userName)
.setIssuedAt(new Date())
.setExpiration(expireAt)
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
public static JwtPayload validateToken(String tokenString) {
try {
Jws<Claims> claimsJws = Jwts.parser()
.setSigningKey(key)
.build()
.parseClaimsJws(tokenString);
Claims claims = claimsJws.getBody();
return new JwtPayload(
claims.getIssuer(),
((Number) claims.get("UserID")).intValue(),
claims.getIssuedAt().getTime(),
claims.getExpiration().getTime()
);
} catch (JwtException e) {
throw new RuntimeException("Error: Unable to validate token");
}
}
public static String refreshToken(String tokenString) {
try {
Claims claims = Jwts.parser()
.setSigningKey(key)
.build()
.parseClaimsJws(tokenString)
.getBody();
Date expireAt = Date.from(Instant.now().plusSeconds(DEFAULT_EXPIRE_SECONDS));
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(new Date())
.setExpiration(expireAt)
.signWith(key, SignatureAlgorithm.HS256)
.compact();
} catch (JwtException e) {
throw new RuntimeException("Error: Failed to generate new fresh JWT");
}
}
public static String generateSalt() {
byte[] buf = new byte[PASSWORD_HASH_BYTES];
new SecureRandom().nextBytes(buf);
return Hex.encodeHexString(buf);
}
public static String generatePassHash(String password, String salt) {
try {
return Hex.encodeHexString(
javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
.generateSecret(new javax.crypto.spec.PBEKeySpec(
password.toCharArray(),
salt.getBytes(),
16384,
PASSWORD_HASH_BYTES * 8))
.getEncoded()
);
} catch (Exception e) {
throw new RuntimeException("Error: failed to generate password hash");
}
}
public static TokenStatus checkStatus(String tokenString) {
try {
JwtPayload jp = validateToken(tokenString);
long timeDiff = (jp.getExpiresAt() - System.currentTimeMillis()) / 1000;
if (timeDiff <= 30) {
String newToken = refreshToken(tokenString);
return new TokenStatus(newToken, timeDiff);
}
return new TokenStatus(tokenString, timeDiff);
} catch (Exception e) {
return new TokenStatus("", -1L);
}
}
public static class TokenStatus {
private final String token;
private final long timeDiff;
public TokenStatus(String token, long timeDiff) {
this.token = token;
this.timeDiff = timeDiff;
}
public String getToken() {
return token;
}
public long getTimeDiff() {
return timeDiff;
}
}
public static class JwtPayload {
private final String username;
private final int userId;
private final long issuedAt;
private final long expiresAt;
public JwtPayload(String username, int userId, long issuedAt, long expiresAt) {
this.username = username;
this.userId = userId;
this.issuedAt = issuedAt;
this.expiresAt = expiresAt;
}
public String getUsername() {
return username;
}
public int getUserId() {
return userId;
}
public long getIssuedAt() {
return issuedAt;
}
public long getExpiresAt() {
return expiresAt;
}
}
}

View File

@@ -0,0 +1,21 @@
package com.astral.common.utils;
import javax.activation.MimetypesFileTypeMap;
import java.io.IOException;
import java.io.InputStream;
public class MimeTypeUtil {
private static final MimetypesFileTypeMap MIME_TYPES;
static {
try (InputStream is = MimeTypeUtil.class.getResourceAsStream("/mime.types")) {
MIME_TYPES = new MimetypesFileTypeMap(is);
} catch (IOException e) {
throw new RuntimeException("Failed to load mime.types", e);
}
}
public static String getContentType(String fileName) {
return MIME_TYPES.getContentType(fileName);
}
}

View File

@@ -0,0 +1,111 @@
package com.astral.common.utils;
import com.astral.common.base.TreeNode;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 树构造工具类
* @Author yx
*/
public class TreeBuilderUtil {
// /**
// * 构建树形结构
// * @param nodes 所有节点列表
// * @return 树形结构列表
// */
// public static <T extends TreeNode<T>> List<T> buildTree(List<T> nodes) {
// // 获取所有根节点
// List<T> rootNodes = nodes.stream()
// .filter(node -> StringUtils.isBlank(node.getPkey()))
// .sorted(Comparator.comparingInt(T::getSortNum))
// .collect(Collectors.toList());
//
// // 为每个根节点构建子树
// for (T rootNode : rootNodes) {
// buildChildNodes(rootNode, nodes);
// }
//
// return rootNodes;
// }
//
// /**
// * 递归构建子节点
// * @param parentNode 父节点
// * @param nodes 所有节点列表
// */
// private static <T extends TreeNode<T>> void buildChildNodes(T parentNode, List<T> nodes) {
// // 获取当前节点的所有子节点
// List<T> children = nodes.stream()
// .filter(node -> parentNode.getKey().equals(node.getPkey()))
// .sorted(Comparator.comparingInt(T::getSortNum))
// .collect(Collectors.toList());
//
// // 递归构建每个子节点的子节点
// for (T child : children) {
// buildChildNodes(child, nodes);
// }
//
// // 设置子节点列表
// parentNode.setChildren(children);
// }
// /**
// * 构建树形结构
// * @param nodes 所有节点列表
// * @return 树形结构列表
// */
// public static <T extends TreeNode<T>> List<TreeNode> build(List<T> nodes) {
// // 按父节点ID分组
// Map<String, List<T>> pCodeMap = nodes.stream()
// .filter(node -> StringUtils.isNotBlank(node.getPkey()))
// .collect(Collectors.groupingBy(T::getPkey));
//
// // 设置子节点
// nodes.forEach(node -> {
// List<T> children = pCodeMap.get(node.getKey());
// if (children != null && !children.isEmpty()) {
// // 排序(如果有sort字段)
// children.sort(Comparator.comparingInt(T::getSortNum));
// node.setChildren(children);
// }
// });
//
// // 获取所有根节点
// return nodes.stream()
// .filter(node -> StringUtils.isBlank(node.getPkey()))
// .sorted(Comparator.comparingInt(T::getSortNum))
// .collect(Collectors.toList());
// }
public static List<TreeNode> buildTree(List<TreeNode> nodes) {
// 按父节点ID分组
Map<String, List<TreeNode>> pCodeMap = nodes.stream()
.filter(node -> StringUtils.isNotBlank(node.getPkey()))
.collect(Collectors.groupingBy(treeNode -> treeNode.getPkey() + "_" + treeNode.getType()));
// 设置子节点
nodes.forEach(node -> {
List<TreeNode> children = pCodeMap.get(node.getKey() + "_" + node.getType());
if (children != null && !children.isEmpty()) {
// 排序(如果有sort字段)
children.sort(Comparator.comparing(TreeNode::getSortNum, Comparator.nullsLast(Comparator.naturalOrder())));
node.setChildren(children);
}
});
// 获取所有根节点
return nodes.stream()
.filter(node -> StringUtils.isBlank(node.getPkey()))
.sorted(Comparator.comparing(TreeNode::getSortNum, Comparator.nullsLast(Comparator.naturalOrder())))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,117 @@
package com.astral.common.utils;
import com.UpYun;
import com.astral.common.config.AstralConfig;
import com.upyun.FormUploader;
import com.upyun.Params;
import com.upyun.Result;
import com.upyun.UpException;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class UpYunUtil {
private static String bucket;
private static String operator;
private static String password;
private static String domain;
public static void setBucket(String bucket) {
UpYunUtil.bucket = bucket;
}
public static String getBucket() {
return bucket;
}
public static void setOperator(String operator) {
UpYunUtil.operator = operator;
}
public static String getOperator() {
return operator;
}
public static void setPassword(String password) {
UpYunUtil.password = password;
}
public static String getPassword() {
return password;
}
public static String getDomain() {
return domain;
}
public static void setDomain(String domain) {
UpYunUtil.domain = domain;
}
public static String upload(String savePath, File file) {
// 创建FormUploader实例
FormUploader uploader = new FormUploader(bucket, operator, password);
// 设置上传参数
Map<String, Object> paramsMap = new HashMap<>();
String uploadDir = AstralConfig.getUploadDir();
if (StringUtils.hasLength(uploadDir)) {
savePath = uploadDir + "/" + savePath;
}
paramsMap.put(Params.SAVE_KEY, savePath);
try {
// 执行上传
Result result = uploader.upload(paramsMap, file);
// 处理上传结果
if (result.isSucceed()) {
System.out.println("文件上传成功!");
} else {
System.out.println("文件上传失败!");
System.out.println("错误信息: " + result.getMsg());
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("文件上传失败->" + e.getMessage());
}
return savePath;
}
public static boolean deleteFile(String dirPath) throws IOException, UpException {
UpYun upyun = new UpYun(bucket, operator, password);
Map<String, String> params = new HashMap<String, String>();
params.put(UpYun.PARAMS.KEY_X_LIST_LIMIT.getValue(), "100");
List<UpYun.FolderItem> folderItems = new ArrayList<>();
do {
folderItems = upyun.readDir(dirPath, params);
if (!CollectionUtils.isEmpty(folderItems)) {
folderItems.forEach(item -> {
try {
if ("folder".equals(item.type) || "Folder".equals(item.type)) {
deleteFile(dirPath + "/" + item.name);
} else {
upyun.deleteFile(dirPath + "/" + item.name, null);
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (UpException e) {
throw new RuntimeException(e);
}
});
}
} while (!CollectionUtils.isEmpty(folderItems));
return upyun.rmDir(dirPath);
}
}

View File

@@ -0,0 +1,89 @@
package com.astral.common.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipUtil {
private static final Logger logger = LoggerFactory.getLogger(ZipUtil.class);
/**
* 解压ZIP文件到指定目录
*/
public static void extractZip(MultipartFile zipFile, String targetDirectory) throws IOException {
// 创建目标目录
File targetDir = new File(targetDirectory);
if (!targetDir.exists()) {
targetDir.mkdirs();
}
try {
ZipInputStream zipInputStream = new ZipInputStream(zipFile.getInputStream());
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
if (!entry.isDirectory()) {
File extractedFile = extractFile(zipInputStream, targetDir, entry.getName());
}
zipInputStream.closeEntry();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static boolean extractZipCheckFile(MultipartFile zipFile, String targetDirectory, String checkFileName) throws IOException {
// 创建目标目录
File targetDir = new File(targetDirectory);
if (!targetDir.exists()) {
targetDir.mkdirs();
}
boolean flag = false;
try {
ZipInputStream zipInputStream = new ZipInputStream(zipFile.getInputStream());
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
if (!entry.isDirectory()) {
File extractedFile = extractFile(zipInputStream, targetDir, entry.getName());
System.out.println(entry.getName());
if (checkFileName.equals(entry.getName())) {
flag = true;
}
}
zipInputStream.closeEntry();
}
} catch (IOException e) {
e.printStackTrace();
}
return flag;
}
private static File extractFile(ZipInputStream zipInputStream, File targetDir, String fileName) throws IOException {
File file = new File(targetDir, fileName);
// 创建父目录
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
try (FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
byte[] buffer = new byte[1024];
int length;
while ((length = zipInputStream.read(buffer)) > 0) {
bos.write(buffer, 0, length);
}
}
return file;
}
}