最新产品
This commit is contained in:
54
src/main/java/com/yj/earth/ServerApp.java
Normal file
54
src/main/java/com/yj/earth/ServerApp.java
Normal file
@ -0,0 +1,54 @@
|
||||
package com.yj.earth;
|
||||
|
||||
import com.yj.earth.business.service.SourceService;
|
||||
import com.yj.earth.common.config.ServerConfig;
|
||||
import com.yj.earth.common.service.ServerInitService;
|
||||
import com.yj.earth.common.util.SdkUtil;
|
||||
import com.yj.earth.datasource.DatabaseManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
@EnableAspectJAutoProxy
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
||||
public class ServerApp implements CommandLineRunner {
|
||||
@Resource
|
||||
private SourceService sourceService;
|
||||
@Resource
|
||||
private ServerConfig serverConfig;
|
||||
@Resource
|
||||
private ServerInitService serverInitService;
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
// 启动项目SDK服务
|
||||
SdkUtil.startSdkIfConfigured();
|
||||
// 初始化数据库相关操作
|
||||
String activeDataSource = DatabaseManager.getActiveDataSource();
|
||||
// 获取数据库类型
|
||||
DatabaseManager.DatabaseType dbType = DatabaseManager.DatabaseType.valueOf(activeDataSource.toUpperCase());
|
||||
// 初始化数据库
|
||||
DatabaseManager.initDatabase(dbType);
|
||||
// 启动应用服务
|
||||
SpringApplication application = new SpringApplication(ServerApp.class);
|
||||
// 允许循环引用
|
||||
application.setAllowCircularReferences(true);
|
||||
// 启动服务
|
||||
application.run(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
// 初始化资源
|
||||
serverInitService.init();
|
||||
// 检查默认数据
|
||||
serverInitService.checkDefaultData();
|
||||
log.info("项目文档地址: {}", "http://" + serverConfig.getHost() + ":" + serverConfig.getPort() + "/doc.html");
|
||||
}
|
||||
}
|
||||
22
src/main/java/com/yj/earth/annotation/EncryptResponse.java
Normal file
22
src/main/java/com/yj/earth/annotation/EncryptResponse.java
Normal file
@ -0,0 +1,22 @@
|
||||
package com.yj.earth.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 返回结果加密注解
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface EncryptResponse {
|
||||
|
||||
/**
|
||||
* 加密密钥的配置键
|
||||
*/
|
||||
String keyProperty() default "encrypt.aes.key";
|
||||
|
||||
/**
|
||||
* 加密算法模式
|
||||
*/
|
||||
String algorithm() default "AES/CBC/PKCS5Padding";
|
||||
}
|
||||
34
src/main/java/com/yj/earth/annotation/ExcludeField.java
Normal file
34
src/main/java/com/yj/earth/annotation/ExcludeField.java
Normal file
@ -0,0 +1,34 @@
|
||||
package com.yj.earth.annotation;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.PropertyWriter;
|
||||
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 用于标记需要在JSON序列化时排除的字段
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ExcludeField {
|
||||
|
||||
/**
|
||||
* Jackson 序列化过滤器、用于过滤被 @ExcludeField 注解标记的字段
|
||||
*/
|
||||
class Filter extends SimpleBeanPropertyFilter {
|
||||
@Override
|
||||
public void serializeAsField(Object pojo, JsonGenerator jsonGenerator, SerializerProvider provider, PropertyWriter writer) throws Exception {
|
||||
// 检查字段是否有 @ExcludeField 注解、如果有则不序列化该字段
|
||||
if (writer.getAnnotation(ExcludeField.class) != null) {
|
||||
return;
|
||||
}
|
||||
// 没有注解的字段正常序列化
|
||||
super.serializeAsField(pojo, jsonGenerator, provider, writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/main/java/com/yj/earth/annotation/RoleAccess.java
Normal file
16
src/main/java/com/yj/earth/annotation/RoleAccess.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.yj.earth.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 角色访问控制注解
|
||||
*/
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface RoleAccess {
|
||||
/**
|
||||
* 允许访问的角色名称数组
|
||||
*/
|
||||
String[] roleNames();
|
||||
}
|
||||
9
src/main/java/com/yj/earth/annotation/SourceType.java
Normal file
9
src/main/java/com/yj/earth/annotation/SourceType.java
Normal file
@ -0,0 +1,9 @@
|
||||
package com.yj.earth.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SourceType {
|
||||
String value();
|
||||
}
|
||||
64
src/main/java/com/yj/earth/aspect/EncryptResponseAspect.java
Normal file
64
src/main/java/com/yj/earth/aspect/EncryptResponseAspect.java
Normal file
@ -0,0 +1,64 @@
|
||||
package com.yj.earth.aspect;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yj.earth.annotation.EncryptResponse;
|
||||
import com.yj.earth.common.util.AesEncryptUtil;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 响应加密切面: 拦截 @EncryptResponse 注解的方法、对返回结果进行加密
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class EncryptResponseAspect {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final Environment environment;
|
||||
|
||||
/**
|
||||
* 定义切点: 拦截所有被 @EncryptResponse 标记的方法
|
||||
*/
|
||||
@Pointcut("@annotation(encryptResponse)")
|
||||
public void pointCut(EncryptResponse encryptResponse) {}
|
||||
|
||||
/**
|
||||
* 环绕通知: 对方法返回结果进行加密处理
|
||||
*/
|
||||
@Around("pointCut(encryptResponse)")
|
||||
public Object around(ProceedingJoinPoint joinPoint, EncryptResponse encryptResponse) throws Throwable {
|
||||
// 执行原方法、获取返回结果
|
||||
Object result = joinPoint.proceed();
|
||||
|
||||
// 从配置文件获取密钥
|
||||
String key = environment.getProperty(encryptResponse.keyProperty());
|
||||
if (key == null || key.isEmpty()) {
|
||||
log.error("加密密钥未配置、keyProperty: {}", encryptResponse.keyProperty());
|
||||
throw new RuntimeException("加密密钥未配置");
|
||||
}
|
||||
|
||||
// 将返回结果转为JSON字符串
|
||||
String jsonResult;
|
||||
try {
|
||||
jsonResult = objectMapper.writeValueAsString(result);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("返回结果转JSON失败", e);
|
||||
throw new RuntimeException("返回结果序列化失败");
|
||||
}
|
||||
|
||||
// 执行加密
|
||||
String encryptedResult = AesEncryptUtil.encrypt(jsonResult, key, encryptResponse.algorithm());
|
||||
log.debug("接口返回结果已加密、原始长度: {}、加密后长度: {}", jsonResult.length(), encryptedResult.length());
|
||||
return ApiResponse.success(encryptedResult);
|
||||
}
|
||||
}
|
||||
78
src/main/java/com/yj/earth/aspect/RoleAccessAspect.java
Normal file
78
src/main/java/com/yj/earth/aspect/RoleAccessAspect.java
Normal file
@ -0,0 +1,78 @@
|
||||
package com.yj.earth.aspect;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.yj.earth.annotation.RoleAccess;
|
||||
import com.yj.earth.business.domain.User;
|
||||
import com.yj.earth.business.domain.Role;
|
||||
import com.yj.earth.business.service.UserService;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 角色访问控制切面
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class RoleAccessAspect {
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@Resource
|
||||
private RoleService roleService;
|
||||
|
||||
/**
|
||||
* 环绕通知、验证角色权限
|
||||
*/
|
||||
@Around("@annotation(com.yj.earth.annotation.RoleAccess)")
|
||||
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
// 获取当前登录用户ID
|
||||
if (!StpUtil.isLogin()) {
|
||||
return ApiResponse.failure("请先登录");
|
||||
}
|
||||
String userId = StpUtil.getLoginIdAsString();
|
||||
|
||||
// 获取用户信息
|
||||
User user = userService.getById(userId);
|
||||
if (user == null) {
|
||||
return ApiResponse.failure("用户不存在");
|
||||
}
|
||||
|
||||
// 获取用户角色信息
|
||||
Role role = roleService.getById(user.getRoleId());
|
||||
if (role == null) {
|
||||
return ApiResponse.failure("用户角色不存在");
|
||||
}
|
||||
|
||||
// 获取注解信息
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
RoleAccess roleAccess = method.getAnnotation(RoleAccess.class);
|
||||
String[] allowedRoles = roleAccess.roleNames();
|
||||
|
||||
// 验证角色是否有权限
|
||||
boolean hasPermission = false;
|
||||
for (String roleName : allowedRoles) {
|
||||
if (roleName.equals(role.getRoleName())) {
|
||||
hasPermission = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasPermission) {
|
||||
return ApiResponse.failure("没有访问权限、需要角色: " + String.join(",", allowedRoles));
|
||||
}
|
||||
|
||||
// 有权限、执行原方法
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
}
|
||||
74
src/main/java/com/yj/earth/auth/AuthGenerator.java
Normal file
74
src/main/java/com/yj/earth/auth/AuthGenerator.java
Normal file
@ -0,0 +1,74 @@
|
||||
package com.yj.earth.auth;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yj.earth.common.util.ServerUniqueIdUtil;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 授权生成工具类
|
||||
*/
|
||||
public class AuthGenerator {
|
||||
// AES加密密钥(16位)
|
||||
private static final String AES_KEY = "7AJD6H5AGHY6SJU7";
|
||||
|
||||
// Jackson JSON处理器
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 生成授权信息
|
||||
*
|
||||
* @param versionType 版本类型
|
||||
* @param maxResourceCount 最大资源数量
|
||||
* @param authDays 授权天数
|
||||
* @param serverHardwareMd5 服务器硬件信息MD5
|
||||
* @return 加密后的授权字符串
|
||||
*/
|
||||
public static String generateAuth(String versionType, int maxResourceCount, int authDays, String serverHardwareMd5) {
|
||||
// 创建授权信息对象
|
||||
AuthInfo authInfo = new AuthInfo();
|
||||
authInfo.setVersionType(versionType);
|
||||
authInfo.setMaxResourceCount(maxResourceCount);
|
||||
authInfo.setAuthDays(authDays);
|
||||
authInfo.setServerHardwareMd5(serverHardwareMd5);
|
||||
|
||||
// 设置生成时间和过期时间
|
||||
Date now = new Date();
|
||||
authInfo.setGenerateTime(now);
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(now);
|
||||
calendar.add(Calendar.DAY_OF_YEAR, authDays);
|
||||
authInfo.setExpireTime(calendar.getTime());
|
||||
|
||||
// 序列化为JSON并加密
|
||||
String jsonStr = null;
|
||||
try {
|
||||
jsonStr = objectMapper.writeValueAsString(authInfo);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return encrypt(jsonStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* AES加密
|
||||
*/
|
||||
private static String encrypt(String content) {
|
||||
try {
|
||||
SecretKeySpec keySpec = new SecretKeySpec(AES_KEY.getBytes(StandardCharsets.UTF_8), "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
|
||||
byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.getEncoder().encodeToString(encrypted);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("授权信息加密失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/main/java/com/yj/earth/auth/AuthInfo.java
Normal file
32
src/main/java/com/yj/earth/auth/AuthInfo.java
Normal file
@ -0,0 +1,32 @@
|
||||
package com.yj.earth.auth;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 授权信息模型类
|
||||
* 存储授权相关的所有信息
|
||||
*/
|
||||
@Data
|
||||
public class AuthInfo {
|
||||
// 版本类型
|
||||
private String versionType;
|
||||
|
||||
// 最大可加载资源数量
|
||||
private int maxResourceCount;
|
||||
|
||||
// 授权天数
|
||||
private int authDays;
|
||||
|
||||
// 服务器硬件信息MD5
|
||||
private String serverHardwareMd5;
|
||||
|
||||
// 授权生成时间
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date generateTime;
|
||||
|
||||
// 授权过期时间
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date expireTime;
|
||||
}
|
||||
115
src/main/java/com/yj/earth/auth/AuthValidator.java
Normal file
115
src/main/java/com/yj/earth/auth/AuthValidator.java
Normal file
@ -0,0 +1,115 @@
|
||||
package com.yj.earth.auth;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yj.earth.common.util.ServerUniqueIdUtil;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 授权校验工具类
|
||||
*/
|
||||
public class AuthValidator {
|
||||
// 与生成工具类使用相同的AES密钥
|
||||
private static final String AES_KEY = "7AJD6H5AGHY6SJU7";
|
||||
|
||||
// Jackson JSON处理器
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 校验授权是否有效
|
||||
*
|
||||
* @param authString 加密的授权字符串
|
||||
* @param currentServerHardwareMd5 当前服务器硬件信息MD5
|
||||
* @return 授权是否有效的布尔值
|
||||
*/
|
||||
public static boolean validateAuth(String authString, String currentServerHardwareMd5) {
|
||||
try {
|
||||
// 解密授权信息
|
||||
AuthInfo authInfo = getAuthInfo(authString);
|
||||
|
||||
// 校验服务器硬件信息是否匹配
|
||||
if (!authInfo.getServerHardwareMd5().equals(currentServerHardwareMd5)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 校验授权是否过期
|
||||
Date now = new Date();
|
||||
return now.before(authInfo.getExpireTime());
|
||||
} catch (Exception e) {
|
||||
// 解密失败或格式错误均视为无效授权
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权详情
|
||||
*
|
||||
* @param authString 加密的授权字符串
|
||||
* @return 授权信息对象
|
||||
* @throws Exception 解密失败或格式错误时抛出异常
|
||||
*/
|
||||
public static AuthInfo getAuthInfo(String authString) {
|
||||
try {
|
||||
// 解密
|
||||
String decrypted = decrypt(authString);
|
||||
// 转换为 AuthInfo 对象
|
||||
return objectMapper.readValue(decrypted, AuthInfo.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AES解密
|
||||
*/
|
||||
private static String decrypt(String encryptedContent) throws Exception {
|
||||
SecretKeySpec keySpec = new SecretKeySpec(AES_KEY.getBytes(StandardCharsets.UTF_8), "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, keySpec);
|
||||
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedContent));
|
||||
return new String(decrypted, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权剩余天数
|
||||
*/
|
||||
public static int getRemainingDays(String authString){
|
||||
try {
|
||||
AuthInfo authInfo = getAuthInfo(authString);
|
||||
Date now = new Date();
|
||||
|
||||
// 如果已过期、返回0
|
||||
if (now.after(authInfo.getExpireTime())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 计算剩余天数
|
||||
long remainingMillis = authInfo.getExpireTime().getTime() - now.getTime();
|
||||
return (int) (remainingMillis / (24 * 60 * 60 * 1000)) + 1;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
String serverUniqueId = ServerUniqueIdUtil.getServerUniqueId();
|
||||
// 生成授权
|
||||
String authString = AuthGenerator.generateAuth("标准版", 1000, 30, serverUniqueId);
|
||||
System.out.println("授权字符串:" + authString);
|
||||
// 验证授权
|
||||
boolean isValid = AuthValidator.validateAuth(authString, serverUniqueId);
|
||||
System.out.println("授权是否有效:" + isValid);
|
||||
|
||||
int remainingDays = AuthValidator.getRemainingDays(authString);
|
||||
System.out.println("剩余天数:" + remainingDays);
|
||||
|
||||
AuthInfo authInfo = AuthValidator.getAuthInfo(authString);
|
||||
System.out.println("授权信息:" + authInfo);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.yj.earth.auth.AuthInfo;
|
||||
import com.yj.earth.auth.AuthValidator;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.common.util.ServerUniqueIdUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Tag(name = "系统设置管理")
|
||||
@RestController
|
||||
@RequestMapping("/auth")
|
||||
public class AuthController {
|
||||
|
||||
// 授权文件存储路径、项目根目录下的license目录
|
||||
private static final String AUTH_FILE_PATH = "license/yjearth.lic";
|
||||
|
||||
@GetMapping("/info")
|
||||
@Operation(summary = "获取系统授权码")
|
||||
public ApiResponse info() {
|
||||
return ApiResponse.success(ServerUniqueIdUtil.getServerUniqueId());
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "导入授权信息")
|
||||
public ApiResponse importAuth(@Parameter(description = "授权文件", required = true) @RequestParam("file") MultipartFile file) {
|
||||
// 验证文件是否为空
|
||||
if (file.isEmpty()) {
|
||||
return ApiResponse.failure("请选择授权文件");
|
||||
}
|
||||
|
||||
// 验证文件名是否为 yjearth.lic
|
||||
String fileName = file.getOriginalFilename();
|
||||
if (fileName == null || !fileName.equals("yjearth.lic")) {
|
||||
return ApiResponse.failure("请上传 yjearth.lic");
|
||||
}
|
||||
|
||||
try {
|
||||
// 读取文件内容
|
||||
String authContent = new String(file.getBytes(), StandardCharsets.UTF_8).trim();
|
||||
// 验证授权内容有效性
|
||||
String serverHardwareMd5 = ServerUniqueIdUtil.getServerUniqueId();
|
||||
boolean isValid = AuthValidator.validateAuth(authContent, serverHardwareMd5);
|
||||
if (!isValid) {
|
||||
return ApiResponse.failure("授权文件无效或已过期");
|
||||
}
|
||||
// 创建目录(如果不存在)
|
||||
Path path = Paths.get(AUTH_FILE_PATH);
|
||||
if (!Files.exists(path.getParent())) {
|
||||
Files.createDirectories(path.getParent());
|
||||
}
|
||||
// 保存授权文件
|
||||
Files.write(path, authContent.getBytes(StandardCharsets.UTF_8));
|
||||
return ApiResponse.success(null);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.failure("导入授权文件失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/show")
|
||||
@Operation(summary = "查看授权信息")
|
||||
public ApiResponse showAuth() {
|
||||
try {
|
||||
// 检查授权文件是否存在
|
||||
Path path = Paths.get(AUTH_FILE_PATH);
|
||||
if (!Files.exists(path)) {
|
||||
return ApiResponse.failure("请先导入授权");
|
||||
}
|
||||
// 读取授权文件内容
|
||||
String authContent = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
|
||||
// 获取授权详情
|
||||
AuthInfo authInfo = AuthValidator.getAuthInfo(authContent);
|
||||
return ApiResponse.success(authInfo);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.failure("获取授权信息失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,241 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.yj.earth.business.domain.FileInfo;
|
||||
import com.yj.earth.business.service.FileInfoService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.vo.FileInfoVo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Tag(name = "文件数据管理")
|
||||
@RestController
|
||||
@RequestMapping("/fileInfo")
|
||||
public class FileInfoController {
|
||||
|
||||
@Resource
|
||||
private FileInfoService fileInfoService;
|
||||
|
||||
@Value("${file.upload.path}")
|
||||
private String uploadPath;
|
||||
|
||||
// 获取项目根目录
|
||||
private String getProjectRootPath() {
|
||||
return System.getProperty("user.dir");
|
||||
}
|
||||
|
||||
// 获取完整的上传目录路径
|
||||
private String getFullUploadPath() {
|
||||
// 拼接项目根目录和配置的上传路径
|
||||
return getProjectRootPath() + File.separator + uploadPath;
|
||||
}
|
||||
|
||||
@Operation(summary = "文件上传")
|
||||
@PostMapping("/upload")
|
||||
public ApiResponse uploadFiles(@Parameter(description = "上传的文件数组", required = true) @RequestParam("files") MultipartFile[] files) throws IOException {
|
||||
|
||||
// 校验文件数组是否为空
|
||||
if (files == null || files.length == 0) {
|
||||
return ApiResponse.failure("上传文件不能为空");
|
||||
}
|
||||
|
||||
// 获取完整的上传目录路径
|
||||
String fullUploadPath = getFullUploadPath();
|
||||
|
||||
List<FileInfoVo> fileInfoVoList = new ArrayList<>();
|
||||
|
||||
// 遍历处理每个文件
|
||||
for (MultipartFile file : files) {
|
||||
// 跳过空文件
|
||||
if (file.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取原始文件名和后缀
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String fileSuffix = FileUtil.extName(originalFilename);
|
||||
String contentType = file.getContentType();
|
||||
|
||||
// 生成唯一文件名、避免重复
|
||||
String uniqueFileName = IdUtil.simpleUUID() + "." + fileSuffix;
|
||||
|
||||
// 创建文件存储目录(如果不存在则自动创建)
|
||||
File uploadDir = new File(fullUploadPath);
|
||||
FileUtil.mkdir(uploadDir);
|
||||
|
||||
// 构建完整文件路径
|
||||
String filePath = fullUploadPath + File.separator + uniqueFileName;
|
||||
File destFile = new File(filePath);
|
||||
|
||||
// 使用Hutool工具类保存文件
|
||||
FileUtil.writeBytes(file.getBytes(), destFile);
|
||||
|
||||
// 计算文件MD5(用于校验文件完整性)
|
||||
String fileMd5 = DigestUtil.md5Hex(destFile);
|
||||
|
||||
// 查询有没有文件名一样并且 MD5 也一样的数据
|
||||
LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FileInfo::getFileName, originalFilename).eq(FileInfo::getFileMd5, fileMd5);
|
||||
if (fileInfoService.count(queryWrapper) > 0) { // 修复了此处的bug、添加了queryWrapper参数
|
||||
return ApiResponse.failure("已存在文件名相同且内容完全一致的文件");
|
||||
}
|
||||
|
||||
// 保存文件信息到数据库
|
||||
FileInfo fileInfo = new FileInfo();
|
||||
fileInfo.setFileName(originalFilename);
|
||||
fileInfo.setFileSuffix(fileSuffix);
|
||||
fileInfo.setContentType(contentType);
|
||||
fileInfo.setFileSize(file.getSize());
|
||||
fileInfo.setFilePath(uniqueFileName); // 只保存相对文件名、不保存完整路径
|
||||
fileInfo.setFileMd5(fileMd5);
|
||||
|
||||
// 保存文件信息并获取ID
|
||||
fileInfoService.save(fileInfo);
|
||||
|
||||
// 构建并设置预览URL和下载URL
|
||||
String previewUrl = "/fileInfo/preview/" + fileInfo.getId();
|
||||
String downloadUrl = "/fileInfo/download/" + fileInfo.getId();
|
||||
|
||||
FileInfoVo fileInfoVo = new FileInfoVo();
|
||||
BeanUtils.copyProperties(fileInfo, fileInfoVo);
|
||||
fileInfoVo.setPreviewUrl(previewUrl);
|
||||
fileInfoVo.setDownloadUrl(downloadUrl);
|
||||
|
||||
fileInfoVoList.add(fileInfoVo);
|
||||
}
|
||||
|
||||
if (fileInfoVoList.isEmpty()) {
|
||||
return ApiResponse.failure("未成功上传任何文件");
|
||||
}
|
||||
|
||||
return ApiResponse.success(fileInfoVoList);
|
||||
}
|
||||
|
||||
@Operation(summary = "文件下载")
|
||||
@GetMapping("/download/{id}")
|
||||
public void downloadFile(@Parameter(description = "文件ID", required = true) @PathVariable String id, HttpServletResponse response) throws IOException {
|
||||
// 根据ID查询文件信息
|
||||
FileInfo fileInfo = fileInfoService.getById(id);
|
||||
if (fileInfo == null) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建完整文件路径
|
||||
String fullPath = getFullUploadPath() + File.separator + fileInfo.getFilePath();
|
||||
File file = new File(fullPath);
|
||||
|
||||
// 校验文件是否存在
|
||||
if (!file.exists()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
response.setContentType(fileInfo.getContentType());
|
||||
response.setContentLengthLong(fileInfo.getFileSize());
|
||||
String encodedFileName = URLEncoder.encode(fileInfo.getFileName(), StandardCharsets.UTF_8.name());
|
||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
|
||||
|
||||
// 使用 Hutool 工具类复制文件流到响应输出流
|
||||
try (OutputStream os = response.getOutputStream()) {
|
||||
FileUtil.writeToStream(file, os);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "文件预览")
|
||||
@GetMapping("/preview/{id}")
|
||||
public void previewFile(
|
||||
@Parameter(description = "文件ID", required = true)
|
||||
@PathVariable String id,
|
||||
HttpServletResponse response) throws IOException {
|
||||
|
||||
// 根据ID查询文件信息
|
||||
FileInfo fileInfo = fileInfoService.getById(id);
|
||||
if (fileInfo == null) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建完整文件路径
|
||||
String fullPath = getFullUploadPath() + File.separator + fileInfo.getFilePath();
|
||||
File file = new File(fullPath);
|
||||
|
||||
// 校验文件是否存在
|
||||
if (!file.exists()) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置响应头(不设置 attachment、让浏览器直接显示)
|
||||
response.setContentType(fileInfo.getContentType());
|
||||
response.setContentLengthLong(fileInfo.getFileSize());
|
||||
|
||||
// 使用 Hutool 工具类复制文件流到响应输出流
|
||||
try (OutputStream os = response.getOutputStream()) {
|
||||
FileUtil.writeToStream(file, os);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "文件列表")
|
||||
@GetMapping("/list")
|
||||
public ApiResponse getFileList(
|
||||
@Parameter(description = "页码", required = true) Integer pageNum,
|
||||
@Parameter(description = "每页条数", required = true) Integer pageSize,
|
||||
@Parameter(description = "文件名称") String fileName) {
|
||||
|
||||
// 创建分页对象
|
||||
Page<FileInfo> page = new Page<>(pageNum, pageSize);
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
if (fileName != null && !fileName.isEmpty()) {
|
||||
queryWrapper.like(FileInfo::getFileName, fileName);
|
||||
}
|
||||
// 按创建时间倒序排列、最新上传的文件在前
|
||||
queryWrapper.orderByDesc(FileInfo::getCreatedAt);
|
||||
|
||||
// 执行分页查询
|
||||
IPage<FileInfo> fileInfoPage = fileInfoService.page(page, queryWrapper);
|
||||
|
||||
// 转换为VO对象并设置URL
|
||||
List<FileInfoVo> records = fileInfoPage.getRecords().stream().map(fileInfo -> {
|
||||
FileInfoVo vo = new FileInfoVo();
|
||||
BeanUtils.copyProperties(fileInfo, vo);
|
||||
vo.setPreviewUrl("/fileInfo/preview/" + fileInfo.getId());
|
||||
vo.setDownloadUrl("/fileInfo/download/" + fileInfo.getId());
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// 构建分页结果
|
||||
Page<FileInfoVo> resultPage = new Page<>();
|
||||
resultPage.setRecords(records);
|
||||
resultPage.setTotal(fileInfoPage.getTotal());
|
||||
resultPage.setSize(fileInfoPage.getSize());
|
||||
resultPage.setCurrent(fileInfoPage.getCurrent());
|
||||
resultPage.setPages(fileInfoPage.getPages());
|
||||
return ApiResponse.success(resultPage);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,237 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.graphhopper.GHRequest;
|
||||
import com.graphhopper.GHResponse;
|
||||
import com.graphhopper.GraphHopper;
|
||||
import com.graphhopper.ResponsePath;
|
||||
import com.graphhopper.config.Profile;
|
||||
import com.graphhopper.util.shapes.GHPoint;
|
||||
import com.yj.earth.business.domain.FileInfo;
|
||||
import com.yj.earth.business.service.FileInfoService;
|
||||
import com.yj.earth.common.config.GraphHopperProperties;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.model.Point;
|
||||
import com.yj.earth.model.RouteRequest;
|
||||
import com.yj.earth.model.RouteResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
@Tag(name = "路径规划管理")
|
||||
@RestController
|
||||
@RequestMapping("/graphhopper")
|
||||
public class GraphHopperController {
|
||||
@Resource
|
||||
private FileInfoService fileInfoService;
|
||||
@Resource
|
||||
private GraphHopperProperties graphHopperProperties;
|
||||
|
||||
// 存储当前可用的 GraphHopper 实例
|
||||
private volatile GraphHopper currentHopper;
|
||||
|
||||
// 状态控制: 线程安全
|
||||
private final AtomicBoolean isLoading = new AtomicBoolean(false);
|
||||
private final AtomicBoolean isLoaded = new AtomicBoolean(false);
|
||||
|
||||
@Operation(summary = "获取地图列表")
|
||||
@GetMapping("/list")
|
||||
public ApiResponse list() {
|
||||
LambdaQueryWrapper<FileInfo> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(FileInfo::getFileSuffix, "pbf");
|
||||
return ApiResponse.success(fileInfoService.list(queryWrapper));
|
||||
}
|
||||
|
||||
@Operation(summary = "加载地图数据")
|
||||
@PostMapping("/loadMap")
|
||||
public ApiResponse loadMap(@Parameter(description = "文件ID") @RequestParam String fileId) {
|
||||
// 参数校验
|
||||
if (fileId == null) {
|
||||
return ApiResponse.failure("文件ID不能为空");
|
||||
}
|
||||
|
||||
// 获取并校验OSM文件
|
||||
String osmFilePath = fileInfoService.getFileAbsolutePath(fileId);
|
||||
File osmFile = new File(osmFilePath);
|
||||
if (!osmFile.exists()) {
|
||||
return ApiResponse.failure("地图文件不存在: " + osmFilePath);
|
||||
}
|
||||
if (!osmFile.isFile() || !osmFile.getName().endsWith(".pbf")) {
|
||||
return ApiResponse.failure("仅支持有效的.pbf格式OSM文件");
|
||||
}
|
||||
// 防止并发加载
|
||||
if (isLoading.get()) {
|
||||
return ApiResponse.failure("地图正在加载中、请稍后查询状态");
|
||||
}
|
||||
|
||||
// 标记加载状态
|
||||
isLoading.set(true);
|
||||
isLoaded.set(false);
|
||||
|
||||
// 异步执行: 删除旧数据 → 创建新实例 → 加载新地图
|
||||
new Thread(() -> {
|
||||
GraphHopper newHopper = null;
|
||||
try {
|
||||
// 关键步骤1: 彻底删除旧地图数据目录
|
||||
deleteOldGraphDir();
|
||||
// 关键步骤2: 创建全新的GraphHopper实例
|
||||
newHopper = createNewGraphHopperInstance(osmFilePath);
|
||||
// 关键步骤3: 加载新地图
|
||||
newHopper.importOrLoad();
|
||||
// 关键步骤4: 加载成功 → 替换当前实例 + 更新状态
|
||||
currentHopper = newHopper;
|
||||
isLoaded.set(true);
|
||||
log.info("地图加载成功");
|
||||
} catch (Exception e) {
|
||||
// 加载失败 → 清理新实例资源
|
||||
if (newHopper != null) {
|
||||
newHopper.close();
|
||||
}
|
||||
isLoaded.set(false);
|
||||
e.printStackTrace();
|
||||
log.error("地图加载失败: " + e.getMessage());
|
||||
|
||||
} finally {
|
||||
// 无论成功/失败、释放加载锁
|
||||
isLoading.set(false);
|
||||
}
|
||||
}).start();
|
||||
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "路径规划")
|
||||
@PostMapping("/route")
|
||||
public ApiResponse calculateRoute(@RequestBody RouteRequest request) {
|
||||
// 校验地图是否加载完成 + 实例是否可用
|
||||
if (!isLoaded.get() || currentHopper == null) {
|
||||
return ApiResponse.failure("地图未加载完成");
|
||||
}
|
||||
try {
|
||||
// 构建路径点列表
|
||||
List<GHPoint> ghPoints = new ArrayList<>();
|
||||
ghPoints.add(new GHPoint(request.getStartLat(), request.getStartLng())); // 起点
|
||||
// 添加途经点
|
||||
if (request.getWaypoints() != null && !request.getWaypoints().isEmpty()) {
|
||||
for (Point waypoint : request.getWaypoints()) {
|
||||
ghPoints.add(new GHPoint(waypoint.getLat(), waypoint.getLng()));
|
||||
}
|
||||
}
|
||||
ghPoints.add(new GHPoint(request.getEndLat(), request.getEndLng())); // 终点
|
||||
|
||||
// 构建请求(仅指定Profile、无setWeighting)
|
||||
String targetProfile = request.getProfile() != null ? request.getProfile() : "car";
|
||||
GHRequest ghRequest = new GHRequest(ghPoints)
|
||||
.setProfile(targetProfile);
|
||||
|
||||
// 用新实例计算路径
|
||||
GHResponse response = currentHopper.route(ghRequest);
|
||||
|
||||
// 处理错误
|
||||
if (response.hasErrors()) {
|
||||
return ApiResponse.failure("路径计算失败: " + response.getErrors().toString());
|
||||
}
|
||||
|
||||
// 解析结果
|
||||
ResponsePath bestPath = response.getBest();
|
||||
List<Point> pathPoints = new ArrayList<>();
|
||||
bestPath.getPoints().forEach(ghPoint ->
|
||||
pathPoints.add(new Point(ghPoint.getLat(), ghPoint.getLon()))
|
||||
);
|
||||
|
||||
// 封装返回
|
||||
RouteResponse routeResponse = new RouteResponse(bestPath.getDistance() / 1000, (double) (bestPath.getTime() / 60000), pathPoints);
|
||||
return ApiResponse.success(routeResponse);
|
||||
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.failure("路径计算异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "获取交通方式")
|
||||
@PostMapping("/profiles")
|
||||
public ApiResponse profiles() {
|
||||
return ApiResponse.success(graphHopperProperties.getProfiles());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建全新的 GraphHopper 实例
|
||||
*/
|
||||
private GraphHopper createNewGraphHopperInstance(String osmFilePath) {
|
||||
GraphHopper hopper = new GraphHopper();
|
||||
// 配置基础参数
|
||||
hopper.setOSMFile(osmFilePath);
|
||||
hopper.setGraphHopperLocation(graphHopperProperties.getGraphLocation());
|
||||
|
||||
// 配置交通方式 + 权重策略
|
||||
List<Profile> profileList = new ArrayList<>();
|
||||
for (String profileName : graphHopperProperties.getProfiles()) {
|
||||
Profile profile = new Profile(profileName);
|
||||
profile.setVehicle(profileName);
|
||||
profile.setWeighting("fastest");
|
||||
profileList.add(profile);
|
||||
}
|
||||
hopper.setProfiles(profileList);
|
||||
|
||||
return hopper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归删除旧地图数据目录
|
||||
*/
|
||||
private void deleteOldGraphDir() {
|
||||
File graphDir = new File(graphHopperProperties.getGraphLocation());
|
||||
if (!graphDir.exists()) {
|
||||
log.info("旧地图目录不存在、无需删除: " + graphHopperProperties.getGraphLocation());
|
||||
return;
|
||||
}
|
||||
|
||||
// 递归删除所有文件和子目录
|
||||
File[] files = graphDir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
deleteOldGraphDir(file);
|
||||
} else {
|
||||
boolean deleted = file.delete();
|
||||
log.info("删除旧地图文件: " + file.getAbsolutePath() + " → " + (deleted ? "成功" : "失败"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除空目录
|
||||
boolean dirDeleted = graphDir.delete();
|
||||
System.out.println("删除旧地图目录: " + graphDir.getAbsolutePath() + " → " + (dirDeleted ? "成功" : "失败"));
|
||||
}
|
||||
|
||||
// 重载: 递归删除子目录
|
||||
private void deleteOldGraphDir(File subDir) {
|
||||
File[] files = subDir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
deleteOldGraphDir(file);
|
||||
} else {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
subDir.delete();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.yj.earth.business.domain.Role;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.dto.role.AddRoleDto;
|
||||
import com.yj.earth.dto.role.UpdateRoleDto;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Tag(name = "角色数据管理")
|
||||
@RestController
|
||||
@RequestMapping("/role")
|
||||
public class RoleController {
|
||||
@Resource
|
||||
private RoleService roleService;
|
||||
|
||||
@Operation(summary = "新增角色")
|
||||
@PostMapping("/add")
|
||||
public ApiResponse save(@RequestBody AddRoleDto addRoleDto) {
|
||||
Role role = new Role();
|
||||
BeanUtils.copyProperties(addRoleDto, role);
|
||||
roleService.save(role);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除角色")
|
||||
@PostMapping("/delete")
|
||||
public ApiResponse delete(@Parameter(description = "角色ID") String id) {
|
||||
roleService.removeById(id);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新角色")
|
||||
@PostMapping("/update")
|
||||
public ApiResponse update(@RequestBody UpdateRoleDto updateRoleDto) {
|
||||
Role role = new Role();
|
||||
BeanUtils.copyProperties(updateRoleDto, role);
|
||||
roleService.updateById(role);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "角色详情")
|
||||
@GetMapping("/getById")
|
||||
public ApiResponse get(@Parameter(description = "角色ID") String id) {
|
||||
Role role = roleService.getById(id);
|
||||
return ApiResponse.success(role);
|
||||
}
|
||||
|
||||
@Operation(summary = "角色列表")
|
||||
@GetMapping("/list")
|
||||
public ApiResponse list(@Parameter(description = "分页数量") Integer pageNum, @Parameter(description = "分页大小") Integer pageSize) {
|
||||
Page<Role> rolePage = roleService.page(new Page<>(pageNum, pageSize));
|
||||
return ApiResponse.success(rolePage);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.yj.earth.business.domain.RoleSource;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.yj.earth.business.service.RoleSourceService;
|
||||
import com.yj.earth.business.service.SourceService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.dto.relation.RoleBindOrUnBindSourceDto;
|
||||
import com.yj.earth.dto.relation.SourceBindOrUnBindRoleDto;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Tag(name = "角色资源管理")
|
||||
@RestController
|
||||
@RequestMapping("/roleSource")
|
||||
public class RoleSourceController {
|
||||
@Resource
|
||||
private RoleSourceService roleSourceService;
|
||||
|
||||
@Operation(summary = "角色绑定资源")
|
||||
@PostMapping("/roleBindSource")
|
||||
public ApiResponse roleBindSource(@RequestBody RoleBindOrUnBindSourceDto roleBindOrUnBindSourceDto) {
|
||||
// 先删除该角色下的所有资源
|
||||
roleSourceService.remove(new LambdaQueryWrapper<RoleSource>().eq(RoleSource::getRoleId, roleBindOrUnBindSourceDto.getRoleId()));
|
||||
// 再设置新的资源
|
||||
for (String sourceId : roleBindOrUnBindSourceDto.getSourceIdList()) {
|
||||
RoleSource roleSource = new RoleSource();
|
||||
roleSource.setRoleId(roleBindOrUnBindSourceDto.getRoleId());
|
||||
roleSource.setSourceId(sourceId);
|
||||
roleSourceService.save(roleSource);
|
||||
}
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,168 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yj.earth.business.domain.Source;
|
||||
import com.yj.earth.business.service.RoleSourceService;
|
||||
import com.yj.earth.business.service.SourceService;
|
||||
import com.yj.earth.business.service.UserService;
|
||||
import com.yj.earth.common.service.SourceParamsValidator;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.common.util.MapUtil;
|
||||
import com.yj.earth.dto.source.AddDirectoryDto;
|
||||
import com.yj.earth.dto.source.AddModelSourceDto;
|
||||
import com.yj.earth.dto.source.AddOtherSourceDto;
|
||||
import com.yj.earth.dto.source.UpdateSourceDto;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.yj.earth.common.constant.GlobalConstant.DIRECTORY;
|
||||
import static com.yj.earth.common.constant.GlobalConstant.SHOW;
|
||||
|
||||
@Slf4j
|
||||
@Tag(name = "树形结构管理")
|
||||
@RestController
|
||||
@RequestMapping("/source")
|
||||
public class SourceController {
|
||||
@Resource
|
||||
private SourceService sourceService;
|
||||
@Resource
|
||||
private UserService userService;
|
||||
@Resource
|
||||
private RoleSourceService roleSourceService;
|
||||
@Resource
|
||||
private SourceParamsValidator sourceParamsValidator;
|
||||
|
||||
@PostMapping("/addDirectory")
|
||||
@Operation(summary = "新增目录资源")
|
||||
public ApiResponse addDirectory(@RequestBody AddDirectoryDto addDirectoryDto) {
|
||||
// 校验是否通过
|
||||
String message = sourceService.checkIsPass(addDirectoryDto.getParentId(), addDirectoryDto.getSourceName());
|
||||
if (message != null) {
|
||||
return ApiResponse.failure(message);
|
||||
}
|
||||
// 通过之后保存资源
|
||||
Source source = new Source();
|
||||
BeanUtils.copyProperties(addDirectoryDto, source);
|
||||
source.setSourceType(DIRECTORY);
|
||||
source.setIsShow(SHOW);
|
||||
sourceService.save(source);
|
||||
// 添加资源到该用户的角色下
|
||||
roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId());
|
||||
return ApiResponse.success(source);
|
||||
}
|
||||
|
||||
@Operation(summary = "新增模型资源")
|
||||
@PostMapping("/addModelSource")
|
||||
public ApiResponse addModelSource(@RequestBody AddModelSourceDto addModelSourceDto) {
|
||||
// 获取资源绝对路径
|
||||
String sourcePath = addModelSourceDto.getSourcePath();
|
||||
// 获取资源名称
|
||||
String sourceName = FileUtil.mainName(sourcePath);
|
||||
// 校验是否通过
|
||||
String message = sourceService.checkIsPass(addModelSourceDto.getParentId(), sourceName);
|
||||
if (message != null) {
|
||||
return ApiResponse.failure(message);
|
||||
}
|
||||
// 调用SDK加载资源
|
||||
String sourceId = sourceService.addAndGetSourceId(sourcePath);
|
||||
// 获取文件路径并处理详情
|
||||
String detail = sourceService.getDetail(sourcePath, sourceId);
|
||||
// 构建并保存资源对象
|
||||
Source source = new Source();
|
||||
source.setSourcePath(sourcePath);
|
||||
source.setSourceName(sourceName);
|
||||
source.setParentId(addModelSourceDto.getParentId());
|
||||
source.setTreeIndex(addModelSourceDto.getTreeIndex());
|
||||
source.setDetail(detail);
|
||||
source.setSourceType(MapUtil.getString(MapUtil.jsonToMap(detail), "fileType"));
|
||||
source.setIsShow(SHOW);
|
||||
source.setParams(addModelSourceDto.getParams());
|
||||
sourceService.save(source);
|
||||
// 添加资源到该用户的角色下
|
||||
roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId());
|
||||
return ApiResponse.success(source);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "新增其他资源")
|
||||
@PostMapping("/addOtherSource")
|
||||
public ApiResponse addOtherSource(@RequestBody AddOtherSourceDto addOtherSourceDto) throws JsonProcessingException {
|
||||
// 校验是否通过
|
||||
String message = sourceService.checkIsPass(addOtherSourceDto.getParentId(), addOtherSourceDto.getSourceName());
|
||||
if (message != null) {
|
||||
return ApiResponse.failure(message);
|
||||
}
|
||||
// 验证并转换参数
|
||||
Object validatedParams = sourceParamsValidator.validateAndConvert(
|
||||
addOtherSourceDto.getSourceType(),
|
||||
addOtherSourceDto.getParams()
|
||||
);
|
||||
System.out.println(validatedParams);
|
||||
Source source = new Source();
|
||||
BeanUtils.copyProperties(addOtherSourceDto, source);
|
||||
source.setIsShow(SHOW);
|
||||
source.setParams(MapUtil.objectToJson(validatedParams));
|
||||
sourceService.save(source);
|
||||
// 添加资源到该用户的角色下
|
||||
roleSourceService.addRoleSource(userService.getById(StpUtil.getLoginIdAsString()).getRoleId(), source.getId());
|
||||
return ApiResponse.success(source);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "更新资源信息及参数")
|
||||
@PostMapping("/update")
|
||||
public ApiResponse updateSource(@RequestBody UpdateSourceDto updateSourceDto) {
|
||||
// 查询资源
|
||||
Source source = sourceService.getById(updateSourceDto.getId());
|
||||
if (source == null) {
|
||||
return ApiResponse.failure("资源不存在");
|
||||
}
|
||||
|
||||
// 更新基本信息
|
||||
BeanUtils.copyProperties(updateSourceDto, source);
|
||||
|
||||
// 处理参数更新
|
||||
if (updateSourceDto.getParams() != null && !updateSourceDto.getParams().isEmpty()) {
|
||||
// 获取类型
|
||||
String sourceType = source.getSourceType();
|
||||
// 验证参数
|
||||
Object validatedParams = sourceParamsValidator.validateAndConvert(
|
||||
sourceType,
|
||||
updateSourceDto.getParams()
|
||||
);
|
||||
|
||||
// 获取原始数据的 Map 并合并新参数
|
||||
Map<String, Object> dataMap = MapUtil.jsonToMap(source.getParams());
|
||||
MapUtil.mergeMaps(dataMap, updateSourceDto.getParams());
|
||||
source.setParams(MapUtil.mapToString(dataMap));
|
||||
}
|
||||
|
||||
// 保存更新
|
||||
sourceService.updateById(source);
|
||||
return ApiResponse.success(source);
|
||||
}
|
||||
|
||||
@GetMapping("/type")
|
||||
@Operation(summary = "获取支持的资源类型")
|
||||
public ApiResponse getSupportedSourceTypes() {
|
||||
Set<String> supportedTypes = sourceParamsValidator.getSupportedSourceTypes();
|
||||
return ApiResponse.success(supportedTypes);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "获取资源列表")
|
||||
@GetMapping("/list")
|
||||
public ApiResponse list() {
|
||||
return ApiResponse.success(sourceService.getSourceListByUserId(StpUtil.getLoginIdAsString()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.yj.earth.annotation.EncryptResponse;
|
||||
import com.yj.earth.annotation.ExcludeField;
|
||||
import com.yj.earth.annotation.RoleAccess;
|
||||
import com.yj.earth.business.domain.Role;
|
||||
import com.yj.earth.business.domain.User;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.yj.earth.dto.relation.UserBindOrUnBindRoleDto;
|
||||
import com.yj.earth.dto.user.AddUserDto;
|
||||
import com.yj.earth.dto.user.UpdatePasswordDto;
|
||||
import com.yj.earth.dto.user.UpdateUserDto;
|
||||
import com.yj.earth.dto.user.UserLoginDto;
|
||||
import com.yj.earth.business.service.UserService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "用户数据管理")
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
public class UserController {
|
||||
@Resource
|
||||
private UserService userService;
|
||||
@Resource
|
||||
private RoleService roleService;
|
||||
|
||||
@Operation(summary = "新增用户")
|
||||
@PostMapping("/add")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse save(@RequestBody AddUserDto addUserDto) {
|
||||
User user = new User();
|
||||
BeanUtils.copyProperties(addUserDto, user);
|
||||
if (userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, user.getUsername())) != null) {
|
||||
return ApiResponse.failure("用户已存在");
|
||||
}
|
||||
String password = user.getPassword();
|
||||
user.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
|
||||
if (addUserDto.getRoleId() == null) {
|
||||
// 查询系统名字为默认角色的角色ID
|
||||
user.setRoleId(roleService.getOne(new LambdaQueryWrapper<Role>().eq(Role::getRoleName, "默认角色")).getId());
|
||||
}
|
||||
userService.save(user);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除用户")
|
||||
@PostMapping("/delete")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse delete(@Parameter(description = "用户ID") String id) {
|
||||
userService.removeById(id);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新信息")
|
||||
@PostMapping("/update")
|
||||
public ApiResponse update(@RequestBody UpdateUserDto updateUserDto) {
|
||||
User user = new User();
|
||||
BeanUtils.copyProperties(updateUserDto, user);
|
||||
userService.updateById(user);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新密码")
|
||||
@PostMapping("/updatePassword")
|
||||
public ApiResponse updatePassword(@RequestBody UpdatePasswordDto updatePasswordDto) {
|
||||
User user = userService.getById(updatePasswordDto.getId());
|
||||
if (user == null) {
|
||||
return ApiResponse.failure("用户不存在");
|
||||
}
|
||||
if (!BCrypt.checkpw(updatePasswordDto.getOldPassword(), user.getPassword())) {
|
||||
return ApiResponse.failure("旧密码错误");
|
||||
}
|
||||
user.setPassword(BCrypt.hashpw(updatePasswordDto.getNewPassword(), BCrypt.gensalt()));
|
||||
userService.updateById(user);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "用户详情")
|
||||
@GetMapping("/getById")
|
||||
public ApiResponse get(@Parameter(description = "用户ID") String id) {
|
||||
return ApiResponse.success(userService.getById(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "用户列表")
|
||||
@GetMapping("/list")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse list(@Parameter(description = "分页数量") Integer pageNum, @Parameter(description = "分页大小") Integer pageSize) {
|
||||
Page<User> userPage = userService.page(new Page<>(pageNum, pageSize));
|
||||
return ApiResponse.success(userPage);
|
||||
}
|
||||
|
||||
@Operation(summary = "用户登录")
|
||||
@PostMapping("/login")
|
||||
public ApiResponse login(@RequestBody UserLoginDto userLoginDto) {
|
||||
User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, userLoginDto.getUsername()));
|
||||
if (user == null) {
|
||||
return ApiResponse.failure("用户名不存在");
|
||||
}
|
||||
if (!BCrypt.checkpw(userLoginDto.getPassword(), user.getPassword())) {
|
||||
return ApiResponse.failure("密码错误");
|
||||
}
|
||||
StpUtil.login(user.getId());
|
||||
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
|
||||
return ApiResponse.success(Map.of("header", tokenInfo.getTokenName(), "token", tokenInfo.getTokenValue()));
|
||||
}
|
||||
|
||||
@Operation(summary = "用户登出")
|
||||
@PostMapping("/logout")
|
||||
public ApiResponse logout() {
|
||||
StpUtil.logout();
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "设置角色")
|
||||
@PostMapping("/userBindOrUnBindRole")
|
||||
@RoleAccess(roleNames = "管理员")
|
||||
public ApiResponse userBindOrUnBindRole(@RequestBody UserBindOrUnBindRoleDto userBindOrUnBindRoleDto) {
|
||||
userService.lambdaUpdate().set(User::getRoleId, userBindOrUnBindRoleDto.getRoleId()).eq(User::getId, userBindOrUnBindRoleDto.getUserId()).update();
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取当前用户信息")
|
||||
@GetMapping("/getCurrentUserInfo")
|
||||
public ApiResponse getCurrentUserInfo() {
|
||||
return ApiResponse.success(userService.getById(StpUtil.getLoginIdAsString()));
|
||||
}
|
||||
}
|
||||
50
src/main/java/com/yj/earth/business/domain/FileInfo.java
Normal file
50
src/main/java/com/yj/earth/business/domain/FileInfo.java
Normal file
@ -0,0 +1,50 @@
|
||||
package com.yj.earth.business.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
public class FileInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "主键")
|
||||
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
@Schema(description = "文件名")
|
||||
private String fileName;
|
||||
|
||||
@Schema(description = "文件后缀")
|
||||
private String fileSuffix;
|
||||
|
||||
@Schema(description = "内容类型")
|
||||
private String contentType;
|
||||
|
||||
@Schema(description = "文件大小")
|
||||
private Long fileSize;
|
||||
|
||||
@Schema(description = "文件路径")
|
||||
private String filePath;
|
||||
|
||||
@Schema(description = "文件MD5")
|
||||
private String fileMd5;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
41
src/main/java/com/yj/earth/business/domain/Role.java
Normal file
41
src/main/java/com/yj/earth/business/domain/Role.java
Normal file
@ -0,0 +1,41 @@
|
||||
package com.yj.earth.business.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
public class Role implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "主键")
|
||||
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
@Schema(description = "角色名称")
|
||||
private String roleName;
|
||||
|
||||
@Schema(description = "角色描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "是否超级管理员")
|
||||
private Integer isSuper;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
39
src/main/java/com/yj/earth/business/domain/RoleSource.java
Normal file
39
src/main/java/com/yj/earth/business/domain/RoleSource.java
Normal file
@ -0,0 +1,39 @@
|
||||
package com.yj.earth.business.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
public class RoleSource implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "主键")
|
||||
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
@Schema(description = "角色ID")
|
||||
private String roleId;
|
||||
|
||||
@Schema(description = "资源ID")
|
||||
private String sourceId;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
56
src/main/java/com/yj/earth/business/domain/Source.java
Normal file
56
src/main/java/com/yj/earth/business/domain/Source.java
Normal file
@ -0,0 +1,56 @@
|
||||
package com.yj.earth.business.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
public class Source implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "主键")
|
||||
@TableId(value = "id", type = IdType.INPUT)
|
||||
private String id;
|
||||
|
||||
@Schema(description = "资源名称")
|
||||
private String sourceName;
|
||||
|
||||
@Schema(description = "资源类型")
|
||||
private String sourceType;
|
||||
|
||||
@Schema(description = "资源路径")
|
||||
private String sourcePath;
|
||||
|
||||
@Schema(description = "父级ID")
|
||||
private String parentId;
|
||||
|
||||
@Schema(description = "树形索引")
|
||||
private Integer treeIndex;
|
||||
|
||||
@Schema(description = "是否显示")
|
||||
private Integer isShow;
|
||||
|
||||
@Schema (description = "其他内容")
|
||||
private String detail;
|
||||
|
||||
@Schema (description = "前端参数")
|
||||
private String params;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
51
src/main/java/com/yj/earth/business/domain/User.java
Normal file
51
src/main/java/com/yj/earth/business/domain/User.java
Normal file
@ -0,0 +1,51 @@
|
||||
package com.yj.earth.business.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.yj.earth.annotation.ExcludeField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
public class User implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码")
|
||||
@ExcludeField
|
||||
private String password;
|
||||
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "昵称")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "所属角色")
|
||||
private String roleId;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.yj.earth.business.mapper;
|
||||
|
||||
import com.yj.earth.business.domain.FileInfo;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-08-29
|
||||
*/
|
||||
@Mapper
|
||||
public interface FileInfoMapper extends BaseMapper<FileInfo> {
|
||||
|
||||
}
|
||||
18
src/main/java/com/yj/earth/business/mapper/RoleMapper.java
Normal file
18
src/main/java/com/yj/earth/business/mapper/RoleMapper.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.yj.earth.business.mapper;
|
||||
|
||||
import com.yj.earth.business.domain.Role;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-08-28
|
||||
*/
|
||||
@Mapper
|
||||
public interface RoleMapper extends BaseMapper<Role> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.yj.earth.business.mapper;
|
||||
|
||||
import com.yj.earth.business.domain.RoleSource;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-08-27
|
||||
*/
|
||||
@Mapper
|
||||
public interface RoleSourceMapper extends BaseMapper<RoleSource> {
|
||||
|
||||
}
|
||||
18
src/main/java/com/yj/earth/business/mapper/SourceMapper.java
Normal file
18
src/main/java/com/yj/earth/business/mapper/SourceMapper.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.yj.earth.business.mapper;
|
||||
|
||||
import com.yj.earth.business.domain.Source;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-08-26
|
||||
*/
|
||||
@Mapper
|
||||
public interface SourceMapper extends BaseMapper<Source> {
|
||||
|
||||
}
|
||||
18
src/main/java/com/yj/earth/business/mapper/UserMapper.java
Normal file
18
src/main/java/com/yj/earth/business/mapper/UserMapper.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.yj.earth.business.mapper;
|
||||
|
||||
import com.yj.earth.business.domain.User;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-08-28
|
||||
*/
|
||||
@Mapper
|
||||
public interface UserMapper extends BaseMapper<User> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.yj.earth.business.service;
|
||||
|
||||
import com.yj.earth.business.domain.FileInfo;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
public interface FileInfoService extends IService<FileInfo> {
|
||||
// 根据文件ID获取文件绝对路径
|
||||
String getFileAbsolutePath(String id);
|
||||
}
|
||||
16
src/main/java/com/yj/earth/business/service/RoleService.java
Normal file
16
src/main/java/com/yj/earth/business/service/RoleService.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.yj.earth.business.service;
|
||||
|
||||
import com.yj.earth.business.domain.Role;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-08-28
|
||||
*/
|
||||
public interface RoleService extends IService<Role> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.yj.earth.business.service;
|
||||
|
||||
import com.yj.earth.business.domain.RoleSource;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
|
||||
public interface RoleSourceService extends IService<RoleSource> {
|
||||
void addRoleSource(String roleId, String sourceId);
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.yj.earth.business.service;
|
||||
|
||||
import com.yj.earth.business.domain.Source;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface SourceService extends IService<Source> {
|
||||
String addAndGetSourceId(String sourcePath);
|
||||
|
||||
String getDetail(String sourcePath, String sourceId);
|
||||
|
||||
String buildSdkUrl(String path);
|
||||
|
||||
String fetchCltDetail(String sourceId);
|
||||
|
||||
String fetchMbtilesDetail(String sourceId);
|
||||
|
||||
String fetchPakDetail(String sourceId);
|
||||
|
||||
List<Source> getSourceListByUserId(String userId);
|
||||
|
||||
String checkIsPass(String parentId, String sourceName);
|
||||
}
|
||||
16
src/main/java/com/yj/earth/business/service/UserService.java
Normal file
16
src/main/java/com/yj/earth/business/service/UserService.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.yj.earth.business.service;
|
||||
|
||||
import com.yj.earth.business.domain.User;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-08-28
|
||||
*/
|
||||
public interface UserService extends IService<User> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package com.yj.earth.business.service.impl;
|
||||
|
||||
import com.yj.earth.business.domain.FileInfo;
|
||||
import com.yj.earth.business.mapper.FileInfoMapper;
|
||||
import com.yj.earth.business.service.FileInfoService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@Service
|
||||
public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements FileInfoService {
|
||||
|
||||
@Value("${file.upload.path}")
|
||||
private String uploadPath;
|
||||
|
||||
public String getFileAbsolutePath(String id) {
|
||||
|
||||
// 根据ID查询文件信息
|
||||
FileInfo fileInfo = this.getById(id);
|
||||
if (fileInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 构建完整文件路径
|
||||
String fullPath = uploadPath + File.separator + fileInfo.getFilePath();
|
||||
File file = new File(fullPath);
|
||||
|
||||
// 校验文件是否存在
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取并返回绝对路径
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.yj.earth.business.service.impl;
|
||||
|
||||
import com.yj.earth.business.domain.Role;
|
||||
import com.yj.earth.business.mapper.RoleMapper;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-08-28
|
||||
*/
|
||||
@Service
|
||||
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.yj.earth.business.service.impl;
|
||||
|
||||
import com.yj.earth.business.domain.RoleSource;
|
||||
import com.yj.earth.business.mapper.RoleSourceMapper;
|
||||
import com.yj.earth.business.service.RoleSourceService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
@Service
|
||||
public class RoleSourceServiceImpl extends ServiceImpl<RoleSourceMapper, RoleSource> implements RoleSourceService {
|
||||
|
||||
@Override
|
||||
public void addRoleSource(String roleId, String sourceId) {
|
||||
RoleSource roleSource = new RoleSource();
|
||||
roleSource.setRoleId(roleId);
|
||||
roleSource.setSourceId(sourceId);
|
||||
save(roleSource);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,166 @@
|
||||
package com.yj.earth.business.service.impl;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.yj.earth.business.domain.Role;
|
||||
import com.yj.earth.business.domain.RoleSource;
|
||||
import com.yj.earth.business.domain.Source;
|
||||
import com.yj.earth.business.domain.User;
|
||||
import com.yj.earth.business.mapper.SourceMapper;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.yj.earth.business.service.RoleSourceService;
|
||||
import com.yj.earth.business.service.SourceService;
|
||||
import com.yj.earth.business.service.UserService;
|
||||
import com.yj.earth.common.config.ServerConfig;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.common.util.HttpUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SourceServiceImpl extends ServiceImpl<SourceMapper, Source> implements SourceService {
|
||||
|
||||
@Resource
|
||||
private ServerConfig serverConfig;
|
||||
@Resource
|
||||
private RoleSourceService roleSourceService;
|
||||
@Resource
|
||||
private UserService userService;
|
||||
@Resource
|
||||
private SourceService sourceService;
|
||||
@Resource
|
||||
private RoleService roleService;
|
||||
|
||||
// 存储文件后缀与对应处理函数的映射关系
|
||||
public final Map<String, Function<String, String>> detailFetchers;
|
||||
|
||||
// 初始化映射关系
|
||||
public SourceServiceImpl() {
|
||||
detailFetchers = new HashMap<>();
|
||||
detailFetchers.put("clt", this::fetchCltDetail);
|
||||
detailFetchers.put("mbtiles", this::fetchMbtilesDetail);
|
||||
detailFetchers.put("pak", this::fetchPakDetail);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用SDK获取资源ID
|
||||
*/
|
||||
@Override
|
||||
public String addAndGetSourceId(String sourcePath) {
|
||||
Map<String, Object> addParams = new HashMap<>();
|
||||
addParams.put("filePath", sourcePath);
|
||||
String url = buildSdkUrl("/sourceMap/add");
|
||||
return HttpUtil.doPostForm(url, addParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测资源是否通过审核
|
||||
*/
|
||||
@Override
|
||||
public String checkIsPass(String parentId, String sourceName) {
|
||||
// 先查询父节点是否存在
|
||||
if (parentId != null) {
|
||||
LambdaQueryWrapper<Source> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(Source::getId, parentId);
|
||||
List<Source> list = sourceService.list(queryWrapper);
|
||||
if (sourceService.count(queryWrapper) == 0) {
|
||||
return "父级不存在";
|
||||
}
|
||||
}
|
||||
// // 验证该目录下是否已经存在此资源名一样的
|
||||
// LambdaQueryWrapper<Source> queryWrapper = new LambdaQueryWrapper<>();
|
||||
// queryWrapper.eq(Source::getSourceName, sourceName);
|
||||
// if (sourceService.count(queryWrapper) > 0) {
|
||||
// return "此目录下已存在此资源";
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件后缀获取详情信息
|
||||
*/
|
||||
@Override
|
||||
public String getDetail(String sourcePath, String sourceId) {
|
||||
String ext = FileUtil.extName(sourcePath);
|
||||
// 通过映射关系获取并执行对应的处理函数
|
||||
Function<String, String> fetcher = detailFetchers.get(ext);
|
||||
if (fetcher != null) {
|
||||
String detailResult = fetcher.apply(sourceId);
|
||||
return detailResult;
|
||||
} else {
|
||||
log.info("未找到{}类型的处理方式", ext);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建SDK请求URL
|
||||
*/
|
||||
@Override
|
||||
public String buildSdkUrl(String path) {
|
||||
return "http://" + serverConfig.getHost() + ":" + serverConfig.getSdkPort() + path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 CLT 类型资源详情
|
||||
*/
|
||||
@Override
|
||||
public String fetchCltDetail(String sourceId) {
|
||||
String url = buildSdkUrl("/data/clt/detail/" + sourceId);
|
||||
return HttpUtil.doGet(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 MBTiles 类型资源详情
|
||||
*/
|
||||
@Override
|
||||
public String fetchMbtilesDetail(String sourceId) {
|
||||
String url = buildSdkUrl("/data/mbtiles/detail/" + sourceId);
|
||||
return HttpUtil.doGet(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 PAK 类型资源详情
|
||||
*/
|
||||
@Override
|
||||
public String fetchPakDetail(String sourceId) {
|
||||
String url = buildSdkUrl("/data/pak/detail/" + sourceId);
|
||||
return HttpUtil.doGet(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取用户资源列表
|
||||
*/
|
||||
@Override
|
||||
public List<Source> getSourceListByUserId(String userId) {
|
||||
// 查询该用户信息
|
||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(User::getId, userId);
|
||||
User user = userService.getOne(queryWrapper);
|
||||
// 查询角色信息
|
||||
String roleId = user.getRoleId();
|
||||
LambdaQueryWrapper<Role> roleQueryWrapper = new LambdaQueryWrapper<>();
|
||||
roleQueryWrapper.eq(Role::getId, roleId);
|
||||
// 如果这个角色是管理员则直接返回所有资源
|
||||
if (roleService.getOne(roleQueryWrapper).getIsSuper() == 1) {
|
||||
return sourceService.list();
|
||||
}
|
||||
// 查询属于该角色的资源列表
|
||||
LambdaQueryWrapper<RoleSource> roleSourceQueryWrapper = new LambdaQueryWrapper<>();
|
||||
roleSourceQueryWrapper.eq(RoleSource::getRoleId, roleId);
|
||||
List<RoleSource> roleSourceList = roleSourceService.list(roleSourceQueryWrapper);
|
||||
// 从结果提取出资源ID列表
|
||||
List<String> sourceIdList = roleSourceList.stream().map(RoleSource::getSourceId).toList();
|
||||
return sourceService.listByIds(sourceIdList);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.yj.earth.business.service.impl;
|
||||
|
||||
import com.yj.earth.business.domain.User;
|
||||
import com.yj.earth.business.mapper.UserMapper;
|
||||
import com.yj.earth.business.service.UserService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author 周志雄
|
||||
* @since 2025-08-28
|
||||
*/
|
||||
@Service
|
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
|
||||
|
||||
}
|
||||
26
src/main/java/com/yj/earth/common/config/CorsConfig.java
Normal file
26
src/main/java/com/yj/earth/common/config/CorsConfig.java
Normal file
@ -0,0 +1,26 @@
|
||||
package com.yj.earth.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
config.addAllowedHeader("*");
|
||||
config.addAllowedMethod("*");
|
||||
config.setAllowedOriginPatterns(Collections.singletonList("*"));
|
||||
config.setAllowCredentials(true);
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package com.yj.earth.common.config;
|
||||
|
||||
import com.yj.earth.common.exception.UnAuthException;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ApiResponse handleException(Exception e) {
|
||||
if (!e.getMessage().contains("No static resource")) {
|
||||
log.error("全局异常处理:{}", e.getMessage());
|
||||
}
|
||||
return ApiResponse.failure(e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(UnAuthException.class)
|
||||
public ApiResponse handleUnAuthException(UnAuthException e) {
|
||||
return ApiResponse.failureWithNoAuth(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
package com.yj.earth.common.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "graphhopper")
|
||||
public class GraphHopperProperties {
|
||||
private String graphLocation;
|
||||
private List<String> profiles;
|
||||
}
|
||||
42
src/main/java/com/yj/earth/common/config/JacksonConfig.java
Normal file
42
src/main/java/com/yj/earth/common/config/JacksonConfig.java
Normal file
@ -0,0 +1,42 @@
|
||||
package com.yj.earth.common.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import com.yj.earth.annotation.ExcludeField;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* Jackson配置类
|
||||
*/
|
||||
@Configuration
|
||||
public class JacksonConfig {
|
||||
|
||||
// 定义日期时间格式
|
||||
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
// 注册 JavaTimeModule 以支持 LocalDateTime 等日期类型
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
// 配置 LocalDateTime 的序列化格式
|
||||
LocalDateTimeSerializer localDateTimeSerializer = new LocalDateTimeSerializer(
|
||||
DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)
|
||||
);
|
||||
javaTimeModule.addSerializer(LocalDateTime.class, localDateTimeSerializer);
|
||||
objectMapper.registerModule(javaTimeModule);
|
||||
// 配置自定义字段过滤器
|
||||
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
|
||||
filterProvider.addFilter("excludeFieldFilter", new ExcludeField.Filter());
|
||||
// 设置默认过滤器、防止未添加@JsonFilter的类报错
|
||||
filterProvider.setDefaultFilter(com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter.serializeAll());
|
||||
objectMapper.setFilterProvider(filterProvider);
|
||||
return objectMapper;
|
||||
}
|
||||
}
|
||||
32
src/main/java/com/yj/earth/common/config/Knife4jConfig.java
Normal file
32
src/main/java/com/yj/earth/common/config/Knife4jConfig.java
Normal file
@ -0,0 +1,32 @@
|
||||
package com.yj.earth.common.config;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
|
||||
|
||||
@Configuration
|
||||
@EnableKnife4j
|
||||
public class Knife4jConfig {
|
||||
|
||||
/**
|
||||
* 自定义Swagger3文档信息
|
||||
*/
|
||||
@Bean
|
||||
public OpenAPI customOpenAPI() {
|
||||
return new OpenAPI()
|
||||
// 文档基本信息
|
||||
.info(new Info()
|
||||
.title("最新产品API文档")
|
||||
.description("远界大数据最新产品API文档【默认账号:admin、密码:admin123】")
|
||||
.version("v1.0.0")
|
||||
.contact(new Contact()
|
||||
.name("周志雄"))
|
||||
.license(new License()
|
||||
.name("Apache 2.0")
|
||||
.url("https://www.apache.org/licenses/LICENSE-2.0.html")));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package com.yj.earth.common.config;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Component
|
||||
public class MyMetaObjectConfig implements MetaObjectHandler {
|
||||
|
||||
// 插入时自动填充
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
this.strictInsertFill(metaObject, "createdAt", LocalDateTime.class, LocalDateTime.now());
|
||||
this.strictInsertFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now());
|
||||
}
|
||||
|
||||
// 更新时自动填充
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
this.strictUpdateFill(metaObject, "updatedAt", LocalDateTime.class, LocalDateTime.now());
|
||||
}
|
||||
|
||||
// 配置分页拦截器
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 添加分页拦截器
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
41
src/main/java/com/yj/earth/common/config/SaTokenConfig.java
Normal file
41
src/main/java/com/yj/earth/common/config/SaTokenConfig.java
Normal file
@ -0,0 +1,41 @@
|
||||
package com.yj.earth.common.config;
|
||||
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.yj.earth.common.exception.UnAuthException;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class SaTokenConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
List<String> excludePathPatterns = new ArrayList<>();
|
||||
excludePathPatterns.add("/user/login");
|
||||
excludePathPatterns.add("/user/add");
|
||||
excludePathPatterns.add("/doc.html");
|
||||
excludePathPatterns.add("/webjars/**");
|
||||
excludePathPatterns.add("/v3/api-docs/**");
|
||||
excludePathPatterns.add("/fileInfo/download/**");
|
||||
excludePathPatterns.add("/fileInfo/preview/**");
|
||||
excludePathPatterns.add("/data/clt/**");
|
||||
excludePathPatterns.add("/data/mbtiles/**");
|
||||
excludePathPatterns.add("/data/pak/**");
|
||||
|
||||
// 注册 Sa-Token 拦截器
|
||||
registry.addInterceptor(new SaInterceptor(handle -> {
|
||||
// 登录校验
|
||||
try {
|
||||
StpUtil.checkLogin();
|
||||
} catch (Exception e) {
|
||||
throw new UnAuthException("未携带登录凭证");
|
||||
}
|
||||
})).addPathPatterns("/**")
|
||||
.excludePathPatterns(excludePathPatterns);
|
||||
}
|
||||
}
|
||||
18
src/main/java/com/yj/earth/common/config/ServerConfig.java
Normal file
18
src/main/java/com/yj/earth/common/config/ServerConfig.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.yj.earth.common.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
public class ServerConfig {
|
||||
@Value("${server.port}")
|
||||
private int port;
|
||||
|
||||
@Value("${server.host}")
|
||||
private String host;
|
||||
|
||||
@Value("${sdk.port}")
|
||||
private int sdkPort;
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.yj.earth.common.config;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Configuration
|
||||
public class SourceTypeConfig {
|
||||
|
||||
private static final String PACKAGE = "com.yj.earth.params";
|
||||
|
||||
@Bean
|
||||
public Set<Class<?>> sourceParamClasses() throws ClassNotFoundException {
|
||||
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
|
||||
scanner.addIncludeFilter(new AnnotationTypeFilter(SourceType.class));
|
||||
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
for (var beanDefinition : scanner.findCandidateComponents(PACKAGE)) {
|
||||
classes.add(Class.forName(beanDefinition.getBeanClassName()));
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.yj.earth.common.constant;
|
||||
|
||||
public class GlobalConstant {
|
||||
// 目录类型
|
||||
public static final String DIRECTORY = "directory";
|
||||
// 显示
|
||||
public static final Integer SHOW = 1;
|
||||
// 隐藏
|
||||
public static final Integer HIDE = 0;
|
||||
// SDK路径
|
||||
public static final String SDKPATH = "sdk/geographysdk.jar";
|
||||
// SDK日志路径
|
||||
public static final String SDKLOG = "logs/sdk.log";
|
||||
}
|
||||
170
src/main/java/com/yj/earth/common/core/MapRedisTemplate.java
Normal file
170
src/main/java/com/yj/earth/common/core/MapRedisTemplate.java
Normal file
@ -0,0 +1,170 @@
|
||||
package com.yj.earth.common.core;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 封装一个 StringRedisTemplate 的功能
|
||||
*/
|
||||
@Component
|
||||
public class MapRedisTemplate {
|
||||
// 底层存储结构、使用HashMap模拟Redis
|
||||
private final Map<String, String> storage = new HashMap<>();
|
||||
// 用于存储过期时间
|
||||
private final Map<String, Long> expirationMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 获取操作字符串的接口
|
||||
*/
|
||||
public ValueOperations opsForValue() {
|
||||
return new ValueOperations();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的键
|
||||
* @param key 要删除的键
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
public Boolean delete(String key) {
|
||||
checkExpiration(key);
|
||||
return storage.remove(key) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查键是否存在
|
||||
* @param key 要检查的键
|
||||
* @return 键是否存在
|
||||
*/
|
||||
public Boolean hasKey(String key) {
|
||||
checkExpiration(key);
|
||||
return storage.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置键的过期时间
|
||||
* @param key 键
|
||||
* @param timeout 过期时间
|
||||
* @param unit 时间单位
|
||||
* @return 是否设置成功
|
||||
*/
|
||||
public Boolean expire(String key, long timeout, TimeUnit unit) {
|
||||
if (!storage.containsKey(key)) {
|
||||
return false;
|
||||
}
|
||||
long expirationTime = System.currentTimeMillis() + unit.toMillis(timeout);
|
||||
expirationMap.put(key, expirationTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取键的剩余过期时间
|
||||
* @param key 键
|
||||
* @param unit 时间单位
|
||||
* @return 剩余过期时间
|
||||
*/
|
||||
public Long getExpire(String key, TimeUnit unit) {
|
||||
checkExpiration(key);
|
||||
if (!expirationMap.containsKey(key)) {
|
||||
return -1L; // 永久有效
|
||||
}
|
||||
long remainingMillis = expirationMap.get(key) - System.currentTimeMillis();
|
||||
if (remainingMillis <= 0) {
|
||||
delete(key);
|
||||
return 0L;
|
||||
}
|
||||
return unit.convert(remainingMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查键是否过期、如果过期则删除
|
||||
*/
|
||||
private void checkExpiration(String key) {
|
||||
if (expirationMap.containsKey(key)) {
|
||||
long expirationTime = expirationMap.get(key);
|
||||
if (System.currentTimeMillis() > expirationTime) {
|
||||
storage.remove(key);
|
||||
expirationMap.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作字符串的内部类、模拟ValueOperations
|
||||
*/
|
||||
public class ValueOperations {
|
||||
/**
|
||||
* 设置键值对
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
*/
|
||||
public void set(String key, String value) {
|
||||
storage.put(key, value);
|
||||
// 设置值时清除过期时间、模拟Redis行为
|
||||
expirationMap.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置键值对并指定过期时间
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param timeout 过期时间
|
||||
* @param unit 时间单位
|
||||
*/
|
||||
public void set(String key, String value, long timeout, TimeUnit unit) {
|
||||
storage.put(key, value);
|
||||
long expirationTime = System.currentTimeMillis() + unit.toMillis(timeout);
|
||||
expirationMap.put(key, expirationTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取键对应的值
|
||||
* @param key 键
|
||||
* @return 对应的值
|
||||
*/
|
||||
public String get(String key) {
|
||||
checkExpiration(key);
|
||||
return storage.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果键不存在则设置值
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return 是否设置成功
|
||||
*/
|
||||
public Boolean setIfAbsent(String key, String value) {
|
||||
checkExpiration(key);
|
||||
if (!storage.containsKey(key)) {
|
||||
storage.put(key, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增操作
|
||||
* @param key 键
|
||||
* @return 自增后的值
|
||||
*/
|
||||
public Long increment(String key) {
|
||||
return increment(key, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加指定的值
|
||||
* @param key 键
|
||||
* @param delta 要增加的值
|
||||
* @return 增加后的值
|
||||
*/
|
||||
public Long increment(String key, long delta) {
|
||||
checkExpiration(key);
|
||||
String value = storage.get(key);
|
||||
long num = value == null ? 0 : Long.parseLong(value);
|
||||
num += delta;
|
||||
storage.put(key, String.valueOf(num));
|
||||
return num;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package com.yj.earth.common.exception;
|
||||
|
||||
public class UnAuthException extends RuntimeException{
|
||||
/**
|
||||
* 带异常信息的构造方法
|
||||
*/
|
||||
public UnAuthException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package com.yj.earth.common.service;
|
||||
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.yj.earth.business.domain.Role;
|
||||
import com.yj.earth.business.domain.Source;
|
||||
import com.yj.earth.business.domain.User;
|
||||
import com.yj.earth.business.service.RoleService;
|
||||
import com.yj.earth.business.service.SourceService;
|
||||
import com.yj.earth.business.service.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ServerInitService {
|
||||
@Resource
|
||||
private SourceService sourceService;
|
||||
@Resource
|
||||
private UserService userService;
|
||||
@Resource
|
||||
private RoleService roleService;
|
||||
|
||||
public void init() {
|
||||
// 查询数据库所有需要加载的资源
|
||||
List<Source> list =sourceService.list(new LambdaQueryWrapper<Source>()
|
||||
.eq(Source::getSourceType, "terrain")
|
||||
.or().eq(Source::getSourceType, "layer")
|
||||
.or().eq(Source::getSourceType, "tileset"));
|
||||
// 依次初始化
|
||||
for (Source source : list) {
|
||||
// 同步资源
|
||||
sourceService.getDetail(source.getSourcePath(), sourceService.addAndGetSourceId(source.getSourcePath()));
|
||||
log.info("初始化资源<--{}-->完成", source.getSourceName());
|
||||
}
|
||||
}
|
||||
|
||||
public void checkDefaultData() {
|
||||
// 查询角色表和用户表是否有数据
|
||||
if(roleService.count() == 0 && userService.count() == 0) {
|
||||
log.info("初始化默认数据");
|
||||
// 新增一个管理员角色
|
||||
Role adminRole = new Role();
|
||||
adminRole.setRoleName("管理员");
|
||||
adminRole.setDescription("系统管理员");
|
||||
adminRole.setIsSuper(1);
|
||||
roleService.save(adminRole);
|
||||
// 新增一个默认角色
|
||||
Role defaultRole = new Role();
|
||||
defaultRole.setRoleName("默认角色");
|
||||
defaultRole.setDescription("系统默认角色");
|
||||
defaultRole.setIsSuper(0);
|
||||
roleService.save(defaultRole);
|
||||
// 新增一个用户
|
||||
User user = new User();
|
||||
user.setUsername("admin");
|
||||
user.setPassword(BCrypt.hashpw("admin123", BCrypt.gensalt()));
|
||||
user.setNickname("管理员");
|
||||
user.setRoleId(adminRole.getId());
|
||||
user.setPhone("13888888888");
|
||||
userService.save(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.yj.earth.common.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
public class SourceParamsValidator {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final Map<String, Class<?>> sourceTypeMap = new HashMap<>();
|
||||
|
||||
@Autowired
|
||||
public SourceParamsValidator(ObjectMapper objectMapper, Set<Class<?>> sourceParamClasses) {
|
||||
this.objectMapper = objectMapper;
|
||||
|
||||
// 初始化资源类型与参数类的映射关系
|
||||
for (Class<?> clazz : sourceParamClasses) {
|
||||
SourceType annotation = clazz.getAnnotation(SourceType.class);
|
||||
if (annotation != null) {
|
||||
sourceTypeMap.put(annotation.value(), clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并转换参数
|
||||
*/
|
||||
public Object validateAndConvert(String sourceType, Map<String, Object> params) {
|
||||
// 检查是否有对应的参数类
|
||||
Class<?> paramClass = sourceTypeMap.get(sourceType);
|
||||
if (paramClass == null) {
|
||||
String message = "不支持 " + sourceType + "的资源类型";
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
// 转换并验证参数
|
||||
try {
|
||||
return objectMapper.convertValue(params, paramClass);
|
||||
} catch (IllegalArgumentException e) {
|
||||
String message = "请核对类型和参数";
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有支持的资源类型
|
||||
*/
|
||||
public Set<String> getSupportedSourceTypes() {
|
||||
return sourceTypeMap.keySet();
|
||||
}
|
||||
}
|
||||
66
src/main/java/com/yj/earth/common/util/AesEncryptUtil.java
Normal file
66
src/main/java/com/yj/earth/common/util/AesEncryptUtil.java
Normal file
@ -0,0 +1,66 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* AES对称加密工具类
|
||||
*/
|
||||
public class AesEncryptUtil {
|
||||
|
||||
/**
|
||||
* AES加密
|
||||
* @param content 待加密内容
|
||||
* @param key 密钥(16位/24位/32位、对应AES-128/AES-192/AES-256)
|
||||
* @param algorithm 加密算法(如AES/CBC/PKCS5Padding)
|
||||
* @return 加密后的Base64字符串
|
||||
*/
|
||||
public static String encrypt(String content, String key, String algorithm) {
|
||||
try {
|
||||
// 创建密钥
|
||||
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
|
||||
|
||||
// 初始化加密器
|
||||
Cipher cipher = Cipher.getInstance(algorithm);
|
||||
|
||||
// 如果是CBC模式、需要初始化向量IV(与密钥同长度)
|
||||
if (algorithm.contains("CBC")) {
|
||||
IvParameterSpec iv = new IvParameterSpec(key.substring(0, 16).getBytes(StandardCharsets.UTF_8));
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
|
||||
} else {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||
}
|
||||
|
||||
// 加密并转为Base64
|
||||
byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.getEncoder().encodeToString(encrypted);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("AES加密失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密方法(如果需要解密可以实现)
|
||||
*/
|
||||
public static String decrypt(String encryptedContent, String key, String algorithm) {
|
||||
try {
|
||||
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
|
||||
Cipher cipher = Cipher.getInstance(algorithm);
|
||||
|
||||
if (algorithm.contains("CBC")) {
|
||||
IvParameterSpec iv = new IvParameterSpec(key.substring(0, 16).getBytes(StandardCharsets.UTF_8));
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
|
||||
} else {
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey);
|
||||
}
|
||||
|
||||
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedContent));
|
||||
return new String(decrypted, StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("AES解密失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
src/main/java/com/yj/earth/common/util/ApiResponse.java
Normal file
56
src/main/java/com/yj/earth/common/util/ApiResponse.java
Normal file
@ -0,0 +1,56 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiResponse<T> {
|
||||
|
||||
private int code; // 状态码
|
||||
private T data; // 响应数据
|
||||
private String message; // 响应消息
|
||||
|
||||
// 私有化构造方法
|
||||
private ApiResponse(int code, T data, String message) {
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
// 成功响应(带数据)
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
return new ApiResponse<>(200, data, "操作成功");
|
||||
}
|
||||
|
||||
// 成功响应(无数据)
|
||||
public static <T> ApiResponse<T> successWithMessage(String message) {
|
||||
return new ApiResponse<>(200, null, message);
|
||||
}
|
||||
|
||||
// 失败响应(带自定义消息)
|
||||
public static <T> ApiResponse<T> failure(String message) {
|
||||
return new ApiResponse<>(20000, null, message);
|
||||
}
|
||||
|
||||
// 失败响应(未授权)
|
||||
public static <T> ApiResponse<T> failureWithNoAuth(String message) {
|
||||
return new ApiResponse<>(401, null, message);
|
||||
}
|
||||
|
||||
// 设置 data 字段、并返回当前对象、支持链式调用
|
||||
public ApiResponse<T> setData(T data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
// 设置 message 字段、并返回当前对象、支持链式调用
|
||||
public ApiResponse<T> setMessage(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
// 设置 code 字段、并返回当前对象、支持链式调用
|
||||
public ApiResponse<T> setCode(int code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
86
src/main/java/com/yj/earth/common/util/CodeUtil.java
Normal file
86
src/main/java/com/yj/earth/common/util/CodeUtil.java
Normal file
@ -0,0 +1,86 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
|
||||
import com.baomidou.mybatisplus.generator.config.OutputFile;
|
||||
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
|
||||
import com.baomidou.mybatisplus.generator.fill.Column;
|
||||
import com.yj.earth.datasource.DatabaseManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
|
||||
public class CodeUtil {
|
||||
|
||||
// SQLite数据库配置
|
||||
private static String databasePath = null;
|
||||
private static String author = "周志雄";
|
||||
|
||||
public static void main(String[] args) {
|
||||
DatabaseManager.initDatabase(DatabaseManager.DatabaseType.SQLITE);
|
||||
databasePath = DatabaseManager.getSqliteDbFilePath();
|
||||
|
||||
// 检查数据库路径是否有效
|
||||
if (databasePath == null || databasePath.trim().isEmpty()) {
|
||||
throw new RuntimeException("数据库路径未正确初始化");
|
||||
}
|
||||
|
||||
// 确保数据库目录存在
|
||||
File dbFile = new File(databasePath);
|
||||
File parentDir = dbFile.getParentFile();
|
||||
if (!parentDir.exists()) {
|
||||
parentDir.mkdirs();
|
||||
}
|
||||
|
||||
// 传入需要生成代码的表名
|
||||
Generation("file_info");
|
||||
}
|
||||
|
||||
public static void Generation(String... tableName) {
|
||||
// 构建SQLite连接URL
|
||||
String jdbcUrl = "jdbc:sqlite:" + databasePath;
|
||||
|
||||
// FastAutoGenerator 用来创建代码生成器实例
|
||||
FastAutoGenerator.create(jdbcUrl, "", "")
|
||||
.globalConfig(builder -> {
|
||||
builder.author(author)
|
||||
.enableSpringdoc()
|
||||
.outputDir(System.getProperty("user.dir") + "/src/main/java");
|
||||
}).packageConfig(builder -> {
|
||||
builder.entity("domain")
|
||||
.parent("com.yj.earth.business")
|
||||
.controller("controller")
|
||||
.mapper("mapper")
|
||||
.service("service")
|
||||
.serviceImpl("service.impl")
|
||||
.pathInfo(Collections.singletonMap(OutputFile.xml,
|
||||
System.getProperty("user.dir") + "/src/main/resources/mapper"));
|
||||
}).strategyConfig(builder -> {
|
||||
builder.addInclude(tableName)
|
||||
.addTablePrefix("t_")
|
||||
.entityBuilder()
|
||||
.enableLombok()
|
||||
.enableChainModel()
|
||||
.addTableFills(new Column("created_at", FieldFill.INSERT),
|
||||
new Column("updated_at", FieldFill.UPDATE))
|
||||
.naming(NamingStrategy.underline_to_camel)
|
||||
.columnNaming(NamingStrategy.underline_to_camel)
|
||||
.idType(IdType.ASSIGN_UUID)
|
||||
.formatFileName("%s")
|
||||
.mapperBuilder()
|
||||
.enableMapperAnnotation()
|
||||
.enableBaseResultMap()
|
||||
.enableBaseColumnList()
|
||||
.formatMapperFileName("%sMapper")
|
||||
.formatXmlFileName("%sMapper")
|
||||
.serviceBuilder()
|
||||
.formatServiceFileName("%sService")
|
||||
.formatServiceImplFileName("%sServiceImpl")
|
||||
.controllerBuilder()
|
||||
.enableRestStyle()
|
||||
.formatFileName("%sController")
|
||||
.enableHyphenStyle();
|
||||
}).execute();
|
||||
}
|
||||
}
|
||||
180
src/main/java/com/yj/earth/common/util/HttpUtil.java
Normal file
180
src/main/java/com/yj/earth/common/util/HttpUtil.java
Normal file
@ -0,0 +1,180 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* HTTP 请求工具类
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpUtil {
|
||||
// 编码格式
|
||||
private static final String CHARSET = "UTF-8";
|
||||
// 连接超时时间 5 秒
|
||||
private static final int CONNECT_TIMEOUT = 5000;
|
||||
// 读取超时时间 10 秒
|
||||
private static final int READ_TIMEOUT = 10000;
|
||||
// JSON 处理器
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 发送 GET 请求
|
||||
*/
|
||||
public static String doGet(String url) {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
// 设置超时配置
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(CONNECT_TIMEOUT)
|
||||
.setSocketTimeout(READ_TIMEOUT)
|
||||
.build();
|
||||
httpGet.setConfig(requestConfig);
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
|
||||
return handleResponse(response);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("GET 请求发生异常、请求 URL: {}", url, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送表单参数的 POST 请求
|
||||
* 参数类型改为 Map<String, Object> 以支持更多类型的参数值
|
||||
*/
|
||||
public static String doPostForm(String url, Map<String, Object> params) {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
// 设置超时配置
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(CONNECT_TIMEOUT)
|
||||
.setSocketTimeout(READ_TIMEOUT)
|
||||
.build();
|
||||
httpPost.setConfig(requestConfig);
|
||||
// 组装表单参数
|
||||
if (params != null && !params.isEmpty()) {
|
||||
List<NameValuePair> nameValuePairs = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||
// 将 Object 类型的值转换为字符串
|
||||
String value = entry.getValue() != null ? entry.getValue().toString() : null;
|
||||
nameValuePairs.add(new BasicNameValuePair(entry.getKey(), value));
|
||||
}
|
||||
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, CHARSET));
|
||||
}
|
||||
// 执行请求
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
return handleResponse(response);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("表单 POST 请求发生异常、请求 URL: {}、请求参数: {}", url, params, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 JSON 参数的 POST 请求
|
||||
* 参数类型统一为 Map<String, Object>
|
||||
*/
|
||||
public static String doPostJson(String url, Map<String, Object> params) {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
// 设置超时配置
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(CONNECT_TIMEOUT)
|
||||
.setSocketTimeout(READ_TIMEOUT)
|
||||
.build();
|
||||
httpPost.setConfig(requestConfig);
|
||||
// 设置 JSON 请求头
|
||||
httpPost.setHeader("Content-Type", "application/json;charset=" + CHARSET);
|
||||
// Map 转 JSON 字符串并设置为请求体
|
||||
if (params != null && !params.isEmpty()) {
|
||||
String jsonParams = objectMapper.writeValueAsString(params);
|
||||
httpPost.setEntity(new StringEntity(jsonParams, CHARSET));
|
||||
}
|
||||
// 执行请求
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
return handleResponse(response);
|
||||
}
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("JSON POST 请求参数序列化失败、请求 URL: {}、请求参数: {}", url, params, e);
|
||||
} catch (Exception e) {
|
||||
log.error("JSON POST 请求发生异常、请求 URL: {}、请求参数: {}", url, params, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 GET 请求、返回字节数组的ResponseEntity、适用于下载文件
|
||||
*/
|
||||
public static ResponseEntity<byte[]> doGetForByteArrayResponse(String url) {
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
// 设置超时配置
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(CONNECT_TIMEOUT)
|
||||
.setSocketTimeout(READ_TIMEOUT)
|
||||
.build();
|
||||
httpGet.setConfig(requestConfig);
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
|
||||
// 获取状态码
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
HttpStatus httpStatus = HttpStatus.valueOf(statusCode);
|
||||
|
||||
// 处理响应头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
Header[] allHeaders = response.getAllHeaders();
|
||||
for (Header header : allHeaders) {
|
||||
// 特别保留Content-Disposition和Content-Type、用于前端下载
|
||||
headers.add(header.getName(), header.getValue());
|
||||
}
|
||||
|
||||
// 处理响应体(二进制数据)
|
||||
HttpEntity entity = response.getEntity();
|
||||
byte[] body = entity != null ? EntityUtils.toByteArray(entity) : null;
|
||||
|
||||
// 返回ResponseEntity对象
|
||||
return new ResponseEntity<>(body, headers, httpStatus);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("GET 下载请求发生异常、请求 URL: {}", url, e);
|
||||
// 发生异常时返回500错误
|
||||
return new ResponseEntity<>(e.getMessage().getBytes(), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用响应处理方法
|
||||
*/
|
||||
private static String handleResponse(CloseableHttpResponse response) throws IOException {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == 200) {
|
||||
HttpEntity entity = response.getEntity();
|
||||
return entity != null ? EntityUtils.toString(entity, CHARSET) : null;
|
||||
}
|
||||
log.warn("HTTP 请求失败、状态码: {}、响应内容: {}", statusCode, response.getEntity() != null ? EntityUtils.toString(response.getEntity(), CHARSET) : null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
50
src/main/java/com/yj/earth/common/util/JsonMapConverter.java
Normal file
50
src/main/java/com/yj/earth/common/util/JsonMapConverter.java
Normal file
@ -0,0 +1,50 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Map与JSON互相转换工具类
|
||||
*/
|
||||
@Slf4j
|
||||
public class JsonMapConverter {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 将 Map 转换为 JSON 字符串
|
||||
*/
|
||||
public static String mapToJson(Map<String, Object> map) {
|
||||
if (map == null || map.isEmpty()) {
|
||||
return "{}";
|
||||
}
|
||||
|
||||
try {
|
||||
return objectMapper.writeValueAsString(map);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Map转JSON失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 JSON 字符串转换为 Map
|
||||
*/
|
||||
public static Map<String, Object> jsonToMap(String json) {
|
||||
if (json == null || json.trim().isEmpty()) {
|
||||
return new HashMap<>(0);
|
||||
}
|
||||
|
||||
try {
|
||||
return objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
|
||||
} catch (Exception e) {
|
||||
log.error("JSON转Map失败、JSON内容: {}", json, e);
|
||||
return new HashMap<>(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
116
src/main/java/com/yj/earth/common/util/MapUtil.java
Normal file
116
src/main/java/com/yj/earth/common/util/MapUtil.java
Normal file
@ -0,0 +1,116 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MapUtil {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 合并两个 Map、如果存在相同的 Key、则用新 Map 中的值替换原始 Map 中的值
|
||||
* 如果原始 Map 中有某个 Key 而新 Map 中没有、则保持原始 Map 中的值不变
|
||||
*/
|
||||
public static <K, V> void mergeMaps(Map<K, V> originalMap, Map<K, V> newMap) {
|
||||
// 检查参数是否为null、避免空指针异常
|
||||
if (originalMap == null || newMap == null) {
|
||||
throw new IllegalArgumentException("参数Map不能为null");
|
||||
}
|
||||
|
||||
// 遍历新Map中的所有键值对
|
||||
for (Map.Entry<K, V> entry : newMap.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
// 如果原始Map中存在相同的key、则替换值
|
||||
if (originalMap.containsKey(key)) {
|
||||
originalMap.put(key, entry.getValue());
|
||||
}
|
||||
// 如果原始Map中不存在该key、则不做任何操作
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将JSON字符串转换为Map对象
|
||||
*
|
||||
* @param jsonString JSON格式的字符串
|
||||
* @return 转换后的Map对象、如果JSON为空则返回空Map
|
||||
* @throws IllegalArgumentException 当JSON字符串无效或解析失败时抛出
|
||||
*/
|
||||
public static <K, V> Map<K, V> jsonToMap(String jsonString) {
|
||||
if (jsonString == null || jsonString.trim().isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
try {
|
||||
// 将JSON字符串转换为Map
|
||||
return objectMapper.readValue(jsonString, Map.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException("JSON字符串解析失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Map 对象转换为JSON字符串
|
||||
*/
|
||||
public static <K, V> String mapToString(Map<K, V> map) {
|
||||
if (map == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
// 将Map转换为JSON字符串
|
||||
return objectMapper.writeValueAsString(map);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException("Map转换为JSON字符串失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将任意类型对象转换为JSON字符串
|
||||
*/
|
||||
public static <T> String objectToJson(T object) {
|
||||
if (object == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
// 将对象转换为JSON字符串
|
||||
return objectMapper.writeValueAsString(object);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new IllegalArgumentException("对象转换为JSON字符串失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Map中获取指定key的字符串值
|
||||
*
|
||||
* @param map 数据源Map
|
||||
* @param key 要获取的字段名
|
||||
* @return 字段的字符串值、如果map为null或key不存在则返回null
|
||||
*/
|
||||
public static String getString(Map<?, ?> map, String key) {
|
||||
if (map == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object value = map.get(key);
|
||||
return value != null ? value.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接从JSON字符串中获取指定key的字符串值
|
||||
*
|
||||
* @param jsonString JSON格式的字符串
|
||||
* @param key 要获取的字段名
|
||||
* @return 字段的字符串值、如果JSON为空或key不存在则返回null
|
||||
*/
|
||||
public static String getString(String jsonString, String key) {
|
||||
if (jsonString == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<?, ?> map = jsonToMap(jsonString);
|
||||
return getString(map, key);
|
||||
}
|
||||
}
|
||||
114
src/main/java/com/yj/earth/common/util/PortKillUtil.java
Normal file
114
src/main/java/com/yj/earth/common/util/PortKillUtil.java
Normal file
@ -0,0 +1,114 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
@Slf4j
|
||||
public class PortKillUtil {
|
||||
|
||||
/**
|
||||
* 根据端口号杀死对应的进程
|
||||
*/
|
||||
public static boolean killProcessByPort(int port) {
|
||||
// 获取操作系统类型
|
||||
String osName = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
try {
|
||||
if (osName.contains("windows")) {
|
||||
// Windows系统处理逻辑
|
||||
return killWindowsProcess(port);
|
||||
} else if (osName.contains("linux") || osName.contains("unix")) {
|
||||
// Linux/Unix系统处理逻辑
|
||||
return killLinuxProcess(port);
|
||||
} else {
|
||||
log.error("不支持的操作系统: " + osName);
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("杀死进程时发生错误: " + e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 杀死 Windows 系统中占用指定端口的进程
|
||||
*/
|
||||
private static boolean killWindowsProcess(int port) throws IOException, InterruptedException {
|
||||
// 查找占用端口的进程ID
|
||||
Process process = Runtime.getRuntime().exec("netstat -ano | findstr :" + port);
|
||||
process.waitFor();
|
||||
|
||||
// 读取命令输出
|
||||
String pid = getWindowsPidFromOutput(process.getInputStream());
|
||||
if (pid == null || pid.isEmpty()) {
|
||||
log.error("端口 " + port + " 未被占用");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 杀死找到的进程
|
||||
Process killProcess = Runtime.getRuntime().exec("taskkill /F /PID " + pid);
|
||||
int exitCode = killProcess.waitFor();
|
||||
|
||||
if (exitCode == 0) {
|
||||
log.info("成功杀死端口 " + port + " 对应的进程、PID: " + pid);
|
||||
return true;
|
||||
} else {
|
||||
log.error("杀死端口 " + port + " 对应的进程失败、PID: " + pid);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Windows 命令输出中提取进程 ID
|
||||
*/
|
||||
private static String getWindowsPidFromOutput(InputStream inputStream) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
// 查找包含LISTENING状态的行
|
||||
if (line.contains("LISTENING")) {
|
||||
// 提取最后一个空格后的数字作为PID
|
||||
String[] parts = line.split("\\s+");
|
||||
return parts[parts.length - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 杀死 Linux 系统中占用指定端口的进程
|
||||
*/
|
||||
private static boolean killLinuxProcess(int port) throws IOException, InterruptedException {
|
||||
// 查找占用端口的进程ID
|
||||
Process process = Runtime.getRuntime().exec("lsof -i:" + port + " -t");
|
||||
process.waitFor();
|
||||
|
||||
// 读取命令输出获取PID
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
|
||||
String pid = reader.readLine();
|
||||
if (pid == null || pid.isEmpty()) {
|
||||
log.error("端口 " + port + " 未被占用");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 杀死找到的进程
|
||||
Process killProcess = Runtime.getRuntime().exec("kill -9 " + pid);
|
||||
int exitCode = killProcess.waitFor();
|
||||
|
||||
if (exitCode == 0) {
|
||||
log.info("成功杀死端口 " + port + " 对应的进程、PID: " + pid);
|
||||
return true;
|
||||
} else {
|
||||
log.error("杀死端口 " + port + " 对应的进程失败、PID: " + pid);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
33
src/main/java/com/yj/earth/common/util/PositionUtil.java
Normal file
33
src/main/java/com/yj/earth/common/util/PositionUtil.java
Normal file
@ -0,0 +1,33 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
public class PositionUtil {
|
||||
private static final double a = 6378137.0; // 椭球长半轴
|
||||
private static final double e = 0.0818191908426; // 椭球第一偏心率
|
||||
private static final double epsilon = 1e-8; // 迭代精度
|
||||
private static final double r2d = 180.0 / Math.PI; // 弧度转角度
|
||||
|
||||
public static double[] xyz2Blh(double x, double y, double z) {
|
||||
double tmpX = x;
|
||||
double tmpY = y;
|
||||
double tmpZ = z;
|
||||
|
||||
double curB = 0.0;
|
||||
double N = 0.0;
|
||||
double calB = Math.atan2(tmpZ, Math.sqrt(tmpX * tmpX + tmpY * tmpY));
|
||||
|
||||
int counter = 0;
|
||||
while (Math.abs(curB - calB) * r2d > epsilon && counter < 25) {
|
||||
curB = calB;
|
||||
N = a / Math.sqrt(1 - e * e * Math.sin(curB) * Math.sin(curB));
|
||||
calB = Math.atan2(tmpZ + N * e * e * Math.sin(curB),
|
||||
Math.sqrt(tmpX * tmpX + tmpY * tmpY));
|
||||
counter++;
|
||||
}
|
||||
|
||||
double longitude = Math.atan2(tmpY, tmpX) * r2d;
|
||||
double latitude = curB * r2d;
|
||||
double height = tmpZ / Math.sin(curB) - N * (1 - e * e);
|
||||
|
||||
return new double[]{longitude, latitude, height};
|
||||
}
|
||||
}
|
||||
126
src/main/java/com/yj/earth/common/util/SdkUtil.java
Normal file
126
src/main/java/com/yj/earth/common/util/SdkUtil.java
Normal file
@ -0,0 +1,126 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import com.yj.earth.common.constant.GlobalConstant;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class SdkUtil {
|
||||
// 保存SDK进程引用
|
||||
private static Process sdkProcess;
|
||||
// 保存SDK端口号、用于关闭时强制终止
|
||||
private static Integer sdkPort;
|
||||
|
||||
// 对外提供的启动入口
|
||||
public static void startSdkIfConfigured() throws IOException {
|
||||
// 读取配置
|
||||
sdkPort = getSdkPortFromYamlConfig();
|
||||
// 未配置则不启动
|
||||
if (sdkPort == null) {
|
||||
log.info("请先配置SDK端口");
|
||||
return;
|
||||
}
|
||||
// 配置存在时、正常启动SDK
|
||||
startSdkJar(sdkPort);
|
||||
}
|
||||
|
||||
// 接收已确认的端口、启动SDK
|
||||
private static void startSdkJar(int sdkPort) throws IOException {
|
||||
// 获取项目根目录(当前工作目录)
|
||||
String projectRoot = System.getProperty("user.dir");
|
||||
// 获取SDK完整路径
|
||||
String sdkJarPath = new File(projectRoot, GlobalConstant.SDKPATH).getAbsolutePath();
|
||||
// 校验SDK
|
||||
File sdkJarFile = new File(sdkJarPath);
|
||||
if (!sdkJarFile.exists() || !sdkJarFile.isFile()) {
|
||||
log.error("SDK不存在或不是有效文件:{}", sdkJarPath);
|
||||
}
|
||||
log.info("准备启动SDK: {}", sdkJarPath);
|
||||
log.info("使用SDK端口: {}", sdkPort);
|
||||
// 构建启动命令、添加 -Dserver.port 参数
|
||||
List<String> command = new ArrayList<>();
|
||||
command.add("java");
|
||||
command.add("-Dserver.port=" + sdkPort);
|
||||
command.add("-jar");
|
||||
command.add(sdkJarPath);
|
||||
// 构建进程启动器
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||
// 打印执行的命令
|
||||
String commandStr = command.stream().collect(Collectors.joining(" "));
|
||||
log.info("执行命令: {}", commandStr);
|
||||
// 输出SDK的控制台日志到当前应用的日志中
|
||||
processBuilder.redirectErrorStream(true);
|
||||
// 日志文件路径建议优化: 避免与项目根目录混淆
|
||||
File sdkLogFile = new File(projectRoot, GlobalConstant.SDKLOG);
|
||||
// 确保目录存在(避免日志写入失败)
|
||||
if (!sdkLogFile.getParentFile().exists()) {
|
||||
sdkLogFile.getParentFile().mkdirs();
|
||||
}
|
||||
processBuilder.redirectOutput(sdkLogFile);
|
||||
// 启动进程(非阻塞)
|
||||
sdkProcess = processBuilder.start();
|
||||
log.info("SDK已在后台启动、进程ID: {}", sdkProcess.pid());
|
||||
// 注册JVM关闭钩子、在主程序退出时关闭SDK进程
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
if (sdkProcess != null && sdkProcess.isAlive()) {
|
||||
log.info("主程序关闭、正在停止SDK进程(PID: {})...", sdkProcess.pid());
|
||||
// 销毁子进程
|
||||
sdkProcess.destroy();
|
||||
try {
|
||||
// 等待进程终止(最多5秒)
|
||||
boolean terminated = sdkProcess.waitFor(5, TimeUnit.SECONDS);
|
||||
if (terminated) {
|
||||
log.info("SDK进程已成功停止");
|
||||
} else {
|
||||
log.warn("SDK进程未能正常停止、尝试通过端口{}强制终止...", sdkPort);
|
||||
// 通过端口强制终止
|
||||
boolean killSuccess = PortKillUtil.killProcessByPort(sdkPort);
|
||||
if (killSuccess) {
|
||||
log.info("已通过端口{}强制终止SDK进程", sdkPort);
|
||||
} else {
|
||||
log.error("通过端口{}强制终止SDK进程失败", sdkPort);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
log.error("停止SDK进程时发生中断", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}, "SDK-Process-Shutdown-Hook"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置文件读取SDK端口配置
|
||||
*/
|
||||
private static Integer getSdkPortFromYamlConfig() {
|
||||
Yaml yaml = new Yaml();
|
||||
try (InputStream inputStream = new ClassPathResource("application.yml").getInputStream()) {
|
||||
// 解析YAML文件为Map
|
||||
Map<String, Object> yamlMap = yaml.load(inputStream);
|
||||
// 逐级获取配置
|
||||
if (yamlMap.containsKey("sdk")) {
|
||||
Object sdkObj = yamlMap.get("sdk");
|
||||
if (sdkObj instanceof Map) {
|
||||
Map<?, ?> sdkMap = (Map<?, ?>) sdkObj;
|
||||
if (sdkMap.containsKey("port")) {
|
||||
return ((Number) sdkMap.get("port")).intValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
log.error("未配置SDK端口");
|
||||
} catch (IOException e) {
|
||||
log.error("读取配置文件失败", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
package com.yj.earth.common.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.*;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
import java.util.HexFormat;
|
||||
|
||||
/**
|
||||
* 服务器唯一标识工具类
|
||||
* 基于CPU、主板、磁盘、网卡硬件信息生成唯一标识、不更换核心硬件则标识不变
|
||||
*/
|
||||
@Slf4j
|
||||
public class ServerUniqueIdUtil {
|
||||
|
||||
/**
|
||||
* 获取服务器唯一标识
|
||||
*/
|
||||
public static String getServerUniqueId() {
|
||||
// 初始化系统信息(oshi核心入口)
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
HardwareAbstractionLayer hardware = systemInfo.getHardware();
|
||||
|
||||
try {
|
||||
// 收集稳定的核心硬件信息
|
||||
StringBuilder hardwareRawInfo = new StringBuilder();
|
||||
|
||||
// CPU唯一标识
|
||||
CentralProcessor cpu = hardware.getProcessor();
|
||||
String cpuId = cpu.getProcessorIdentifier().getProcessorID();
|
||||
if (cpuId != null && !cpuId.trim().isEmpty()) {
|
||||
hardwareRawInfo.append(cpuId).append("|");
|
||||
}
|
||||
|
||||
// 主板UUID
|
||||
ComputerSystem mainBoard = hardware.getComputerSystem();
|
||||
String boardUuid = mainBoard.getHardwareUUID();
|
||||
if (boardUuid != null && !boardUuid.trim().isEmpty()) {
|
||||
hardwareRawInfo.append(boardUuid).append("|");
|
||||
}
|
||||
|
||||
// 第一个物理磁盘序列号
|
||||
List<HWDiskStore> disks = hardware.getDiskStores();
|
||||
for (HWDiskStore disk : disks) {
|
||||
// 过滤虚拟磁盘(
|
||||
if (!disk.getModel().toLowerCase().contains("virtual") && disk.getSize() > 0) {
|
||||
String diskSerial = disk.getSerial();
|
||||
if (diskSerial != null && !diskSerial.trim().isEmpty()) {
|
||||
hardwareRawInfo.append(diskSerial).append("|");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 第一个物理网卡MAC地址
|
||||
List<NetworkIF> netCards = hardware.getNetworkIFs();
|
||||
for (NetworkIF netCard : netCards) {
|
||||
String mac = netCard.getMacaddr();
|
||||
// 过滤条件非空、非全零MAC、非回环网卡
|
||||
if (mac != null && !mac.trim().isEmpty() && !mac.startsWith("00:00:00:00:00:00")) {
|
||||
hardwareRawInfo.append(mac).append("|");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// MD5哈希
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] hashBytes = md.digest(hardwareRawInfo.toString().getBytes());
|
||||
|
||||
// 字节数组转十六进制字符串
|
||||
return HexFormat.of().formatHex(hashBytes).toUpperCase();
|
||||
|
||||
} catch (Exception e) {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
401
src/main/java/com/yj/earth/datasource/DatabaseManager.java
Normal file
401
src/main/java/com/yj/earth/datasource/DatabaseManager.java
Normal file
@ -0,0 +1,401 @@
|
||||
package com.yj.earth.datasource;
|
||||
|
||||
import com.yj.earth.design.*;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
public class DatabaseManager {
|
||||
public enum DatabaseType {
|
||||
SQLITE, MYSQL
|
||||
}
|
||||
|
||||
private static final String EXCLUDE_SERIAL_FIELD = "serialVersionUID";
|
||||
private static final List<Class<?>> ENTITY_CLASSES;
|
||||
private static final String FOLDER_NAME = "yjearth";
|
||||
private static final String DB_FILE_NAME = "app.db";
|
||||
private static String sqliteDbFilePath;
|
||||
private static boolean isSqliteInitialized = false;
|
||||
private static String mysqlUrl;
|
||||
private static String mysqlUsername;
|
||||
private static String mysqlPassword;
|
||||
private static boolean isMysqlInitialized = false;
|
||||
private static final String SQLITE_DRIVER = "org.sqlite.JDBC";
|
||||
private static final String MYSQL_DRIVER = "com.mysql.cj.jdbc.Driver";
|
||||
private static final String MYSQL_TABLE_ENGINE = "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
|
||||
|
||||
static {
|
||||
List<Class<?>> classes = new ArrayList<>();
|
||||
classes.add(User.class);
|
||||
classes.add(Role.class);
|
||||
classes.add(Source.class);
|
||||
classes.add(RoleSource.class);
|
||||
classes.add(FileInfo.class);
|
||||
ENTITY_CLASSES = Collections.unmodifiableList(classes);
|
||||
}
|
||||
|
||||
public static void initDatabase(DatabaseType dbType) {
|
||||
if (dbType == null) {
|
||||
log.error("数据库类型不能为空");
|
||||
return;
|
||||
}
|
||||
if (isInitialized(dbType)) {
|
||||
log.info("{}数据库已初始化、无需重复执行", dbType.name());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
loadConfig(dbType);
|
||||
if (!validateConfig(dbType)) {
|
||||
log.error("{}数据库配置无效、初始化失败", dbType.name());
|
||||
return;
|
||||
}
|
||||
loadDriver(dbType);
|
||||
preProcess(dbType);
|
||||
createTablesForEntities(dbType);
|
||||
markInitialized(dbType, true);
|
||||
log.info("{}数据库初始化成功({})", dbType.name(), getInitSuccessMsg(dbType));
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.error("{}驱动类未找到、请检查依赖: {}", dbType.name(), e.getMessage(), e);
|
||||
} catch (SQLException e) {
|
||||
log.error("{}数据库操作失败: {}", dbType.name(), e.getMessage(), e);
|
||||
} catch (Exception e) {
|
||||
log.error("{}数据库初始化未知异常: {}", dbType.name(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getUnderlineName(String camelCaseName) {
|
||||
if (camelCaseName == null || camelCaseName.isEmpty()) {
|
||||
throw new IllegalArgumentException("命名转换的入参名称不能为空");
|
||||
}
|
||||
StringBuilder underlineName = new StringBuilder();
|
||||
underlineName.append(Character.toLowerCase(camelCaseName.charAt(0)));
|
||||
|
||||
for (int i = 1; i < camelCaseName.length(); i++) {
|
||||
char currentChar = camelCaseName.charAt(i);
|
||||
if (Character.isUpperCase(currentChar)) {
|
||||
underlineName.append("_").append(Character.toLowerCase(currentChar));
|
||||
} else {
|
||||
underlineName.append(currentChar);
|
||||
}
|
||||
}
|
||||
return underlineName.toString();
|
||||
}
|
||||
|
||||
public static String getSqliteDbFilePath() {
|
||||
return sqliteDbFilePath;
|
||||
}
|
||||
|
||||
private static void loadConfig(DatabaseType dbType) throws IOException {
|
||||
if (dbType != DatabaseType.MYSQL) return;
|
||||
|
||||
try (InputStream yamlInput = new ClassPathResource("application.yml").getInputStream()) {
|
||||
Yaml yaml = new Yaml();
|
||||
Map<String, Object> yamlData = yaml.load(yamlInput);
|
||||
Map<String, Object> springMap = (Map<String, Object>) yamlData.get("spring");
|
||||
Map<String, Object> datasourceMap = (Map<String, Object>) springMap.get("datasource");
|
||||
Map<String, Object> mysqlMap = (Map<String, Object>) datasourceMap.get("mysql");
|
||||
|
||||
mysqlUrl = getConfigValue(mysqlMap, "url");
|
||||
mysqlUsername = getConfigValue(mysqlMap, "username");
|
||||
mysqlPassword = getConfigValue(mysqlMap, "password");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean validateConfig(DatabaseType dbType) {
|
||||
if (dbType == DatabaseType.SQLITE) return true;
|
||||
|
||||
if (mysqlUrl == null || mysqlUrl.isEmpty()) {
|
||||
log.error("MySQL配置缺失: spring.datasource.mysql.url");
|
||||
return false;
|
||||
}
|
||||
if (mysqlUsername == null || mysqlUsername.isEmpty()) {
|
||||
log.error("MySQL配置缺失: spring.datasource.mysql.username");
|
||||
return false;
|
||||
}
|
||||
if (!mysqlUrl.startsWith("jdbc:mysql://")) {
|
||||
log.error("MySQL URL格式错误、需以[jdbc:mysql://]开头、当前: {}", mysqlUrl);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void loadDriver(DatabaseType dbType) throws ClassNotFoundException {
|
||||
if (dbType == DatabaseType.SQLITE) {
|
||||
Class.forName(SQLITE_DRIVER);
|
||||
log.info("SQLite驱动加载成功");
|
||||
} else {
|
||||
Class.forName(MYSQL_DRIVER);
|
||||
log.info("MySQL驱动加载成功");
|
||||
}
|
||||
}
|
||||
|
||||
private static void preProcess(DatabaseType dbType) throws IOException {
|
||||
if (dbType != DatabaseType.SQLITE) return;
|
||||
|
||||
Path systemCacheDir = getRecommendedCacheDirectory();
|
||||
if (systemCacheDir == null) {
|
||||
throw new IOException("无法获取有效的系统缓存目录、无法创建SQLite文件");
|
||||
}
|
||||
|
||||
Path appDir = systemCacheDir.resolve(FOLDER_NAME);
|
||||
Path dbFile = appDir.resolve(DB_FILE_NAME);
|
||||
sqliteDbFilePath = dbFile.toAbsolutePath().toString();
|
||||
|
||||
if (!Files.exists(appDir)) {
|
||||
Files.createDirectories(appDir);
|
||||
log.info("创建SQLite应用目录: {}", appDir);
|
||||
}
|
||||
if (!Files.isWritable(appDir)) {
|
||||
throw new IOException("无权限写入SQLite目录: " + appDir);
|
||||
}
|
||||
|
||||
if (!Files.exists(dbFile)) {
|
||||
Files.createFile(dbFile);
|
||||
log.info("创建SQLite新文件: {}", sqliteDbFilePath);
|
||||
} else {
|
||||
log.info("SQLite文件已存在: {}", sqliteDbFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
private static void createTablesForEntities(DatabaseType dbType) throws SQLException {
|
||||
if (ENTITY_CLASSES.isEmpty()) {
|
||||
log.warn("未配置需要创建表的实体类、跳过批量建表");
|
||||
return;
|
||||
}
|
||||
|
||||
try (Connection connection = getConnection(dbType)) {
|
||||
for (Class<?> entityClass : ENTITY_CLASSES) {
|
||||
createTableIfNotExists(connection, dbType, entityClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void createTableIfNotExists(Connection connection, DatabaseType dbType, Class<?> entityClass) throws SQLException {
|
||||
String tableName = getUnderlineName(entityClass.getSimpleName());
|
||||
|
||||
if (isTableExists(connection, dbType, tableName)) {
|
||||
log.info("{}表[{}]已存在、跳过创建", dbType.name(), tableName);
|
||||
return;
|
||||
}
|
||||
|
||||
String createSql = generateCreateTableSql(dbType, entityClass, tableName);
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.execute(createSql);
|
||||
log.info("{}表[{}]创建成功、执行SQL: {}", dbType.name(), tableName, createSql);
|
||||
}
|
||||
}
|
||||
|
||||
private static Connection getConnection(DatabaseType dbType) throws SQLException {
|
||||
if (dbType == DatabaseType.SQLITE) {
|
||||
return DriverManager.getConnection("jdbc:sqlite:" + sqliteDbFilePath);
|
||||
} else {
|
||||
return DriverManager.getConnection(mysqlUrl, mysqlUsername, mysqlPassword);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isTableExists(Connection connection, DatabaseType dbType, String tableName) throws SQLException {
|
||||
if (dbType == DatabaseType.SQLITE) {
|
||||
try (ResultSet rs = connection.getMetaData().getTables(null, null, tableName, new String[]{"TABLE"})) {
|
||||
return rs.next();
|
||||
}
|
||||
} else {
|
||||
String dbName = extractDbNameFromMysqlUrl(mysqlUrl);
|
||||
String checkSql = "SELECT 1 FROM information_schema.tables WHERE table_schema = ? AND table_name = ? LIMIT 1";
|
||||
|
||||
try (PreparedStatement pstmt = connection.prepareStatement(checkSql)) {
|
||||
pstmt.setString(1, dbName);
|
||||
pstmt.setString(2, tableName);
|
||||
try (ResultSet rs = pstmt.executeQuery()) {
|
||||
return rs.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateCreateTableSql(DatabaseType dbType, Class<?> entityClass, String tableName) {
|
||||
StringBuilder sqlBuilder = new StringBuilder("CREATE TABLE IF NOT EXISTS ").append(tableName).append(" (");
|
||||
Field[] fields = entityClass.getDeclaredFields();
|
||||
List<String> columnDefinitions = new ArrayList<>();
|
||||
|
||||
for (Field field : fields) {
|
||||
if (EXCLUDE_SERIAL_FIELD.equals(field.getName()) || Modifier.isStatic(field.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
|
||||
String columnName = getUnderlineName(field.getName());
|
||||
String dbTypeStr = mapJavaTypeToDbType(dbType, field.getType());
|
||||
StringBuilder columnDef = new StringBuilder(columnName).append(" ").append(dbTypeStr);
|
||||
|
||||
if ("id".equals(field.getName())) {
|
||||
columnDef.append(dbType == DatabaseType.SQLITE ? " PRIMARY KEY" : " PRIMARY KEY AUTO_INCREMENT");
|
||||
}
|
||||
|
||||
columnDefinitions.add(columnDef.toString());
|
||||
}
|
||||
|
||||
for (int i = 0; i < columnDefinitions.size(); i++) {
|
||||
sqlBuilder.append(columnDefinitions.get(i));
|
||||
if (i != columnDefinitions.size() - 1) {
|
||||
sqlBuilder.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
if (dbType == DatabaseType.MYSQL) {
|
||||
sqlBuilder.append(") ").append(MYSQL_TABLE_ENGINE);
|
||||
} else {
|
||||
sqlBuilder.append(")");
|
||||
}
|
||||
|
||||
return sqlBuilder.toString();
|
||||
}
|
||||
|
||||
private static String mapJavaTypeToDbType(DatabaseType dbType, Class<?> javaType) {
|
||||
return dbType == DatabaseType.SQLITE ? mapJavaTypeToSqlite(javaType) : mapJavaTypeToMysql(javaType);
|
||||
}
|
||||
|
||||
private static String mapJavaTypeToSqlite(Class<?> javaType) {
|
||||
if (javaType == int.class || javaType == Integer.class || javaType == long.class || javaType == Long.class || javaType == short.class || javaType == Short.class) {
|
||||
return "INTEGER";
|
||||
} else if (javaType == float.class || javaType == Float.class || javaType == double.class || javaType == Double.class) {
|
||||
return "REAL";
|
||||
} else if (javaType == boolean.class || javaType == Boolean.class) {
|
||||
return "INTEGER";
|
||||
} else if (javaType == String.class) {
|
||||
return "TEXT";
|
||||
} else if (javaType == byte[].class) {
|
||||
return "BLOB";
|
||||
} else {
|
||||
return "TEXT";
|
||||
}
|
||||
}
|
||||
|
||||
private static Path getRecommendedCacheDirectory() {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
if (os.contains("win")) {
|
||||
String appData = System.getenv("APPDATA");
|
||||
if (appData != null && !appData.isEmpty()) {
|
||||
Path path = Paths.get(appData);
|
||||
if (Files.exists(path) && Files.isWritable(path)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
} else if (os.contains("nix") || os.contains("nux")) {
|
||||
String userHome = System.getProperty("user.home");
|
||||
if (userHome != null && !userHome.isEmpty()) {
|
||||
Path path = Paths.get(userHome).resolve(".cache");
|
||||
try {
|
||||
if (!Files.exists(path)) {
|
||||
Files.createDirectories(path);
|
||||
}
|
||||
if (Files.isWritable(path)) {
|
||||
return path;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("无法访问Linux .cache目录: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String mapJavaTypeToMysql(Class<?> javaType) {
|
||||
if (javaType == int.class || javaType == Integer.class) {
|
||||
return "INT";
|
||||
} else if (javaType == long.class || javaType == Long.class) {
|
||||
return "BIGINT";
|
||||
} else if (javaType == float.class || javaType == Float.class) {
|
||||
return "FLOAT";
|
||||
} else if (javaType == double.class || javaType == Double.class) {
|
||||
return "DOUBLE";
|
||||
} else if (javaType == boolean.class || javaType == Boolean.class) {
|
||||
return "TINYINT(1)";
|
||||
} else if (javaType == String.class) {
|
||||
return "VARCHAR(500)";
|
||||
} else if (javaType == java.util.Date.class || javaType == java.sql.Date.class) {
|
||||
return "DATE";
|
||||
} else if (javaType == java.sql.Timestamp.class || javaType == java.time.LocalDateTime.class) {
|
||||
return "DATETIME";
|
||||
} else if (javaType == byte[].class) {
|
||||
return "BLOB";
|
||||
} else {
|
||||
return "VARCHAR(1000)";
|
||||
}
|
||||
}
|
||||
|
||||
private static String extractDbNameFromMysqlUrl(String url) {
|
||||
if (url == null) return null;
|
||||
String urlWithoutPrefix = url.replace("jdbc:mysql://", "");
|
||||
String urlWithoutParams = urlWithoutPrefix.split("\\?")[0];
|
||||
return urlWithoutParams.substring(urlWithoutParams.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
private static String getConfigValue(Map<String, Object> configMap, String key) {
|
||||
Object value = configMap.get(key);
|
||||
return value == null ? null : value.toString().trim();
|
||||
}
|
||||
|
||||
private static boolean isInitialized(DatabaseType dbType) {
|
||||
return dbType == DatabaseType.SQLITE ? isSqliteInitialized : isMysqlInitialized;
|
||||
}
|
||||
|
||||
private static void markInitialized(DatabaseType dbType, boolean status) {
|
||||
if (dbType == DatabaseType.SQLITE) {
|
||||
isSqliteInitialized = status;
|
||||
} else {
|
||||
isMysqlInitialized = status;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getInitSuccessMsg(DatabaseType dbType) {
|
||||
return dbType == DatabaseType.SQLITE ?
|
||||
"文件路径: " + sqliteDbFilePath :
|
||||
"数据库: " + extractDbNameFromMysqlUrl(mysqlUrl) + "、用户: " + mysqlUsername;
|
||||
}
|
||||
|
||||
public static String getActiveDataSource() throws IOException {
|
||||
// 读取配置文件
|
||||
try (InputStream yamlInput = new ClassPathResource("application.yml").getInputStream()) {
|
||||
Yaml yaml = new Yaml();
|
||||
Map<String, Object> yamlData = yaml.load(yamlInput);
|
||||
if (yamlData.containsKey("spring")) {
|
||||
Object springObj = yamlData.get("spring");
|
||||
if (springObj instanceof Map) {
|
||||
Map<String, Object> springMap = (Map<String, Object>) springObj;
|
||||
if (springMap.containsKey("datasource")) {
|
||||
Object datasourceObj = springMap.get("datasource");
|
||||
if (datasourceObj instanceof Map) {
|
||||
Map<String, Object> datasourceMap = (Map<String, Object>) datasourceObj;
|
||||
if (datasourceMap.containsKey("active")) {
|
||||
Object activeObj = datasourceMap.get("active");
|
||||
if (activeObj != null) {
|
||||
return activeObj.toString().trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("读取配置文件出错");
|
||||
}
|
||||
return "sqlite";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.yj.earth.datasource;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@Configuration
|
||||
public class JdbcTemplateConfig {
|
||||
/**
|
||||
* 配置JdbcTemplate
|
||||
*/
|
||||
@Bean
|
||||
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
|
||||
return new JdbcTemplate(dataSource);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.yj.earth.datasource;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* MySQL 数据源配置类
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(name = "spring.datasource.active", havingValue = "mysql")
|
||||
public class MysqlDataSourceConfig {
|
||||
|
||||
/**
|
||||
* 配置 MySQL 数据源
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource.mysql")
|
||||
public DataSource dataSource() {
|
||||
DruidDataSource dataSource = new DruidDataSource();
|
||||
dataSource.setInitialSize(5);
|
||||
dataSource.setMaxActive(20);
|
||||
dataSource.setMinIdle(5);
|
||||
dataSource.setMaxWait(60000);
|
||||
dataSource.setTestWhileIdle(true);
|
||||
dataSource.setValidationQuery("SELECT 1");
|
||||
dataSource.setTestOnBorrow(false);
|
||||
dataSource.setTestOnReturn(false);
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.yj.earth.datasource;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* SQLite 数据源配置类
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(name = "spring.datasource.active", havingValue = "sqlite", matchIfMissing = true)
|
||||
public class SqliteDataSourceConfig {
|
||||
|
||||
/**
|
||||
* 配置 SQLite 数据源
|
||||
*/
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
String dbPath = DatabaseManager.getSqliteDbFilePath();
|
||||
if (dbPath == null) {
|
||||
throw new RuntimeException("获取SQLite数据库文件路径失败");
|
||||
}
|
||||
DruidDataSource dataSource = new DruidDataSource();
|
||||
dataSource.setDriverClassName("org.sqlite.JDBC");
|
||||
dataSource.setUrl("jdbc:sqlite:" + dbPath);
|
||||
dataSource.setInitialSize(5);
|
||||
dataSource.setMaxActive(20);
|
||||
dataSource.setMinIdle(5);
|
||||
dataSource.setMaxWait(60000);
|
||||
dataSource.setTestWhileIdle(true);
|
||||
dataSource.setValidationQuery("SELECT 1");
|
||||
dataSource.setTestOnBorrow(false);
|
||||
dataSource.setTestOnReturn(false);
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
38
src/main/java/com/yj/earth/design/FileInfo.java
Normal file
38
src/main/java/com/yj/earth/design/FileInfo.java
Normal file
@ -0,0 +1,38 @@
|
||||
package com.yj.earth.design;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
|
||||
@Data
|
||||
public class FileInfo {
|
||||
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "文件名")
|
||||
private String fileName;
|
||||
|
||||
@Schema(description = "文件后缀")
|
||||
private String fileSuffix;
|
||||
|
||||
@Schema(description = "内容类型")
|
||||
private String contentType;
|
||||
|
||||
@Schema(description = "文件大小")
|
||||
private Long fileSize;
|
||||
|
||||
@Schema(description = "文件路径")
|
||||
private String filePath;
|
||||
|
||||
@Schema(description = "文件MD5")
|
||||
private String fileMd5;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
28
src/main/java/com/yj/earth/design/Role.java
Normal file
28
src/main/java/com/yj/earth/design/Role.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.yj.earth.design;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class Role {
|
||||
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "角色名称")
|
||||
private String roleName;
|
||||
|
||||
@Schema(description = "角色描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "是否超级管理员")
|
||||
private Integer isSuper;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
25
src/main/java/com/yj/earth/design/RoleSource.java
Normal file
25
src/main/java/com/yj/earth/design/RoleSource.java
Normal file
@ -0,0 +1,25 @@
|
||||
package com.yj.earth.design;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class RoleSource {
|
||||
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "角色ID")
|
||||
private String roleId;
|
||||
|
||||
@Schema(description = "资源ID")
|
||||
private String sourceId;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
43
src/main/java/com/yj/earth/design/Source.java
Normal file
43
src/main/java/com/yj/earth/design/Source.java
Normal file
@ -0,0 +1,43 @@
|
||||
package com.yj.earth.design;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class Source {
|
||||
|
||||
@Schema (description = "主键")
|
||||
private String id;
|
||||
|
||||
@Schema (description = "资源名称")
|
||||
private String sourceName;
|
||||
|
||||
@Schema (description = "资源类型")
|
||||
private String sourceType;
|
||||
|
||||
@Schema (description = "资源路径")
|
||||
private String sourcePath;
|
||||
|
||||
@Schema (description = "父级ID")
|
||||
private String parentId;
|
||||
|
||||
@Schema (description = "树状索引")
|
||||
private Integer treeIndex;
|
||||
|
||||
@Schema (description = "是否显示")
|
||||
private Integer isShow;
|
||||
|
||||
@Schema (description = "其他内容")
|
||||
private String detail;
|
||||
|
||||
@Schema (description = "前端参数")
|
||||
private String params;
|
||||
|
||||
@Schema (description = "创建时间")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema (description = "更新时间")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
38
src/main/java/com/yj/earth/design/User.java
Normal file
38
src/main/java/com/yj/earth/design/User.java
Normal file
@ -0,0 +1,38 @@
|
||||
package com.yj.earth.design;
|
||||
|
||||
import com.yj.earth.annotation.ExcludeField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class User{
|
||||
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "昵称")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "所属角色")
|
||||
private String roleId;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.yj.earth.dto.relation;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RoleBindOrUnBindSourceDto {
|
||||
@Schema(description = "角色ID")
|
||||
private String roleId;
|
||||
@Schema(description = "资源ID列表")
|
||||
private List<String> sourceIdList;
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.yj.earth.dto.relation;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class SourceBindOrUnBindRoleDto {
|
||||
@Schema(description = "角色ID列表")
|
||||
private List<String> roleIdList;
|
||||
@Schema(description = "资源ID")
|
||||
private String sourceId;
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.yj.earth.dto.relation;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class UserBindOrUnBindRoleDto {
|
||||
@Schema(description = "角色ID")
|
||||
private String roleId;
|
||||
@Schema(description = "用户ID")
|
||||
private String userId;
|
||||
}
|
||||
16
src/main/java/com/yj/earth/dto/role/AddRoleDto.java
Normal file
16
src/main/java/com/yj/earth/dto/role/AddRoleDto.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.yj.earth.dto.role;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AddRoleDto {
|
||||
@Schema(description = "角色名称")
|
||||
private String roleName;
|
||||
|
||||
@Schema(description = "角色描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "是否超级管理员")
|
||||
private Integer isSuper;
|
||||
}
|
||||
16
src/main/java/com/yj/earth/dto/role/UpdateRoleDto.java
Normal file
16
src/main/java/com/yj/earth/dto/role/UpdateRoleDto.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.yj.earth.dto.role;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateRoleDto {
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "角色描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "是否超级管理员")
|
||||
private Integer isSuper;
|
||||
}
|
||||
17
src/main/java/com/yj/earth/dto/source/AddDirectoryDto.java
Normal file
17
src/main/java/com/yj/earth/dto/source/AddDirectoryDto.java
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
package com.yj.earth.dto.source;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AddDirectoryDto {
|
||||
@Schema (description = "资源ID")
|
||||
private String id;
|
||||
@Schema(description = "资源名称")
|
||||
private String sourceName;
|
||||
@Schema (description = "父级ID")
|
||||
private String parentId;
|
||||
@Schema (description = "树状索引")
|
||||
private Integer treeIndex;
|
||||
}
|
||||
18
src/main/java/com/yj/earth/dto/source/AddModelSourceDto.java
Normal file
18
src/main/java/com/yj/earth/dto/source/AddModelSourceDto.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.yj.earth.dto.source;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AddModelSourceDto {
|
||||
@Schema (description = "资源ID")
|
||||
private String id;
|
||||
@Schema(description = "资源路径")
|
||||
private String sourcePath;
|
||||
@Schema(description = "父节点ID")
|
||||
private String parentId;
|
||||
@Schema(description = "树状索引")
|
||||
private Integer treeIndex;
|
||||
@Schema(description = "前端参数")
|
||||
private String params;
|
||||
}
|
||||
22
src/main/java/com/yj/earth/dto/source/AddOtherSourceDto.java
Normal file
22
src/main/java/com/yj/earth/dto/source/AddOtherSourceDto.java
Normal file
@ -0,0 +1,22 @@
|
||||
package com.yj.earth.dto.source;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class AddOtherSourceDto {
|
||||
@Schema (description = "资源ID")
|
||||
private String id;
|
||||
@Schema(description = "资源名称")
|
||||
private String sourceName;
|
||||
@Schema(description = "资源类型")
|
||||
private String sourceType;
|
||||
@Schema(description = "父级ID")
|
||||
private String parentId;
|
||||
@Schema(description = "树形索引")
|
||||
private Integer treeIndex;
|
||||
@Schema(description = "前端参数")
|
||||
private Map<String, Object> params;
|
||||
}
|
||||
16
src/main/java/com/yj/earth/dto/source/DragSourceDto.java
Normal file
16
src/main/java/com/yj/earth/dto/source/DragSourceDto.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.yj.earth.dto.source;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DragSourceDto {
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "父级ID")
|
||||
private String parentId;
|
||||
|
||||
@Schema(description = "树形索引")
|
||||
private Integer treeIndex;
|
||||
}
|
||||
27
src/main/java/com/yj/earth/dto/source/UpdateSourceDto.java
Normal file
27
src/main/java/com/yj/earth/dto/source/UpdateSourceDto.java
Normal file
@ -0,0 +1,27 @@
|
||||
package com.yj.earth.dto.source;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class UpdateSourceDto {
|
||||
@Schema(description = "主键ID")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "资源名称")
|
||||
private String sourceName;
|
||||
|
||||
@Schema(description = "上级ID")
|
||||
private String parentId;
|
||||
|
||||
@Schema(description = "树形索引")
|
||||
private Integer treeIndex;
|
||||
|
||||
@Schema(description = "是否显示")
|
||||
private Integer isShow;
|
||||
|
||||
@Schema(description = "资源参数")
|
||||
private Map<String, Object> params;
|
||||
}
|
||||
26
src/main/java/com/yj/earth/dto/user/AddUserDto.java
Normal file
26
src/main/java/com/yj/earth/dto/user/AddUserDto.java
Normal file
@ -0,0 +1,26 @@
|
||||
package com.yj.earth.dto.user;
|
||||
|
||||
import com.yj.earth.annotation.ExcludeField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AddUserDto {
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "昵称")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "所属角色")
|
||||
private String roleId;
|
||||
}
|
||||
14
src/main/java/com/yj/earth/dto/user/UpdatePasswordDto.java
Normal file
14
src/main/java/com/yj/earth/dto/user/UpdatePasswordDto.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.yj.earth.dto.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdatePasswordDto {
|
||||
@Schema(description = "用户ID")
|
||||
private String id;
|
||||
@Schema(description = "旧密码")
|
||||
private String oldPassword;
|
||||
@Schema(description = "新密码")
|
||||
private String newPassword;
|
||||
}
|
||||
20
src/main/java/com/yj/earth/dto/user/UpdateUserDto.java
Normal file
20
src/main/java/com/yj/earth/dto/user/UpdateUserDto.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.yj.earth.dto.user;
|
||||
|
||||
import com.yj.earth.annotation.ExcludeField;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UpdateUserDto {
|
||||
@Schema(description = "主键")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "昵称")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String phone;
|
||||
}
|
||||
12
src/main/java/com/yj/earth/dto/user/UserLoginDto.java
Normal file
12
src/main/java/com/yj/earth/dto/user/UserLoginDto.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.yj.earth.dto.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserLoginDto {
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
}
|
||||
14
src/main/java/com/yj/earth/model/Point.java
Normal file
14
src/main/java/com/yj/earth/model/Point.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.yj.earth.model;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Point {
|
||||
private double lat; // 纬度
|
||||
private double lng; // 经度
|
||||
}
|
||||
22
src/main/java/com/yj/earth/model/RouteRequest.java
Normal file
22
src/main/java/com/yj/earth/model/RouteRequest.java
Normal file
@ -0,0 +1,22 @@
|
||||
package com.yj.earth.model;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RouteRequest {
|
||||
@Schema(description = "起点纬度")
|
||||
private Double startLat;
|
||||
@Schema(description = "起点经度")
|
||||
private Double startLng;
|
||||
@Schema(description = "终点纬度")
|
||||
private Double endLat;
|
||||
@Schema(description = "终点经度")
|
||||
private Double endLng;
|
||||
@Schema(description = "交通方式")
|
||||
private String profile;
|
||||
@Schema(description = "途经点")
|
||||
private List<Point> waypoints;
|
||||
}
|
||||
14
src/main/java/com/yj/earth/model/RouteResponse.java
Normal file
14
src/main/java/com/yj/earth/model/RouteResponse.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.yj.earth.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class RouteResponse {
|
||||
private Double distanceKm; // 距离(公里)
|
||||
private Double timeMinutes; // 时间(分钟)
|
||||
private List<Point> pathPoints; // 路径点列表
|
||||
}
|
||||
12
src/main/java/com/yj/earth/model/StatusResponse.java
Normal file
12
src/main/java/com/yj/earth/model/StatusResponse.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.yj.earth.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class StatusResponse {
|
||||
private boolean loading; // 是否正在加载
|
||||
private boolean loaded; // 是否已加载完成
|
||||
private String message; // 状态消息
|
||||
}
|
||||
222
src/main/java/com/yj/earth/params/BillboardObject.java
Normal file
222
src/main/java/com/yj/earth/params/BillboardObject.java
Normal file
@ -0,0 +1,222 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "点标注对象")
|
||||
@SourceType("point")
|
||||
public class BillboardObject {
|
||||
@Schema(description = "唯一标识")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "标注整体的显隐", defaultValue = "true")
|
||||
private boolean show = true;
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "位置(必填)")
|
||||
private Position position = new Position();
|
||||
|
||||
@Schema(description = "高度模式(0:海拔高度;1:相对地表;2:依附地表; 3:依附模型)", defaultValue = "3")
|
||||
private int heightMode = 3;
|
||||
|
||||
@Schema(description = "是否开启跟随视野缩放", defaultValue = "true")
|
||||
private boolean scaleByDistance = true;
|
||||
|
||||
@Schema(description = "视野缩放最近距离", defaultValue = "2000")
|
||||
private int near = 2000;
|
||||
|
||||
@Schema(description = "视野缩放最远距离", defaultValue = "100000")
|
||||
private int far = 100000;
|
||||
|
||||
@Schema(description = "图标参数")
|
||||
private Billboard billboard = new Billboard();
|
||||
|
||||
@Schema(description = "文字参数")
|
||||
private Label label = new Label();
|
||||
|
||||
@Schema(description = "属性内容")
|
||||
private Attribute attribute = new Attribute();
|
||||
|
||||
@Schema(description = "富文本内容")
|
||||
private String richTextContent;
|
||||
|
||||
@Schema(description = "默认视角")
|
||||
private CustomView customView = new CustomView();
|
||||
|
||||
@Data
|
||||
@Schema(description = "位置属性")
|
||||
public static class Position {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "图标参数")
|
||||
public static class Billboard {
|
||||
@Schema(description = "图标显隐", defaultValue = "true")
|
||||
private boolean show = true;
|
||||
|
||||
@Schema(description = "图标路径")
|
||||
private String image;
|
||||
|
||||
@Schema(description = "默认图标的唯一标识")
|
||||
private String defaultImage;
|
||||
|
||||
@Schema(description = "图标放大倍数", defaultValue = "3")
|
||||
private int scale = 3;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "文字参数")
|
||||
public static class Label {
|
||||
@Schema(description = "文字内容")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "文字显隐", defaultValue = "true")
|
||||
private boolean show = true;
|
||||
|
||||
@Schema(description = "文字字体项(0:黑体;1:思源黑体;2:庞门正道标题体;3:数黑体)", defaultValue = "0")
|
||||
private int fontFamily = 0;
|
||||
|
||||
@Schema(description = "文字大小、单位px", defaultValue = "39")
|
||||
private int fontSize = 39;
|
||||
|
||||
@Schema(description = "文字颜色", defaultValue = "#00ffff")
|
||||
private String color = "#00ffff";
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "属性内容")
|
||||
public static class Attribute {
|
||||
@Schema(description = "链接")
|
||||
private Link link = new Link();
|
||||
|
||||
@Schema(description = "全景图")
|
||||
private Vr vr = new Vr();
|
||||
|
||||
@Schema(description = "摄像头")
|
||||
private Camera camera = new Camera();
|
||||
|
||||
@Schema(description = "ISC")
|
||||
private Isc isc = new Isc();
|
||||
|
||||
@Schema(description = "物资")
|
||||
private Goods goods = new Goods();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接属性")
|
||||
public static class Link {
|
||||
@Schema(description = "链接内容列表")
|
||||
private List<LinkContent> content = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接内容")
|
||||
public static class LinkContent {
|
||||
@Schema(description = "链接名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "链接地址")
|
||||
private String url;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "全景图属性")
|
||||
public static class Vr {
|
||||
@Schema(description = "全景图内容列表")
|
||||
private List<VrContent> content = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "全景图内容")
|
||||
public static class VrContent {
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "地址")
|
||||
private String url;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "摄像头属性")
|
||||
public static class Camera {
|
||||
@Schema(description = "摄像头内容列表")
|
||||
private List<Object> content = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "ISC属性")
|
||||
public static class Isc {
|
||||
@Schema(description = "ISC内容列表")
|
||||
private List<Object> content = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "物资属性")
|
||||
public static class Goods {
|
||||
@Schema(description = "物资内容列表")
|
||||
private List<GoodsContent> content = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "物资内容")
|
||||
public static class GoodsContent {
|
||||
@Schema(description = "id")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "数量")
|
||||
private String cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "默认视角属性")
|
||||
public static class CustomView {
|
||||
@Schema(description = "默认视角方位")
|
||||
private Orientation orientation = new Orientation();
|
||||
|
||||
@Schema(description = "视角相对位置")
|
||||
private RelativePosition relativePosition = new RelativePosition();
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角方位属性")
|
||||
public static class Orientation {
|
||||
@Schema(description = "航向角")
|
||||
private double heading;
|
||||
|
||||
@Schema(description = "俯仰角")
|
||||
private double pitch;
|
||||
|
||||
@Schema(description = "翻滚角")
|
||||
private double roll;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角相对位置属性")
|
||||
public static class RelativePosition {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
70
src/main/java/com/yj/earth/params/Circle.java
Normal file
70
src/main/java/com/yj/earth/params/Circle.java
Normal file
@ -0,0 +1,70 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@SourceType("circle")
|
||||
public class Circle {
|
||||
private String id;
|
||||
private String name;
|
||||
private Center center;
|
||||
private int radius;
|
||||
private Map<String, Object> customView;
|
||||
private boolean show;
|
||||
private String color;
|
||||
private int heightMode;
|
||||
private Line line;
|
||||
private Label label;
|
||||
private Attribute attribute;
|
||||
private String richTextContent;
|
||||
|
||||
@Data
|
||||
public static class Center {
|
||||
private double lng;
|
||||
private double lat;
|
||||
private double alt;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Line {
|
||||
private int width;
|
||||
private String color;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Label {
|
||||
private String text;
|
||||
private boolean show;
|
||||
private Position position;
|
||||
private int fontSize;
|
||||
private int fontFamily;
|
||||
private String color;
|
||||
private int lineWidth;
|
||||
private int pixelOffset;
|
||||
private List<String> backgroundColor;
|
||||
private String lineColor;
|
||||
private boolean scaleByDistance;
|
||||
private int near;
|
||||
private int far;
|
||||
|
||||
@Data
|
||||
public static class Position {
|
||||
private double lng;
|
||||
private double lat;
|
||||
private double alt;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Attribute {
|
||||
private Link link;
|
||||
|
||||
@Data
|
||||
public static class Link {
|
||||
private List<Object> content;
|
||||
}
|
||||
}
|
||||
}
|
||||
192
src/main/java/com/yj/earth/params/CurvelineObject.java
Normal file
192
src/main/java/com/yj/earth/params/CurvelineObject.java
Normal file
@ -0,0 +1,192 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "曲线对象")
|
||||
@SourceType("curve")
|
||||
public class CurvelineObject {
|
||||
@Schema(description = "唯一标识")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "首尾相反", defaultValue = "false")
|
||||
private boolean rotate = false;
|
||||
|
||||
@Schema(description = "间距", defaultValue = "1")
|
||||
|
||||
private int space = 1;
|
||||
@Schema(description = "速度", defaultValue = "10")
|
||||
|
||||
private String speed = "10";
|
||||
@Schema(description = "空间单位名称", defaultValue = "0")
|
||||
private String wordsName;
|
||||
|
||||
@Schema(description = "长度单位", defaultValue = "0")
|
||||
private String lengthUnit;
|
||||
|
||||
@Schema(description = "线宽", defaultValue = "3")
|
||||
private double width = 3;
|
||||
|
||||
@Schema(description = "颜色", defaultValue = "#ff0000")
|
||||
private String color = "#ff0000";
|
||||
|
||||
@Schema(description = "材质类型 0-实线 1-虚线 2-泛光...", defaultValue = "0")
|
||||
private int type = 0;
|
||||
|
||||
@Schema(description = "高度模式(0:海拔高度;1:相对高度;2:依附模式)", defaultValue = "2")
|
||||
private int heightMode = 2;
|
||||
|
||||
@Schema(description = "首尾相连", defaultValue = "false")
|
||||
private boolean noseToTail = false;
|
||||
|
||||
@Schema(description = "线缓冲", defaultValue = "false")
|
||||
private boolean extend = false;
|
||||
|
||||
@Schema(description = "线缓冲宽度", defaultValue = "10")
|
||||
private double extendWidth = 10;
|
||||
|
||||
@Schema(description = "线缓冲颜色", defaultValue = "rgba(255,255,80,0.3)")
|
||||
private String extendColor = "rgba(255,255,80,0.3)";
|
||||
|
||||
@Schema(description = "显隐", defaultValue = "true")
|
||||
private boolean show = true;
|
||||
|
||||
@Schema(description = "经纬度和高度的列表(必填)")
|
||||
private List<Position> positions = new ArrayList<>();
|
||||
|
||||
@Schema(description = "标签对象")
|
||||
private Label label = new Label();
|
||||
|
||||
@Schema(description = "属性内容")
|
||||
private Attribute attribute = new Attribute();
|
||||
|
||||
@Schema(description = "富文本内容")
|
||||
private String richTextContent;
|
||||
|
||||
@Schema(description = "默认视角")
|
||||
private CustomView customView = new CustomView();
|
||||
|
||||
@Data
|
||||
@Schema(description = "位置属性")
|
||||
public static class Position {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "标签参数")
|
||||
public static class Label {
|
||||
@Schema(description = "标签文本")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "标签显隐")
|
||||
private Boolean show;
|
||||
|
||||
@Schema(description = "标签位置")
|
||||
private Position position = new Position();
|
||||
|
||||
@Schema(description = "字体大小", defaultValue = "20")
|
||||
private int fontSize = 20;
|
||||
|
||||
@Schema(description = "字体项 0:黑体;1:思源黑体;2:庞门正道标题体;3:数黑体", defaultValue = "0")
|
||||
private int fontFamily = 0;
|
||||
|
||||
@Schema(description = "字体颜色", defaultValue = "#ffffff")
|
||||
private String color = "#ffffff";
|
||||
|
||||
@Schema(description = "引线宽", defaultValue = "4")
|
||||
private double lineWidth = 4;
|
||||
|
||||
@Schema(description = "引线颜色", defaultValue = "#00ffff80")
|
||||
private String lineColor = "#00ffff80";
|
||||
|
||||
@Schema(description = "字体偏移(引线长度)", defaultValue = "20")
|
||||
private double pixelOffset = 20;
|
||||
|
||||
@Schema(description = "背景颜色", defaultValue = "['#00ffff80', '#00ffff80']")
|
||||
private String[] backgroundColor = {"#00ffff80", "#00ffff80"};
|
||||
|
||||
@Schema(description = "距离缩放")
|
||||
private Boolean scaleByDistance;
|
||||
|
||||
@Schema(description = "视野缩放最近距离", defaultValue = "2000")
|
||||
private int near = 2000;
|
||||
|
||||
@Schema(description = "视野缩放最远距离", defaultValue = "100000")
|
||||
private int far = 100000;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "属性内容")
|
||||
public static class Attribute {
|
||||
@Schema(description = "链接")
|
||||
private Link link = new Link();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接属性")
|
||||
public static class Link {
|
||||
@Schema(description = "链接内容列表", defaultValue = "[]")
|
||||
private List<LinkContent> content = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接内容")
|
||||
public static class LinkContent {
|
||||
@Schema(description = "链接名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "链接地址")
|
||||
private String url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "默认视角属性")
|
||||
public static class CustomView {
|
||||
@Schema(description = "默认视角方位")
|
||||
private Orientation orientation = new Orientation();
|
||||
|
||||
@Schema(description = "视角相对位置")
|
||||
private RelativePosition relativePosition = new RelativePosition();
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角方位属性")
|
||||
public static class Orientation {
|
||||
@Schema(description = "航向角")
|
||||
private double heading;
|
||||
|
||||
@Schema(description = "俯仰角")
|
||||
private double pitch;
|
||||
|
||||
@Schema(description = "翻滚角")
|
||||
private double roll;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角相对位置属性")
|
||||
public static class RelativePosition {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/main/java/com/yj/earth/params/GroundText.java
Normal file
26
src/main/java/com/yj/earth/params/GroundText.java
Normal file
@ -0,0 +1,26 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@SourceType("groundText")
|
||||
public class GroundText {
|
||||
private String id;
|
||||
private Map<String, Object> customView;
|
||||
private boolean show;
|
||||
private String text;
|
||||
private double angle;
|
||||
private int scale;
|
||||
private int speed;
|
||||
private String color;
|
||||
private Position position;
|
||||
|
||||
@Data
|
||||
public static class Position {
|
||||
private double lng;
|
||||
private double lat;
|
||||
}
|
||||
}
|
||||
11
src/main/java/com/yj/earth/params/Layer.java
Normal file
11
src/main/java/com/yj/earth/params/Layer.java
Normal file
@ -0,0 +1,11 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@SourceType("layer")
|
||||
public class Layer {
|
||||
private Integer alpha;
|
||||
private Integer brightness;
|
||||
}
|
||||
187
src/main/java/com/yj/earth/params/PolygonAttackArrowObject.java
Normal file
187
src/main/java/com/yj/earth/params/PolygonAttackArrowObject.java
Normal file
@ -0,0 +1,187 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "箭头对象")
|
||||
@SourceType("attackArrow")
|
||||
public class PolygonAttackArrowObject {
|
||||
@Schema(description = "唯一标识")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "显示/隐藏", defaultValue = "true")
|
||||
private boolean show = true;
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "颜色", defaultValue = "rgba(255, 0, 0, 0.5)")
|
||||
private String color = "rgba(255, 0, 0, 0.5)";
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double height;
|
||||
|
||||
@Schema(description = "高度模式(0:海拔高度;1:相对地表;2:依附模式)", defaultValue = "2")
|
||||
private int heightMode = 2;
|
||||
|
||||
@Schema(description = "面积单位", defaultValue = "平方米")
|
||||
private String areaUnit = "平方米";
|
||||
|
||||
@Schema(description = "边框")
|
||||
private Line line = new Line();
|
||||
|
||||
@Schema(description = "经纬度和高度的列表(必填)")
|
||||
private List<Position> positions = new ArrayList<>();
|
||||
|
||||
@Schema(description = "动画", defaultValue = "false")
|
||||
private boolean spreadState = false;
|
||||
|
||||
@Schema(description = "动画重复", defaultValue = "false")
|
||||
private boolean loop = false;
|
||||
|
||||
@Schema(description = "动画持续时长(毫秒)", defaultValue = "3000")
|
||||
private int spreadTime = 3000;
|
||||
|
||||
@Schema(description = "标签对象")
|
||||
private Label label = new Label();
|
||||
|
||||
@Schema(description = "属性内容")
|
||||
private Attribute attribute = new Attribute();
|
||||
|
||||
@Schema(description = "富文本内容")
|
||||
private String richTextContent;
|
||||
|
||||
@Schema(description = "默认视角")
|
||||
private CustomView customView = new CustomView();
|
||||
|
||||
@Data
|
||||
@Schema(description = "边框属性")
|
||||
public static class Line {
|
||||
@Schema(description = "边框宽", defaultValue = "2")
|
||||
private double width = 2;
|
||||
|
||||
@Schema(description = "边框颜色", defaultValue = "rgba(155, 155, 124, 0.89)")
|
||||
private String color = "rgba(155, 155, 124, 0.89)";
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "位置属性")
|
||||
public static class Position {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "标签参数")
|
||||
public static class Label {
|
||||
@Schema(description = "标签文本")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "标签显隐")
|
||||
private Boolean show;
|
||||
|
||||
@Schema(description = "标签位置")
|
||||
private Position position = new Position();
|
||||
|
||||
@Schema(description = "字体大小", defaultValue = "20")
|
||||
private int fontSize = 20;
|
||||
|
||||
@Schema(description = "字体项 0:黑体;1:思源黑体;2:庞门正道标题体;3:数黑体", defaultValue = "0")
|
||||
private int fontFamily = 0;
|
||||
|
||||
@Schema(description = "字体颜色", defaultValue = "#ffffff")
|
||||
private String color = "#ffffff";
|
||||
|
||||
@Schema(description = "引线宽", defaultValue = "4")
|
||||
private double lineWidth = 4;
|
||||
|
||||
@Schema(description = "引线颜色", defaultValue = "#00ffff80")
|
||||
private String lineColor = "#00ffff80";
|
||||
|
||||
@Schema(description = "字体偏移(引线长度)", defaultValue = "20")
|
||||
private double pixelOffset = 20;
|
||||
|
||||
@Schema(description = "背景颜色", defaultValue = "['#00ffff80', '#00ffff80']")
|
||||
private String[] backgroundColor = {"#00ffff80", "#00ffff80"};
|
||||
|
||||
@Schema(description = "距离缩放", defaultValue = "false")
|
||||
private boolean scaleByDistance = false;
|
||||
|
||||
@Schema(description = "视野缩放最近距离", defaultValue = "2000")
|
||||
private int near = 2000;
|
||||
|
||||
@Schema(description = "视野缩放最远距离", defaultValue = "100000")
|
||||
private int far = 100000;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "属性内容")
|
||||
public static class Attribute {
|
||||
@Schema(description = "链接", defaultValue = "{}")
|
||||
private Link link = new Link();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接属性")
|
||||
public static class Link {
|
||||
@Schema(description = "链接内容列表", defaultValue = "[]")
|
||||
private List<LinkContent> content = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接内容")
|
||||
public static class LinkContent {
|
||||
@Schema(description = "链接名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "链接地址")
|
||||
private String url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "默认视角属性")
|
||||
public static class CustomView {
|
||||
@Schema(description = "默认视角方位")
|
||||
private Orientation orientation = new Orientation();
|
||||
|
||||
@Schema(description = "视角相对位置")
|
||||
private RelativePosition relativePosition = new RelativePosition();
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角方位属性")
|
||||
public static class Orientation {
|
||||
@Schema(description = "航向角")
|
||||
private double heading;
|
||||
|
||||
@Schema(description = "俯仰角")
|
||||
private double pitch;
|
||||
|
||||
@Schema(description = "翻滚角")
|
||||
private double roll;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角相对位置属性")
|
||||
public static class RelativePosition {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
178
src/main/java/com/yj/earth/params/PolygonPanelObject.java
Normal file
178
src/main/java/com/yj/earth/params/PolygonPanelObject.java
Normal file
@ -0,0 +1,178 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "多边形对象")
|
||||
@SourceType("panel")
|
||||
public class PolygonPanelObject {
|
||||
@Schema(description = "唯一标识")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "显示/隐藏", defaultValue = "true")
|
||||
private boolean show = true;
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "颜色", defaultValue = "rgba(255, 0, 0, 0.5)")
|
||||
private String color = "rgba(255, 0, 0, 0.5)";
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double height;
|
||||
|
||||
@Schema(description = "高度模式(0:海拔高度;1:相对地表;2:依附模式)", defaultValue = "2")
|
||||
private int heightMode = 2;
|
||||
|
||||
@Schema(description = "面积单位", defaultValue = "平方米")
|
||||
private String areaUnit = "平方米";
|
||||
|
||||
@Schema(description = "边框")
|
||||
private Line line = new Line();
|
||||
|
||||
@Schema(description = "经纬度和高度的列表(必填)")
|
||||
private List<Position> positions = new ArrayList<>();
|
||||
|
||||
@Schema(description = "标签对象")
|
||||
private Label label = new Label();
|
||||
|
||||
@Schema(description = "属性内容")
|
||||
private Attribute attribute = new Attribute();
|
||||
|
||||
@Schema(description = "富文本内容")
|
||||
private String richTextContent;
|
||||
|
||||
@Schema(description = "默认视角")
|
||||
private CustomView customView = new CustomView();
|
||||
|
||||
@Data
|
||||
@Schema(description = "边框属性")
|
||||
public static class Line {
|
||||
@Schema(description = "边框宽", defaultValue = "2")
|
||||
private double width = 2;
|
||||
|
||||
@Schema(description = "边框颜色", defaultValue = "rgba(155, 155, 124, 0.89)")
|
||||
private String color = "rgba(155, 155, 124, 0.89)";
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "位置属性")
|
||||
public static class Position {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "标签参数")
|
||||
public static class Label {
|
||||
@Schema(description = "标签文本")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "标签显隐")
|
||||
private Boolean show;
|
||||
|
||||
@Schema(description = "标签位置")
|
||||
private Position position = new Position();
|
||||
|
||||
@Schema(description = "字体大小", defaultValue = "20")
|
||||
private int fontSize = 20;
|
||||
|
||||
@Schema(description = "字体项 0:黑体;1:思源黑体;2:庞门正道标题体;3:数黑体", defaultValue = "0")
|
||||
private int fontFamily = 0;
|
||||
|
||||
@Schema(description = "字体颜色", defaultValue = "#ffffff")
|
||||
private String color = "#ffffff";
|
||||
|
||||
@Schema(description = "引线宽", defaultValue = "4")
|
||||
private double lineWidth = 4;
|
||||
|
||||
@Schema(description = "引线颜色", defaultValue = "#00ffff80")
|
||||
private String lineColor = "#00ffff80";
|
||||
|
||||
@Schema(description = "字体偏移(引线长度)", defaultValue = "20")
|
||||
private double pixelOffset = 20;
|
||||
|
||||
@Schema(description = "背景颜色", defaultValue = "['#00ffff80', '#00ffff80']")
|
||||
private String[] backgroundColor = {"#00ffff80", "#00ffff80"};
|
||||
|
||||
@Schema(description = "距离缩放", defaultValue = "false")
|
||||
private boolean scaleByDistance = false;
|
||||
|
||||
@Schema(description = "视野缩放最近距离", defaultValue = "2000")
|
||||
private int near = 2000;
|
||||
|
||||
@Schema(description = "视野缩放最远距离", defaultValue = "100000")
|
||||
private int far = 100000;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "属性内容")
|
||||
public static class Attribute {
|
||||
@Schema(description = "链接", defaultValue = "{}")
|
||||
private Link link = new Link();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接属性")
|
||||
public static class Link {
|
||||
@Schema(description = "链接内容列表", defaultValue = "[]")
|
||||
private List<LinkContent> content = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接内容")
|
||||
public static class LinkContent {
|
||||
@Schema(description = "链接名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "链接地址")
|
||||
private String url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "默认视角属性")
|
||||
public static class CustomView {
|
||||
@Schema(description = "默认视角方位")
|
||||
private Orientation orientation = new Orientation();
|
||||
|
||||
@Schema(description = "视角相对位置")
|
||||
private RelativePosition relativePosition = new RelativePosition();
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角方位属性")
|
||||
public static class Orientation {
|
||||
@Schema(description = "航向角")
|
||||
private double heading;
|
||||
|
||||
@Schema(description = "俯仰角")
|
||||
private double pitch;
|
||||
|
||||
@Schema(description = "翻滚角")
|
||||
private double roll;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角相对位置属性")
|
||||
public static class RelativePosition {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
187
src/main/java/com/yj/earth/params/PolygonPincerArrowObject.java
Normal file
187
src/main/java/com/yj/earth/params/PolygonPincerArrowObject.java
Normal file
@ -0,0 +1,187 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "钳形箭头对象")
|
||||
@SourceType("pincerArrow")
|
||||
public class PolygonPincerArrowObject {
|
||||
@Schema(description = "唯一标识")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "显示/隐藏", defaultValue = "true")
|
||||
private boolean show = true;
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "颜色", defaultValue = "rgba(255, 0, 0, 0.5)")
|
||||
private String color = "rgba(255, 0, 0, 0.5)";
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double height;
|
||||
|
||||
@Schema(description = "高度模式(0:海拔高度;1:相对地表;2:依附模式)", defaultValue = "2")
|
||||
private int heightMode = 2;
|
||||
|
||||
@Schema(description = "面积单位", defaultValue = "平方米")
|
||||
private String areaUnit = "平方米";
|
||||
|
||||
@Schema(description = "边框")
|
||||
private Line line = new Line();
|
||||
|
||||
@Schema(description = "经纬度和高度的列表(必填)")
|
||||
private List<Position> positions = new ArrayList<>();
|
||||
|
||||
@Schema(description = "动画", defaultValue = "false")
|
||||
private boolean spreadState = false;
|
||||
|
||||
@Schema(description = "动画重复", defaultValue = "false")
|
||||
private boolean loop = false;
|
||||
|
||||
@Schema(description = "动画持续时长(毫秒)", defaultValue = "3000")
|
||||
private int spreadTime = 3000;
|
||||
|
||||
@Schema(description = "标签对象")
|
||||
private Label label = new Label();
|
||||
|
||||
@Schema(description = "属性内容")
|
||||
private Attribute attribute = new Attribute();
|
||||
|
||||
@Schema(description = "富文本内容")
|
||||
private String richTextContent;
|
||||
|
||||
@Schema(description = "默认视角")
|
||||
private CustomView customView = new CustomView();
|
||||
|
||||
@Data
|
||||
@Schema(description = "边框属性")
|
||||
public static class Line {
|
||||
@Schema(description = "边框宽", defaultValue = "2")
|
||||
private double width = 2;
|
||||
|
||||
@Schema(description = "边框颜色", defaultValue = "rgba(155, 155, 124, 0.89)")
|
||||
private String color = "rgba(155, 155, 124, 0.89)";
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "位置属性")
|
||||
public static class Position {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "标签参数")
|
||||
public static class Label {
|
||||
@Schema(description = "标签文本")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "标签显隐")
|
||||
private Boolean show;
|
||||
|
||||
@Schema(description = "标签位置")
|
||||
private Position position = new Position();
|
||||
|
||||
@Schema(description = "字体大小", defaultValue = "20")
|
||||
private int fontSize = 20;
|
||||
|
||||
@Schema(description = "字体项 0:黑体;1:思源黑体;2:庞门正道标题体;3:数黑体", defaultValue = "0")
|
||||
private int fontFamily = 0;
|
||||
|
||||
@Schema(description = "字体颜色", defaultValue = "#ffffff")
|
||||
private String color = "#ffffff";
|
||||
|
||||
@Schema(description = "引线宽", defaultValue = "4")
|
||||
private double lineWidth = 4;
|
||||
|
||||
@Schema(description = "引线颜色", defaultValue = "#00ffff80")
|
||||
private String lineColor = "#00ffff80";
|
||||
|
||||
@Schema(description = "字体偏移(引线长度)", defaultValue = "20")
|
||||
private double pixelOffset = 20;
|
||||
|
||||
@Schema(description = "背景颜色", defaultValue = "['#00ffff80', '#00ffff80']")
|
||||
private String[] backgroundColor = {"#00ffff80", "#00ffff80"};
|
||||
|
||||
@Schema(description = "距离缩放", defaultValue = "false")
|
||||
private boolean scaleByDistance = false;
|
||||
|
||||
@Schema(description = "视野缩放最近距离", defaultValue = "2000")
|
||||
private int near = 2000;
|
||||
|
||||
@Schema(description = "视野缩放最远距离", defaultValue = "100000")
|
||||
private int far = 100000;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "属性内容")
|
||||
public static class Attribute {
|
||||
@Schema(description = "链接", defaultValue = "{}")
|
||||
private Link link = new Link();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接属性")
|
||||
public static class Link {
|
||||
@Schema(description = "链接内容列表", defaultValue = "[]")
|
||||
private List<LinkContent> content = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接内容")
|
||||
public static class LinkContent {
|
||||
@Schema(description = "链接名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "链接地址")
|
||||
private String url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "默认视角属性")
|
||||
public static class CustomView {
|
||||
@Schema(description = "默认视角方位")
|
||||
private Orientation orientation = new Orientation();
|
||||
|
||||
@Schema(description = "视角相对位置")
|
||||
private RelativePosition relativePosition = new RelativePosition();
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角方位属性")
|
||||
public static class Orientation {
|
||||
@Schema(description = "航向角")
|
||||
private double heading;
|
||||
|
||||
@Schema(description = "俯仰角")
|
||||
private double pitch;
|
||||
|
||||
@Schema(description = "翻滚角")
|
||||
private double roll;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角相对位置属性")
|
||||
public static class RelativePosition {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
178
src/main/java/com/yj/earth/params/PolygonRectangleObject.java
Normal file
178
src/main/java/com/yj/earth/params/PolygonRectangleObject.java
Normal file
@ -0,0 +1,178 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "矩形对象")
|
||||
@SourceType("rectangle")
|
||||
public class PolygonRectangleObject {
|
||||
@Schema(description = "唯一标识")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "显示/隐藏", defaultValue = "true")
|
||||
private boolean show = true;
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "颜色", defaultValue = "rgba(255, 0, 0, 0.5)")
|
||||
private String color = "rgba(255, 0, 0, 0.5)";
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double height;
|
||||
|
||||
@Schema(description = "高度模式(0:海拔高度;1:相对地表;2:依附模式)", defaultValue = "2")
|
||||
private int heightMode = 2;
|
||||
|
||||
@Schema(description = "面积单位", defaultValue = "平方米")
|
||||
private String areaUnit = "平方米";
|
||||
|
||||
@Schema(description = "边框")
|
||||
private Line line = new Line();
|
||||
|
||||
@Schema(description = "经纬度和高度的列表(必填)")
|
||||
private List<Position> positions = new ArrayList<>();
|
||||
|
||||
@Schema(description = "标签对象")
|
||||
private Label label = new Label();
|
||||
|
||||
@Schema(description = "属性内容")
|
||||
private Attribute attribute = new Attribute();
|
||||
|
||||
@Schema(description = "富文本内容")
|
||||
private String richTextContent;
|
||||
|
||||
@Schema(description = "默认视角")
|
||||
private CustomView customView = new CustomView();
|
||||
|
||||
@Data
|
||||
@Schema(description = "边框属性")
|
||||
public static class Line {
|
||||
@Schema(description = "边框宽", defaultValue = "2")
|
||||
private double width = 2;
|
||||
|
||||
@Schema(description = "边框颜色", defaultValue = "rgba(155, 155, 124, 0.89)")
|
||||
private String color = "rgba(155, 155, 124, 0.89)";
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "位置属性")
|
||||
public static class Position {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "标签参数")
|
||||
public static class Label {
|
||||
@Schema(description = "标签文本")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "标签显隐")
|
||||
private Boolean show;
|
||||
|
||||
@Schema(description = "标签位置")
|
||||
private Position position = new Position();
|
||||
|
||||
@Schema(description = "字体大小", defaultValue = "20")
|
||||
private int fontSize = 20;
|
||||
|
||||
@Schema(description = "字体项 0:黑体;1:思源黑体;2:庞门正道标题体;3:数黑体", defaultValue = "0")
|
||||
private int fontFamily = 0;
|
||||
|
||||
@Schema(description = "字体颜色", defaultValue = "#ffffff")
|
||||
private String color = "#ffffff";
|
||||
|
||||
@Schema(description = "引线宽", defaultValue = "4")
|
||||
private double lineWidth = 4;
|
||||
|
||||
@Schema(description = "引线颜色", defaultValue = "#00ffff80")
|
||||
private String lineColor = "#00ffff80";
|
||||
|
||||
@Schema(description = "字体偏移(引线长度)", defaultValue = "20")
|
||||
private double pixelOffset = 20;
|
||||
|
||||
@Schema(description = "背景颜色", defaultValue = "['#00ffff80', '#00ffff80']")
|
||||
private String[] backgroundColor = {"#00ffff80", "#00ffff80"};
|
||||
|
||||
@Schema(description = "距离缩放", defaultValue = "false")
|
||||
private boolean scaleByDistance = false;
|
||||
|
||||
@Schema(description = "视野缩放最近距离", defaultValue = "2000")
|
||||
private int near = 2000;
|
||||
|
||||
@Schema(description = "视野缩放最远距离", defaultValue = "100000")
|
||||
private int far = 100000;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "属性内容")
|
||||
public static class Attribute {
|
||||
@Schema(description = "链接", defaultValue = "{}")
|
||||
private Link link = new Link();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接属性")
|
||||
public static class Link {
|
||||
@Schema(description = "链接内容列表", defaultValue = "[]")
|
||||
private List<LinkContent> content = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接内容")
|
||||
public static class LinkContent {
|
||||
@Schema(description = "链接名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "链接地址")
|
||||
private String url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "默认视角属性")
|
||||
public static class CustomView {
|
||||
@Schema(description = "默认视角方位")
|
||||
private Orientation orientation = new Orientation();
|
||||
|
||||
@Schema(description = "视角相对位置")
|
||||
private RelativePosition relativePosition = new RelativePosition();
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角方位属性")
|
||||
public static class Orientation {
|
||||
@Schema(description = "航向角")
|
||||
private double heading;
|
||||
|
||||
@Schema(description = "俯仰角")
|
||||
private double pitch;
|
||||
|
||||
@Schema(description = "翻滚角")
|
||||
private double roll;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角相对位置属性")
|
||||
public static class RelativePosition {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
178
src/main/java/com/yj/earth/params/PolygonRendezvousObject.java
Normal file
178
src/main/java/com/yj/earth/params/PolygonRendezvousObject.java
Normal file
@ -0,0 +1,178 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "集结地对象")
|
||||
@SourceType("rendezvous")
|
||||
public class PolygonRendezvousObject {
|
||||
@Schema(description = "唯一标识")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "显示/隐藏", defaultValue = "true")
|
||||
private boolean show = true;
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "颜色", defaultValue = "rgba(255, 0, 0, 0.5)")
|
||||
private String color = "rgba(255, 0, 0, 0.5)";
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double height;
|
||||
|
||||
@Schema(description = "高度模式(0:海拔高度;1:相对地表;2:依附模式)", defaultValue = "2")
|
||||
private int heightMode = 2;
|
||||
|
||||
@Schema(description = "面积单位", defaultValue = "平方米")
|
||||
private String areaUnit = "平方米";
|
||||
|
||||
@Schema(description = "边框")
|
||||
private Line line = new Line();
|
||||
|
||||
@Schema(description = "经纬度和高度的列表(必填)")
|
||||
private List<Position> positions = new ArrayList<>();
|
||||
|
||||
@Schema(description = "标签对象")
|
||||
private Label label = new Label();
|
||||
|
||||
@Schema(description = "属性内容")
|
||||
private Attribute attribute = new Attribute();
|
||||
|
||||
@Schema(description = "富文本内容")
|
||||
private String richTextContent;
|
||||
|
||||
@Schema(description = "默认视角")
|
||||
private CustomView customView = new CustomView();
|
||||
|
||||
@Data
|
||||
@Schema(description = "边框属性")
|
||||
public static class Line {
|
||||
@Schema(description = "边框宽", defaultValue = "2")
|
||||
private double width = 2;
|
||||
|
||||
@Schema(description = "边框颜色", defaultValue = "rgba(155, 155, 124, 0.89)")
|
||||
private String color = "rgba(155, 155, 124, 0.89)";
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "位置属性")
|
||||
public static class Position {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "标签参数")
|
||||
public static class Label {
|
||||
@Schema(description = "标签文本")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "标签显隐")
|
||||
private Boolean show;
|
||||
|
||||
@Schema(description = "标签位置")
|
||||
private Position position = new Position();
|
||||
|
||||
@Schema(description = "字体大小", defaultValue = "20")
|
||||
private int fontSize = 20;
|
||||
|
||||
@Schema(description = "字体项 0:黑体;1:思源黑体;2:庞门正道标题体;3:数黑体", defaultValue = "0")
|
||||
private int fontFamily = 0;
|
||||
|
||||
@Schema(description = "字体颜色", defaultValue = "#ffffff")
|
||||
private String color = "#ffffff";
|
||||
|
||||
@Schema(description = "引线宽", defaultValue = "4")
|
||||
private double lineWidth = 4;
|
||||
|
||||
@Schema(description = "引线颜色", defaultValue = "#00ffff80")
|
||||
private String lineColor = "#00ffff80";
|
||||
|
||||
@Schema(description = "字体偏移(引线长度)", defaultValue = "20")
|
||||
private double pixelOffset = 20;
|
||||
|
||||
@Schema(description = "背景颜色", defaultValue = "['#00ffff80', '#00ffff80']")
|
||||
private String[] backgroundColor = {"#00ffff80", "#00ffff80"};
|
||||
|
||||
@Schema(description = "距离缩放", defaultValue = "false")
|
||||
private boolean scaleByDistance = false;
|
||||
|
||||
@Schema(description = "视野缩放最近距离", defaultValue = "2000")
|
||||
private int near = 2000;
|
||||
|
||||
@Schema(description = "视野缩放最远距离", defaultValue = "100000")
|
||||
private int far = 100000;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "属性内容")
|
||||
public static class Attribute {
|
||||
@Schema(description = "链接", defaultValue = "{}")
|
||||
private Link link = new Link();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接属性")
|
||||
public static class Link {
|
||||
@Schema(description = "链接内容列表", defaultValue = "[]")
|
||||
private List<LinkContent> content = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接内容")
|
||||
public static class LinkContent {
|
||||
@Schema(description = "链接名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "链接地址")
|
||||
private String url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "默认视角属性")
|
||||
public static class CustomView {
|
||||
@Schema(description = "默认视角方位")
|
||||
private Orientation orientation = new Orientation();
|
||||
|
||||
@Schema(description = "视角相对位置")
|
||||
private RelativePosition relativePosition = new RelativePosition();
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角方位属性")
|
||||
public static class Orientation {
|
||||
@Schema(description = "航向角")
|
||||
private double heading;
|
||||
|
||||
@Schema(description = "俯仰角")
|
||||
private double pitch;
|
||||
|
||||
@Schema(description = "翻滚角")
|
||||
private double roll;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角相对位置属性")
|
||||
public static class RelativePosition {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
194
src/main/java/com/yj/earth/params/PolylineObject.java
Normal file
194
src/main/java/com/yj/earth/params/PolylineObject.java
Normal file
@ -0,0 +1,194 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "线对象")
|
||||
@SourceType("line")
|
||||
public class PolylineObject {
|
||||
@Schema(description = "唯一标识")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "首尾相反", defaultValue = "false")
|
||||
private boolean rotate = false;
|
||||
|
||||
@Schema(description = "间距", defaultValue = "1")
|
||||
private int space = 1;
|
||||
@Schema(description = "速度", defaultValue = "10")
|
||||
|
||||
private String speed = "10";
|
||||
@Schema(description = "空间单位名称", defaultValue = "0")
|
||||
private String wordsName;
|
||||
|
||||
@Schema(description = "长度单位", defaultValue = "0")
|
||||
private String lengthUnit;
|
||||
|
||||
@Schema(description = "线宽", defaultValue = "3")
|
||||
private double width = 3;
|
||||
|
||||
@Schema(description = "颜色", defaultValue = "#ff0000")
|
||||
private String color = "#ff0000";
|
||||
|
||||
@Schema(description = "材质类型 0-实线 1-虚线 2-泛光...", defaultValue = "0")
|
||||
private int type = 0;
|
||||
|
||||
@Schema(description = "高度模式(0:海拔高度;1:相对高度;2:依附模式)", defaultValue = "2")
|
||||
private int heightMode = 2;
|
||||
|
||||
@Schema(description = "首尾相连", defaultValue = "false")
|
||||
private boolean noseToTail = false;
|
||||
|
||||
@Schema(description = "线段圆滑", defaultValue = "false")
|
||||
private boolean smooth = false;
|
||||
|
||||
@Schema(description = "线缓冲", defaultValue = "false")
|
||||
private boolean extend = false;
|
||||
|
||||
@Schema(description = "线缓冲宽度", defaultValue = "10")
|
||||
private double extendWidth = 10;
|
||||
|
||||
@Schema(description = "线缓冲颜色", defaultValue = "rgba(255,255,80,0.3)")
|
||||
private String extendColor = "rgba(255,255,80,0.3)";
|
||||
|
||||
@Schema(description = "显隐", defaultValue = "true")
|
||||
private boolean show = true;
|
||||
|
||||
@Schema(description = "经纬度和高度的列表(必填)")
|
||||
private List<Position> positions = new ArrayList<>();
|
||||
|
||||
@Schema(description = "标签对象")
|
||||
private Label label = new Label();
|
||||
|
||||
@Schema(description = "属性内容")
|
||||
private Attribute attribute = new Attribute();
|
||||
|
||||
@Schema(description = "富文本内容")
|
||||
private String richTextContent;
|
||||
|
||||
@Schema(description = "默认视角")
|
||||
private CustomView customView = new CustomView();
|
||||
|
||||
@Data
|
||||
@Schema(description = "位置属性")
|
||||
public static class Position {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "标签参数")
|
||||
public static class Label {
|
||||
@Schema(description = "标签文本")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "标签显隐")
|
||||
private Boolean show;
|
||||
|
||||
@Schema(description = "标签位置")
|
||||
private Position position = new Position();
|
||||
|
||||
@Schema(description = "字体大小", defaultValue = "20")
|
||||
private int fontSize = 20;
|
||||
|
||||
@Schema(description = "字体项 0:黑体;1:思源黑体;2:庞门正道标题体;3:数黑体", defaultValue = "0")
|
||||
private int fontFamily = 0;
|
||||
|
||||
@Schema(description = "字体颜色", defaultValue = "#ffffff")
|
||||
private String color = "#ffffff";
|
||||
|
||||
@Schema(description = "引线宽", defaultValue = "4")
|
||||
private double lineWidth = 4;
|
||||
|
||||
@Schema(description = "引线颜色", defaultValue = "#00ffff80")
|
||||
private String lineColor = "#00ffff80";
|
||||
|
||||
@Schema(description = "字体偏移(引线长度)", defaultValue = "20")
|
||||
private double pixelOffset = 20;
|
||||
|
||||
@Schema(description = "背景颜色", defaultValue = "['#00ffff80', '#00ffff80']")
|
||||
private String[] backgroundColor = {"#00ffff80", "#00ffff80"};
|
||||
|
||||
@Schema(description = "距离缩放")
|
||||
private Boolean scaleByDistance;
|
||||
|
||||
@Schema(description = "视野缩放最近距离", defaultValue = "2000")
|
||||
private int near = 2000;
|
||||
|
||||
@Schema(description = "视野缩放最远距离", defaultValue = "100000")
|
||||
private int far = 100000;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "属性内容")
|
||||
public static class Attribute {
|
||||
@Schema(description = "链接")
|
||||
private Link link = new Link();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接属性")
|
||||
public static class Link {
|
||||
@Schema(description = "链接内容列表", defaultValue = "[]")
|
||||
private List<LinkContent> content = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Schema(description = "链接内容")
|
||||
public static class LinkContent {
|
||||
@Schema(description = "链接名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "链接地址")
|
||||
private String url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "默认视角属性")
|
||||
public static class CustomView {
|
||||
@Schema(description = "默认视角方位")
|
||||
private Orientation orientation = new Orientation();
|
||||
|
||||
@Schema(description = "视角相对位置")
|
||||
private RelativePosition relativePosition = new RelativePosition();
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角方位属性")
|
||||
public static class Orientation {
|
||||
@Schema(description = "航向角")
|
||||
private double heading;
|
||||
|
||||
@Schema(description = "俯仰角")
|
||||
private double pitch;
|
||||
|
||||
@Schema(description = "翻滚角")
|
||||
private double roll;
|
||||
}
|
||||
|
||||
@Data
|
||||
@Schema(description = "视角相对位置属性")
|
||||
public static class RelativePosition {
|
||||
@Schema(description = "经度")
|
||||
private double lng;
|
||||
|
||||
@Schema(description = "纬度")
|
||||
private double lat;
|
||||
|
||||
@Schema(description = "高度")
|
||||
private double alt;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/main/java/com/yj/earth/params/StandText.java
Normal file
25
src/main/java/com/yj/earth/params/StandText.java
Normal file
@ -0,0 +1,25 @@
|
||||
package com.yj.earth.params;
|
||||
|
||||
import com.yj.earth.annotation.SourceType;
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@SourceType("standText")
|
||||
public class StandText {
|
||||
private String id;
|
||||
private List<Position> positions;
|
||||
private Map<String, Object> customView;
|
||||
private boolean show;
|
||||
private String text;
|
||||
private String color;
|
||||
private int speed;
|
||||
|
||||
@Data
|
||||
public static class Position {
|
||||
private double lng;
|
||||
private double lat;
|
||||
private double alt;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user