Merge pull request '完成前端与后端链接 登录API完成' (#1) from dev into master
Some checks failed
Gitea Actions Build / Build (push) Failing after 1m9s
Some checks failed
Gitea Actions Build / Build (push) Failing after 1m9s
Reviewed-on: #1
This commit is contained in:
commit
3a83354011
@ -12,10 +12,10 @@ jobs:
|
|||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
uses: https://git.mingliqiye.com/Actions/checkout@v4
|
uses: https://git.mingliqiye.com/Actions/checkout@v4
|
||||||
|
|
||||||
- name: build-test
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
pnpm install
|
pnpm install
|
||||||
pnpm run build-jar
|
pnpm run build-jar-auto
|
||||||
|
|
||||||
- name: Releases
|
- name: Releases
|
||||||
run: |
|
run: |
|
||||||
|
17
application.yaml
Normal file
17
application.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
config:
|
||||||
|
port: 9963
|
||||||
|
app-name: maven-repository
|
||||||
|
app-version: 1.0
|
||||||
|
data-source:
|
||||||
|
mysql:
|
||||||
|
username: pan
|
||||||
|
password: PhXCRiCfEmiGesEm
|
||||||
|
host: 10.0.0.4
|
||||||
|
port: 3307
|
||||||
|
database: pan
|
||||||
|
redis:
|
||||||
|
host: 10.0.0.4
|
||||||
|
port: 6380
|
||||||
|
database: 0
|
||||||
|
username: ''
|
||||||
|
password: redis_kTJpsa
|
@ -1,10 +1,12 @@
|
|||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.*
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
id("org.springframework.boot") version "3.5.3"
|
idea
|
||||||
|
id("io.freefair.lombok") version "8.4"
|
||||||
|
id("org.springframework.boot") version "3.5.0"
|
||||||
id("io.spring.dependency-management") version "1.1.7"
|
id("io.spring.dependency-management") version "1.1.7"
|
||||||
}
|
}
|
||||||
val GROUPSID = project.properties["GROUPSID"] as String
|
val GROUPSID = project.properties["GROUPSID"] as String
|
||||||
@ -20,19 +22,55 @@ version = VERSIONS
|
|||||||
|
|
||||||
val libDir = rootDir.resolve("build").resolve("libs")
|
val libDir = rootDir.resolve("build").resolve("libs")
|
||||||
val srcDir = rootDir.resolve("src").resolve("main").resolve("java")
|
val srcDir = rootDir.resolve("src").resolve("main").resolve("java")
|
||||||
val webDir = rootDir.resolve("src").resolve("main").resolve("resources").resolve("static")
|
val webDir = rootDir.resolve("src").resolve("main").resolve("resources").resolve("html")
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(21)
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
configurations.all {
|
||||||
|
exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
jvmArgs = listOf(
|
||||||
|
"-javaagent:${classpath.find { it.name.contains("mockito-core") }?.absolutePath}",
|
||||||
|
"-XX:+EnableDynamicAgentLoading",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.springframework.boot:spring-boot-starter")
|
implementation("org.springframework.boot:spring-boot-starter")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-log4j2")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
||||||
|
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||||
|
compileOnly("org.projectlombok:lombok")
|
||||||
|
testRuntimeOnly("org.projectlombok:lombok")
|
||||||
|
annotationProcessor("org.projectlombok:lombok")
|
||||||
|
implementation("org.jetbrains:annotations:24.0.0")
|
||||||
|
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.19.1")
|
||||||
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
|
runtimeOnly("com.mysql:mysql-connector-j")
|
||||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||||
|
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9")
|
||||||
|
implementation("cn.dev33:sa-token-redis-jackson:1.44.0")
|
||||||
|
implementation("cn.hutool:hutool-all:5.8.24")
|
||||||
|
implementation("cn.dev33:sa-token-jwt:1.44.0")
|
||||||
|
implementation("org.mindrot:jbcrypt:0.4")
|
||||||
|
implementation("com.github.f4b6a3:uuid-creator:6.1.0")
|
||||||
|
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.16")
|
||||||
|
implementation("com.alibaba:druid-spring-boot-3-starter:1.2.25")
|
||||||
|
implementation("cn.dev33:sa-token-jwt:1.44.0")
|
||||||
|
implementation("cn.dev33:sa-token-spring-boot3-starter:1.44.0")
|
||||||
|
implementation("cn.dev33:sa-token-redis-jackson:1.44.0")
|
||||||
|
implementation("com.baomidou:mybatis-plus-spring-boot3-starter:3.5.12")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateHash(file: File, string: String): String {
|
private fun generateHash(file: File, string: String): String {
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build-jar": "run-p \"build-only\" && gradle -Dorg.gradle.java.home=/opt/jdk/21.0.7/ build-jar",
|
"build-jar": "run-p \"build-only\" && gradle build-jar",
|
||||||
|
"build-jar-auto": "run-p \"build-only\" && gradle -Dorg.gradle.java.home=/opt/jdk/21.0.7/ build-jar",
|
||||||
"build": "run-p type-check \"build-only {@}\" --",
|
"build": "run-p type-check \"build-only {@}\" --",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"build-only": "vite build",
|
"build-only": "vite build",
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
package com.mingliqiye.disk;
|
package com.mingliqiye.disk;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@ConfigurationPropertiesScan
|
||||||
|
@MapperScan("com.mingliqiye.disk.mappers")
|
||||||
public class DiskApplication {
|
public class DiskApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
64
src/main/java/com/mingliqiye/disk/cache/MyBatisRedisCache.java
vendored
Normal file
64
src/main/java/com/mingliqiye/disk/cache/MyBatisRedisCache.java
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package com.mingliqiye.disk.cache;
|
||||||
|
|
||||||
|
import com.mingliqiye.disk.config.ApplicationContextHolder;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
import org.apache.ibatis.cache.Cache;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.util.DigestUtils;
|
||||||
|
|
||||||
|
public class MyBatisRedisCache implements Cache {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final RedisTemplate<String, Object> redisTemplate;
|
||||||
|
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||||
|
|
||||||
|
public MyBatisRedisCache(String id) {
|
||||||
|
this.id = id;
|
||||||
|
this.redisTemplate = ApplicationContextHolder.getBean(RedisTemplate.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putObject(Object key, Object value) {
|
||||||
|
String redisKey = getRedisKey(key);
|
||||||
|
redisTemplate.opsForValue().set(redisKey, value, 1, TimeUnit.HOURS); // 设置1小时过期
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getObject(Object key) {
|
||||||
|
String redisKey = getRedisKey(key);
|
||||||
|
return redisTemplate.opsForValue().get(redisKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object removeObject(Object key) {
|
||||||
|
String redisKey = getRedisKey(key);
|
||||||
|
redisTemplate.delete(redisKey);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
redisTemplate.delete(redisTemplate.keys(id + ":*"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadWriteLock getReadWriteLock() {
|
||||||
|
return readWriteLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getRedisKey(Object key) {
|
||||||
|
return (id + ":" + DigestUtils.md5DigestAsHex(String.valueOf(key).getBytes()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.mingliqiye.disk.config;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ApplicationContextHolder implements ApplicationContextAware {
|
||||||
|
|
||||||
|
private static ApplicationContext context;
|
||||||
|
|
||||||
|
public static <T> T getBean(Class<T> name) {
|
||||||
|
return context.getBean(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(String beanName) {
|
||||||
|
return (T) context.getBean(beanName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(String beanName, Class<T> beanType) {
|
||||||
|
return context.getBean(beanName, beanType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
|
||||||
|
context = applicationContext;
|
||||||
|
}
|
||||||
|
}
|
23
src/main/java/com/mingliqiye/disk/config/RedisConfig.java
Normal file
23
src/main/java/com/mingliqiye/disk/config/RedisConfig.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package com.mingliqiye.disk.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class RedisConfig {
|
||||||
|
|
||||||
|
@Primary
|
||||||
|
@Bean
|
||||||
|
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||||
|
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||||
|
template.setConnectionFactory(connectionFactory);
|
||||||
|
template.setKeySerializer(new StringRedisSerializer());
|
||||||
|
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.mingliqiye.disk.config;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||||
|
import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
|
||||||
|
import cn.dev33.satoken.stp.StpLogic;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class SaTokenConfigure implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public StpLogic getStpLogicJwt() {
|
||||||
|
return new StpLogicJwtForSimple();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.mingliqiye.disk.config;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.ExternalDocumentation;
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
|
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
|
||||||
|
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@OpenAPIDefinition(
|
||||||
|
externalDocs = @ExternalDocumentation(
|
||||||
|
description = "@git.mingliqiye",
|
||||||
|
url = "https://git.mingliqiye.com/mingliqiye/pan-disk"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@SecurityScheme(
|
||||||
|
name = "Authorization-Bearer-Token", // 认证方案名称
|
||||||
|
type = SecuritySchemeType.HTTP, // 认证类型,当前为http认证
|
||||||
|
description = "Authorization: bearer {token}", // 描述信息
|
||||||
|
in = SecuritySchemeIn.HEADER, // 代表在http请求头部
|
||||||
|
scheme = "bearer", // 认证方案,如:Authorization: bearer token信息
|
||||||
|
bearerFormat = "JWT"
|
||||||
|
) // 表示使用 JWT 格式作为 Bearer Token 的格式
|
||||||
|
@Configuration
|
||||||
|
public class SpringDocConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public OpenAPI openAPI() {
|
||||||
|
return new OpenAPI()
|
||||||
|
// 配置接口文档基本信息
|
||||||
|
.info(this.getApiInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Info getApiInfo() {
|
||||||
|
return new Info()
|
||||||
|
.title("pan-disk")
|
||||||
|
.description("SpringBoot3 pan-disk Swagger3 ApiDoc")
|
||||||
|
.contact(
|
||||||
|
new Contact().name("mingliqiye").url("https://www.mingliqiye.com").email("minglipro@mingliqiye.com")
|
||||||
|
)
|
||||||
|
.license(new License().name("Apache 2.0").url("https://www.apache.org/licenses/LICENSE-2.0"))
|
||||||
|
.summary("ApiDoc")
|
||||||
|
.termsOfService("https://pan.mingliqiye.com/")
|
||||||
|
.version("1.0");
|
||||||
|
}
|
||||||
|
}
|
14
src/main/java/com/mingliqiye/disk/configuration/Config.java
Normal file
14
src/main/java/com/mingliqiye/disk/configuration/Config.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.mingliqiye.disk.configuration;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "config")
|
||||||
|
public class Config {
|
||||||
|
|
||||||
|
private String appName;
|
||||||
|
private String appVersion;
|
||||||
|
private Integer port;
|
||||||
|
private DataSource dataSource;
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.mingliqiye.disk.configuration;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "config.data-source")
|
||||||
|
public class DataSource {
|
||||||
|
|
||||||
|
private Mysql mysql;
|
||||||
|
private Redis redis;
|
||||||
|
}
|
15
src/main/java/com/mingliqiye/disk/configuration/Mysql.java
Normal file
15
src/main/java/com/mingliqiye/disk/configuration/Mysql.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.mingliqiye.disk.configuration;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "config.data-source.mysql")
|
||||||
|
public class Mysql {
|
||||||
|
|
||||||
|
private String host;
|
||||||
|
private Integer port;
|
||||||
|
private String database;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
}
|
15
src/main/java/com/mingliqiye/disk/configuration/Redis.java
Normal file
15
src/main/java/com/mingliqiye/disk/configuration/Redis.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.mingliqiye.disk.configuration;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ConfigurationProperties(prefix = "config.data-source.redis")
|
||||||
|
public class Redis {
|
||||||
|
|
||||||
|
private String host;
|
||||||
|
private Integer port;
|
||||||
|
private Integer database;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.mingliqiye.disk.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.mingliqiye.disk.dto.auth.Login;
|
||||||
|
import com.mingliqiye.disk.exception.ExceptionCode;
|
||||||
|
import com.mingliqiye.disk.http.Respose;
|
||||||
|
import com.mingliqiye.disk.mappers.UserMapper;
|
||||||
|
import com.mingliqiye.disk.model.User;
|
||||||
|
import com.mingliqiye.disk.util.PanStpUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.mindrot.jbcrypt.BCrypt;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/apis/auth")
|
||||||
|
@Tag(name = "权限管理", description = "权限和用户管理")
|
||||||
|
public class AuthController {
|
||||||
|
|
||||||
|
private final UserMapper userMapper;
|
||||||
|
|
||||||
|
public AuthController(UserMapper userMapper) {
|
||||||
|
this.userMapper = userMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "登陆")
|
||||||
|
@PostMapping("/login")
|
||||||
|
public Respose<String> login(@Valid @RequestBody Login loginBody) {
|
||||||
|
User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", loginBody.getUsername()));
|
||||||
|
if (user != null && BCrypt.checkpw(loginBody.getPassword(), user.getPassword())) {
|
||||||
|
return Respose.builder(PanStpUtil.login(user.getId()));
|
||||||
|
}
|
||||||
|
return Respose.error(String.class, ExceptionCode.ERROR_INTERNAL_SERVER, "用户名或密码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取当前登陆用户的信息")
|
||||||
|
@SecurityRequirement(name = "Authorization-Bearer-Token")
|
||||||
|
@GetMapping("/who-is-me")
|
||||||
|
public Respose<User> whoIsMe() {
|
||||||
|
return Respose.builder().setData(userMapper.selectById(PanStpUtil.getLoginId()).setPasswordNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "登出", security = @SecurityRequirement(name = "Authorization-Bearer-Token"))
|
||||||
|
@DeleteMapping("/logout")
|
||||||
|
@SaCheckLogin
|
||||||
|
public Respose<Object> logout() {
|
||||||
|
StpUtil.logout();
|
||||||
|
return Respose.builder();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.mingliqiye.disk.controller;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping
|
||||||
|
@Tag(name = "前端路由", description = "统一匹配路径指向VueRouter")
|
||||||
|
public class IndexController {
|
||||||
|
|
||||||
|
@GetMapping(value = { "/", "/{path:^(?!static|apis).*$}/**" })
|
||||||
|
public ResponseEntity<StreamingResponseBody> index() {
|
||||||
|
StreamingResponseBody streamingResponseBody = s -> {
|
||||||
|
try (InputStream stream = this.getClass().getResourceAsStream("/html/index.html")) {
|
||||||
|
if (stream != null) {
|
||||||
|
stream.transferTo(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return ResponseEntity.ok().body(streamingResponseBody);
|
||||||
|
}
|
||||||
|
}
|
14
src/main/java/com/mingliqiye/disk/dto/auth/Login.java
Normal file
14
src/main/java/com/mingliqiye/disk/dto/auth/Login.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.mingliqiye.disk.dto.auth;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Login {
|
||||||
|
|
||||||
|
@NotNull(message = "用户名不能为空")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@NotNull(message = "用户密码不能为空")
|
||||||
|
private String password;
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.mingliqiye.disk.exception;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class BaseException extends RuntimeException implements BaseExceptionInterface, Serializable {
|
||||||
|
|
||||||
|
private Integer code;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public BaseException(String message, Integer code, Throwable throwable) {
|
||||||
|
super(message, throwable);
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseException(String message, Integer code) {
|
||||||
|
this(message, code, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Throwable getThrowable() {
|
||||||
|
return this.getCause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
package com.mingliqiye.disk.exception;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.exception.NotPermissionException;
|
||||||
|
import cn.dev33.satoken.exception.NotRoleException;
|
||||||
|
import com.mingliqiye.disk.http.Respose;
|
||||||
|
import com.mingliqiye.disk.util.StringUtil;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||||
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class BaseExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(BaseException.class)
|
||||||
|
public ResponseEntity<Respose<?>> exceptionHandler(BaseException e, HttpServletRequest request) {
|
||||||
|
return ResponseEntity.status(e.getCode()).body(
|
||||||
|
Respose.builder().setCode(e.getCode()).setMessage(StringUtil.format("{}", e.getMessage()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||||
|
public Respose<?> exceptionHandler(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
|
||||||
|
return Respose.builder()
|
||||||
|
.setCode(ExceptionCode.ERROR_METHOD_NOT_ALLOWED.getValue())
|
||||||
|
.setMessage(StringUtil.format("{} by {}", e.getMessage(), e.getClass().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(NoHandlerFoundException.class)
|
||||||
|
public ResponseEntity<Respose<?>> exceptionHandler(
|
||||||
|
NoHandlerFoundException e,
|
||||||
|
HttpServletRequest request,
|
||||||
|
HttpServletResponse response
|
||||||
|
) {
|
||||||
|
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(
|
||||||
|
Respose.builder()
|
||||||
|
.setCode(ExceptionCode.ERROR_NOT_FOUND.getValue())
|
||||||
|
.setMessage(StringUtil.format("{} by {}", e.getMessage(), e.getClass().getName()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(NotLoginException.class)
|
||||||
|
public Respose<?> exceptionHandler(NotLoginException e, HttpServletRequest request) {
|
||||||
|
return Respose.builder()
|
||||||
|
.setCode(ExceptionCode.ERROR_UNAUTHORIZED.getValue())
|
||||||
|
.setMessage(e.getMessage())
|
||||||
|
.setData(e.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||||
|
public Respose<?> exceptionHandler(HttpMessageNotReadableException e, HttpServletRequest request) {
|
||||||
|
return Respose.builder()
|
||||||
|
.setCode(ExceptionCode.ERROR_FORBIDDEN.getValue())
|
||||||
|
.setMessage(StringUtil.format("{} by {}", e.getMessage(), e.getClass().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(NotRoleException.class)
|
||||||
|
public Respose<?> exceptionHandler(NotRoleException e) {
|
||||||
|
return Respose.builder()
|
||||||
|
.setCode(ExceptionCode.ERROR_FORBIDDEN.getValue())
|
||||||
|
.setMessage(e.getMessage())
|
||||||
|
.setData(e.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(NotPermissionException.class)
|
||||||
|
public Respose<?> exceptionHandler(NotPermissionException e) {
|
||||||
|
return Respose.builder()
|
||||||
|
.setCode(ExceptionCode.ERROR_FORBIDDEN.getValue())
|
||||||
|
.setMessage(e.getMessage())
|
||||||
|
.setData(e.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public Respose<?> exceptionHandler(MethodArgumentNotValidException e) {
|
||||||
|
return Respose.builder()
|
||||||
|
.setCode(ExceptionCode.ERROR_FORBIDDEN.getValue())
|
||||||
|
.setMessage(
|
||||||
|
StringUtil.join(
|
||||||
|
",",
|
||||||
|
e.getBindingResult().getFieldErrors(),
|
||||||
|
DefaultMessageSourceResolvable::getDefaultMessage
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public Respose<?> exceptionHandler(Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
return Respose.builder()
|
||||||
|
.setCode(ExceptionCode.ERROR_INTERNAL_SERVER.getValue())
|
||||||
|
.setMessage(StringUtil.format("{} by {}", e.getMessage(), e.getClass().getName()));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.mingliqiye.disk.exception;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public interface BaseExceptionInterface extends Serializable {
|
||||||
|
String getMessage();
|
||||||
|
|
||||||
|
Integer getCode();
|
||||||
|
|
||||||
|
StackTraceElement[] getStackTrace();
|
||||||
|
|
||||||
|
Throwable getThrowable();
|
||||||
|
|
||||||
|
String toString();
|
||||||
|
|
||||||
|
default String getClassName() {
|
||||||
|
return this.getClass().getName();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.mingliqiye.disk.exception;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public enum ExceptionCode {
|
||||||
|
ERROR_INTERNAL_SERVER(500),
|
||||||
|
ERROR_PAYMENT_REQUIRED(402),
|
||||||
|
ERROR_NOT_FOUND(404),
|
||||||
|
ERROR_METHOD_NOT_ALLOWED(405),
|
||||||
|
ERROR_UNAUTHORIZED(401),
|
||||||
|
ERROR_FORBIDDEN(403),
|
||||||
|
OK(200);
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
ExceptionCode(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ExceptionCode getExceptionCode(int value) {
|
||||||
|
for (ExceptionCode exceptionCode : ExceptionCode.values()) {
|
||||||
|
if (exceptionCode.value == value) {
|
||||||
|
return exceptionCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.mingliqiye.disk.exception;
|
||||||
|
|
||||||
|
public class InternalServerException extends BaseException {
|
||||||
|
|
||||||
|
private static final Integer code = ExceptionCode.ERROR_INTERNAL_SERVER.getValue();
|
||||||
|
|
||||||
|
public InternalServerException(String message, Throwable throwable) {
|
||||||
|
super(message, code, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InternalServerException(String message) {
|
||||||
|
super(message, code);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.mingliqiye.disk.exception;
|
||||||
|
|
||||||
|
public class NotFoundException extends BaseException {
|
||||||
|
|
||||||
|
private static final Integer code = ExceptionCode.ERROR_NOT_FOUND.getValue();
|
||||||
|
|
||||||
|
public NotFoundException(String message, Throwable throwable) {
|
||||||
|
super(message, code, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotFoundException(String message) {
|
||||||
|
super(message, code);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.mingliqiye.disk.function;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface P1Function<P> {
|
||||||
|
void call(P p);
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.mingliqiye.disk.function;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface P1R1Function<P, R> {
|
||||||
|
R call(P p);
|
||||||
|
}
|
82
src/main/java/com/mingliqiye/disk/http/Respose.java
Normal file
82
src/main/java/com/mingliqiye/disk/http/Respose.java
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package com.mingliqiye.disk.http;
|
||||||
|
|
||||||
|
import com.mingliqiye.disk.exception.ExceptionCode;
|
||||||
|
import com.mingliqiye.disk.time.DateTime;
|
||||||
|
|
||||||
|
public class Respose<T> {
|
||||||
|
|
||||||
|
private int code = ExceptionCode.OK.getValue();
|
||||||
|
private String message = "操作成功";
|
||||||
|
private T data;
|
||||||
|
private DateTime dateTime = DateTime.now();
|
||||||
|
|
||||||
|
public Respose(int code, String message, T data, DateTime dateTime) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
this.data = data;
|
||||||
|
this.dateTime = dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Respose(int code, String message, T data) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Respose(int code, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Respose(T data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Respose() {}
|
||||||
|
|
||||||
|
public static <T> Respose<T> builder() {
|
||||||
|
return new Respose<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Respose<T> builder(T data) {
|
||||||
|
return new Respose<>().setData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Respose<T> error(Class<T> type, Integer code, String message) {
|
||||||
|
return new Respose<T>().setCode(code).setMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Respose<T> error(Class<T> type, ExceptionCode code, String message) {
|
||||||
|
return error(type, code.getValue(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Respose<T> setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Respose<T> setCode(int code) {
|
||||||
|
this.code = code;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime getDateTime() {
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <TD> Respose<TD> setData(TD data) {
|
||||||
|
return new Respose<>(code, message, data);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.mingliqiye.disk.mappers;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.mingliqiye.disk.cache.MyBatisRedisCache;
|
||||||
|
import com.mingliqiye.disk.model.User;
|
||||||
|
import org.apache.ibatis.annotations.CacheNamespace;
|
||||||
|
|
||||||
|
@CacheNamespace(implementation = MyBatisRedisCache.class)
|
||||||
|
public interface UserMapper extends BaseMapper<User> {}
|
42
src/main/java/com/mingliqiye/disk/model/User.java
Normal file
42
src/main/java/com/mingliqiye/disk/model/User.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package com.mingliqiye.disk.model;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import com.mingliqiye.disk.time.DateTime;
|
||||||
|
import com.mingliqiye.disk.uuid.UUID;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@TableName(value = "users", autoResultMap = true)
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
private UUID id;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
private String nickname;
|
||||||
|
|
||||||
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<String> prermissions;
|
||||||
|
|
||||||
|
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||||
|
private List<String> roles;
|
||||||
|
|
||||||
|
private byte[] icon;
|
||||||
|
private boolean admin;
|
||||||
|
private DateTime creationTime;
|
||||||
|
private DateTime updateTime;
|
||||||
|
|
||||||
|
public User setPasswordNull() {
|
||||||
|
setPassword("***");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
241
src/main/java/com/mingliqiye/disk/time/DateTime.java
Normal file
241
src/main/java/com/mingliqiye/disk/time/DateTime.java
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
package com.mingliqiye.disk.time;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Date;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* com.mingliqiye.libs.Main
|
||||||
|
* <p>自定义时间类
|
||||||
|
*
|
||||||
|
* @author MingLiPro|Armamem0t
|
||||||
|
* @version 1.0
|
||||||
|
* @see LocalDateTime
|
||||||
|
* @see Date
|
||||||
|
* @see ZoneId
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = DateTimeJsonSerializer.class)
|
||||||
|
@JsonDeserialize(using = DateTimeJsonDeserializer.class)
|
||||||
|
@Schema(type = "String")
|
||||||
|
@JsonView(String.class)
|
||||||
|
public class DateTime implements Serializable {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private LocalDateTime localDateTime;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private ZoneId zoneId = ZoneId.systemDefault();
|
||||||
|
|
||||||
|
DateTime() {
|
||||||
|
this(Instant.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime(Instant instant) {
|
||||||
|
this(LocalDateTime.ofInstant(instant, ZoneId.systemDefault()));
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime(LocalDateTime localDateTime) {
|
||||||
|
this.localDateTime = localDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime(Instant instant, ZoneId zoneId) {
|
||||||
|
this(LocalDateTime.ofInstant(instant, zoneId));
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime(Date date) {
|
||||||
|
this(date.toInstant());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime now() {
|
||||||
|
return new DateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime ofCurrentTimeMillis(long time) {
|
||||||
|
return new DateTime(Instant.ofEpochMilli(time));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime of(Instant instant) {
|
||||||
|
return new DateTime(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String format(Date date, String formater) {
|
||||||
|
return format(toLocalDateTime(date), formater);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String format(LocalDateTime date, String formater) {
|
||||||
|
return DateTimeFormatter.ofPattern(formater).format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDateTime toLocalDateTime(Date date) {
|
||||||
|
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String format(LocalDateTime date, Formats formats) {
|
||||||
|
return format(date, formats.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime parse(String timestr, Formats formater) {
|
||||||
|
return parse(timestr, formater.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime parse(String timestr, String formater) {
|
||||||
|
DateTimeFormatter sdf = DateTimeFormatter.ofPattern(formater);
|
||||||
|
return of(LocalDateTime.parse(timestr, sdf));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime of(LocalDateTime localDateTime) {
|
||||||
|
return new DateTime(localDateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return (
|
||||||
|
this.getClass().getName() +
|
||||||
|
'@' +
|
||||||
|
Integer.toHexString(hashCode()) +
|
||||||
|
'(' +
|
||||||
|
format(Formats.STANDARD_DATETIME_MILLISECOUND7) +
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String format(Formats formater) {
|
||||||
|
return format(localDateTime, formater.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant toInstant() {
|
||||||
|
return toInstant(localDateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instant toInstant(LocalDateTime localDateTime) {
|
||||||
|
return toInstant(localDateTime, ZoneId.systemDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instant toInstant(LocalDateTime localDateTime, ZoneId zoneId) {
|
||||||
|
return localDateTime.atZone(zoneId).toInstant();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant toInstant(ZoneId zoneId) {
|
||||||
|
return toInstant(localDateTime, zoneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date toDate() {
|
||||||
|
return toDate(localDateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Date toDate(LocalDateTime localDateTime) {
|
||||||
|
return Date.from(toInstant(localDateTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String format(String formater) {
|
||||||
|
return format(localDateTime, formater);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime plusYears(long years) {
|
||||||
|
return plusYears(this, years);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime plusYears(DateTime dateTime, long years) {
|
||||||
|
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusYears(years));
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime plusMonths(long months) {
|
||||||
|
return plusMonths(this, months);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime plusMonths(DateTime dateTime, long months) {
|
||||||
|
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusMonths(months));
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime plusWeeks(long weeks) {
|
||||||
|
return plusWeeks(this, weeks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime plusWeeks(DateTime dateTime, long weeks) {
|
||||||
|
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusWeeks(weeks));
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime plusDays(long days) {
|
||||||
|
return plusDays(this, days);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime plusDays(DateTime dateTime, long days) {
|
||||||
|
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusDays(days));
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime plusHours(long hours) {
|
||||||
|
return plusHours(this, hours);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime plusHours(DateTime dateTime, long hours) {
|
||||||
|
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusHours(hours));
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime plusMinutes(long minutes) {
|
||||||
|
return plusMinutes(this, minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime plusMinutes(DateTime dateTime, long minutes) {
|
||||||
|
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusMinutes(minutes));
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime plusSeconds(long seconds) {
|
||||||
|
return plusSeconds(this, seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime plusSeconds(DateTime dateTime, long seconds) {
|
||||||
|
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusSeconds(seconds));
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime plusNanos(long nanos) {
|
||||||
|
return plusNanos(this, nanos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DateTime plusNanos(DateTime dateTime, long nanos) {
|
||||||
|
dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusNanos(nanos));
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public enum Formats {
|
||||||
|
STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"),
|
||||||
|
STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
|
||||||
|
STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"),
|
||||||
|
STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"),
|
||||||
|
STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"),
|
||||||
|
STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"),
|
||||||
|
STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"),
|
||||||
|
STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"),
|
||||||
|
STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
|
||||||
|
STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"),
|
||||||
|
STANDARD_DATE("yyyy-MM-dd"),
|
||||||
|
ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"),
|
||||||
|
COMPACT_DATETIME("yyyyMMddHHmmss");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
Formats(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.mingliqiye.disk.time;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class DateTimeJsonDeserializer extends JsonDeserializer<DateTime> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||||
|
if (p.isNaN()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DateTime.parse(p.getValueAsString(), DateTime.Formats.STANDARD_DATETIME_MILLISECOUND6);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.mingliqiye.disk.time;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.core.JsonToken;
|
||||||
|
import com.fasterxml.jackson.core.type.WritableTypeId;
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class DateTimeJsonSerializer extends JsonSerializer<DateTime> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||||
|
if (value == null) {
|
||||||
|
gen.writeNull();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gen.writeString(value.format(DateTime.Formats.STANDARD_DATETIME_MILLISECOUND6));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serializeWithType(
|
||||||
|
DateTime value,
|
||||||
|
JsonGenerator gen,
|
||||||
|
SerializerProvider serializers,
|
||||||
|
TypeSerializer typeSer
|
||||||
|
) throws IOException {
|
||||||
|
WritableTypeId typeId = typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.VALUE_STRING));
|
||||||
|
serialize(value, gen, serializers);
|
||||||
|
typeSer.writeTypeSuffix(gen, typeId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package com.mingliqiye.disk.typeHandlers;
|
||||||
|
|
||||||
|
import com.mingliqiye.disk.time.DateTime;
|
||||||
|
import java.sql.*;
|
||||||
|
import org.apache.ibatis.type.BaseTypeHandler;
|
||||||
|
import org.apache.ibatis.type.JdbcType;
|
||||||
|
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||||
|
import org.apache.ibatis.type.MappedTypes;
|
||||||
|
|
||||||
|
@MappedTypes({ DateTime.class })
|
||||||
|
@MappedJdbcTypes(JdbcType.VARCHAR)
|
||||||
|
public class DateTimeTypeHandler extends BaseTypeHandler<DateTime> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNonNullParameter(PreparedStatement ps, int i, DateTime parameter, JdbcType jdbcType)
|
||||||
|
throws SQLException {
|
||||||
|
ps.setTimestamp(i, Timestamp.valueOf(parameter.getLocalDateTime()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||||
|
return parse(rs.getString(columnName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||||
|
return parse(rs.getString(columnIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||||
|
return parse(cs.getString(columnIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime parse(String s) {
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DateTime.parse(s, DateTime.Formats.STANDARD_DATETIME_MILLISECOUND6);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String format(DateTime t) {
|
||||||
|
return t.format(DateTime.Formats.STANDARD_DATETIME_MILLISECOUND6);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.mingliqiye.disk.typeHandlers;
|
||||||
|
|
||||||
|
import com.mingliqiye.disk.uuid.UUID;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import org.apache.ibatis.type.BaseTypeHandler;
|
||||||
|
import org.apache.ibatis.type.JdbcType;
|
||||||
|
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||||
|
import org.apache.ibatis.type.MappedTypes;
|
||||||
|
|
||||||
|
@MappedTypes({ UUID.class })
|
||||||
|
@MappedJdbcTypes(JdbcType.BINARY)
|
||||||
|
public class UUIDBinaryTypeHandler extends BaseTypeHandler<UUID> {
|
||||||
|
|
||||||
|
public static byte[] UUID_TO_BIN(UUID uuid) {
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
||||||
|
bb.putLong(uuid.GetUUID().getMostSignificantBits());
|
||||||
|
bb.putLong(uuid.GetUUID().getLeastSignificantBits());
|
||||||
|
return bb.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID BIN_TO_UUID(byte[] bytes) {
|
||||||
|
if (bytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
||||||
|
long mostSig = bb.getLong();
|
||||||
|
long leastSig = bb.getLong();
|
||||||
|
return new UUID(mostSig, leastSig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType)
|
||||||
|
throws SQLException {
|
||||||
|
ps.setBytes(i, UUID_TO_BIN(parameter));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||||
|
return BIN_TO_UUID(rs.getBytes(columnName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||||
|
return BIN_TO_UUID(rs.getBytes(columnIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||||
|
return BIN_TO_UUID(cs.getBytes(columnIndex));
|
||||||
|
}
|
||||||
|
}
|
264
src/main/java/com/mingliqiye/disk/util/FileUtil.java
Normal file
264
src/main/java/com/mingliqiye/disk/util/FileUtil.java
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
package com.mingliqiye.disk.util;
|
||||||
|
|
||||||
|
import com.mingliqiye.disk.function.P1Function;
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.WritableByteChannel;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class FileUtil {
|
||||||
|
|
||||||
|
public static String save(InputStream inputStream, Path filepath, P1Function<Long> callback) {
|
||||||
|
return save(inputStream, filepath.toString(), callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String save(InputStream inputStream, String filepath, P1Function<Long> callback) {
|
||||||
|
Path filePath = Paths.get(filepath);
|
||||||
|
Path path1 = filePath.getParent();
|
||||||
|
if (!path1.toFile().exists()) {
|
||||||
|
path1.toFile().mkdirs();
|
||||||
|
}
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int len;
|
||||||
|
long total = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
filePath.toFile().createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
try (FileOutputStream fileOutputStream = new FileOutputStream(filePath.toFile())) {
|
||||||
|
while ((len = inputStream.read(buffer)) != -1) {
|
||||||
|
fileOutputStream.write(buffer, 0, len);
|
||||||
|
total += len;
|
||||||
|
if (callback != null) {
|
||||||
|
callback.call(total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return filePath.toAbsolutePath().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String save(InputStream inputStream, Path filepath) {
|
||||||
|
return save(inputStream, filepath.toString(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String save(InputStream inputStream, String filepath) {
|
||||||
|
return save(inputStream, filepath, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean copy(String fromCopy, String toCopyed) {
|
||||||
|
return copy(Path.of(fromCopy), Path.of(toCopyed));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean copy(Path fromCopy, Path toCopyed) {
|
||||||
|
return copy(fromCopy.toFile(), toCopyed.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean copy(File fromCopy, File toCopyed) {
|
||||||
|
try (InputStream inputStream = new FileInputStream(fromCopy)) {
|
||||||
|
try (OutputStream outputStream = new FileOutputStream(toCopyed)) {
|
||||||
|
inputStream.transferTo(outputStream);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String saveTxt(String txt, String path) {
|
||||||
|
return saveTxt(txt, Paths.get(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String saveTxt(String txt, Path path) {
|
||||||
|
return saveTxt(txt, path.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String saveTxt(String txt, File path) {
|
||||||
|
try (OutputStream outputStream = new FileOutputStream(path)) {
|
||||||
|
outputStream.write(txt.getBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return path.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] readBytes(String path) {
|
||||||
|
return readBytes(Paths.get(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] readBytes(Path path) {
|
||||||
|
return readBytes(path.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] readBytes(File f) {
|
||||||
|
try (FileInputStream fileInputStream = new FileInputStream(f)) {
|
||||||
|
return readBytes(fileInputStream);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] readBytes(InputStream inputStream) {
|
||||||
|
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
|
||||||
|
inputStream.transferTo(byteArrayOutputStream);
|
||||||
|
return byteArrayOutputStream.toByteArray();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readString(String path) {
|
||||||
|
return readString(Paths.get(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readString(Path path) {
|
||||||
|
return readString(path.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readString(File path) {
|
||||||
|
byte[] bytes = readBytes(path);
|
||||||
|
return new String(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readString(InputStream path) {
|
||||||
|
byte[] bytes = readBytes(path);
|
||||||
|
return new String(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean delete(Path filepath) {
|
||||||
|
try {
|
||||||
|
Files.delete(filepath);
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
log.info("This is a test message");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件范围读取工具类(包含起始和结束位置)
|
||||||
|
* 有效范围:from ∈ [0, filesize-1], to ∈ [from, filesize-1]
|
||||||
|
*/
|
||||||
|
public class FileRangeReader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取文件指定范围内容到输出流
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @param from 起始位置(包含,0-based)
|
||||||
|
* @param to 结束位置(包含)
|
||||||
|
* @param output 输出流
|
||||||
|
* @throws IOException 如果发生I/O错误
|
||||||
|
* @throws IllegalArgumentException 如果范围无效
|
||||||
|
*/
|
||||||
|
public static void read(String filePath, long from, long to, OutputStream output)
|
||||||
|
throws IOException, IllegalArgumentException {
|
||||||
|
// 参数验证
|
||||||
|
if (from < 0) {
|
||||||
|
throw new IllegalArgumentException("起始位置不能小于0");
|
||||||
|
}
|
||||||
|
if (to < from) {
|
||||||
|
throw new IllegalArgumentException("结束位置不能小于起始位置");
|
||||||
|
}
|
||||||
|
|
||||||
|
Path path = Paths.get(filePath);
|
||||||
|
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
|
||||||
|
long fileSize = fileChannel.size();
|
||||||
|
|
||||||
|
// 自动调整超出文件范围的请求
|
||||||
|
if (from >= fileSize) {
|
||||||
|
return; // 起始位置已超出文件范围,不读取任何内容
|
||||||
|
}
|
||||||
|
if (to >= fileSize) {
|
||||||
|
to = fileSize - 1; // 调整结束位置为文件末尾
|
||||||
|
}
|
||||||
|
|
||||||
|
try (WritableByteChannel outChannel = Channels.newChannel(output)) {
|
||||||
|
long position = from;
|
||||||
|
long remaining = to - from + 1; // +1因为包含结束位置
|
||||||
|
|
||||||
|
// 使用零拷贝技术传输数据
|
||||||
|
while (remaining > 0) {
|
||||||
|
long transferred = fileChannel.transferTo(position, remaining, outChannel);
|
||||||
|
if (transferred <= 0) break;
|
||||||
|
position += transferred;
|
||||||
|
remaining -= transferred;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取文件全部内容到输出流
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @param output 输出流
|
||||||
|
* @throws IOException 如果发生I/O错误
|
||||||
|
*/
|
||||||
|
public static void readFull(String filePath, OutputStream output) throws IOException {
|
||||||
|
Path path = Paths.get(filePath);
|
||||||
|
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
|
||||||
|
long fileSize = fileChannel.size();
|
||||||
|
if (fileSize > 0) {
|
||||||
|
read(filePath, 0, fileSize - 1, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取文件指定范围内容到字节数组
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @param from 起始位置
|
||||||
|
* @param to 结束位置(包含)
|
||||||
|
* @return 读取的字节数组
|
||||||
|
* @throws IOException 如果发生I/O错误
|
||||||
|
*/
|
||||||
|
public static byte[] readToByteArray(String filePath, long from, long to) throws IOException {
|
||||||
|
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||||
|
read(filePath, from, to, output);
|
||||||
|
return output.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取文件全部内容到字节数组
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @return 文件内容的字节数组
|
||||||
|
* @throws IOException 如果发生I/O错误
|
||||||
|
*/
|
||||||
|
public static byte[] readFullToByteArray(String filePath) throws IOException {
|
||||||
|
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||||
|
readFull(filePath, output);
|
||||||
|
return output.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件大小
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @return 文件大小(字节数)
|
||||||
|
* @throws IOException 如果发生I/O错误
|
||||||
|
*/
|
||||||
|
public static long getFileSize(String filePath) throws IOException {
|
||||||
|
return Paths.get(filePath).toFile().length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
src/main/java/com/mingliqiye/disk/util/HashUtil.java
Normal file
3
src/main/java/com/mingliqiye/disk/util/HashUtil.java
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package com.mingliqiye.disk.util;
|
||||||
|
|
||||||
|
public class HashUtil {}
|
17
src/main/java/com/mingliqiye/disk/util/ListUtil.java
Normal file
17
src/main/java/com/mingliqiye/disk/util/ListUtil.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.mingliqiye.disk.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ListUtil {
|
||||||
|
|
||||||
|
public static List<String> toStringList(Object[] object) {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for (Object obj : object) {
|
||||||
|
list.add(obj.toString());
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StringList extends ArrayList<String> {}
|
||||||
|
}
|
17
src/main/java/com/mingliqiye/disk/util/NullUtil.java
Normal file
17
src/main/java/com/mingliqiye/disk/util/NullUtil.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.mingliqiye.disk.util;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class NullUtil {
|
||||||
|
|
||||||
|
public static class NullJsonSerializer extends JsonSerializer<String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||||
|
gen.writeString("***");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/main/java/com/mingliqiye/disk/util/PanStpUtil.java
Normal file
16
src/main/java/com/mingliqiye/disk/util/PanStpUtil.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package com.mingliqiye.disk.util;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.mingliqiye.disk.uuid.UUID;
|
||||||
|
|
||||||
|
public class PanStpUtil {
|
||||||
|
|
||||||
|
public static UUID getLoginId() {
|
||||||
|
return UUID.ofString((String) StpUtil.getLoginId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String login(UUID uuid) {
|
||||||
|
StpUtil.login(uuid.toUUIDString());
|
||||||
|
return StpUtil.getTokenValue();
|
||||||
|
}
|
||||||
|
}
|
66
src/main/java/com/mingliqiye/disk/util/StringUtil.java
Normal file
66
src/main/java/com/mingliqiye/disk/util/StringUtil.java
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package com.mingliqiye.disk.util;
|
||||||
|
|
||||||
|
import com.mingliqiye.disk.function.P1R1Function;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import org.slf4j.helpers.MessageFormatter;
|
||||||
|
|
||||||
|
public class StringUtil {
|
||||||
|
|
||||||
|
public static String toString(Object obj) {
|
||||||
|
return obj == null ? "" : obj.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String format(String format, Object... args) {
|
||||||
|
return MessageFormatter.arrayFormat(format, args).getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEmpty(String str) {
|
||||||
|
return str == null || str.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String d = joinOf(",", "", "", "1", "", "2", "3", "4", "5", "6", "7", "8", "9");
|
||||||
|
StringUtil.println(d);
|
||||||
|
List<String> dd = split(d, ",");
|
||||||
|
StringUtil.println(dd);
|
||||||
|
println(10000 / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String joinOf(String spec, String... objects) {
|
||||||
|
return join(spec, Arrays.asList(objects));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void println(Object... objects) {
|
||||||
|
if (objects.length == 1) {
|
||||||
|
System.out.println(objects[0]);
|
||||||
|
} else {
|
||||||
|
System.out.println(join(" ", ListUtil.toStringList(objects)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> split(String str, String separator) {
|
||||||
|
List<String> data = new ArrayList<>(Arrays.asList(str.split(separator)));
|
||||||
|
while (!data.isEmpty() && data.getFirst().isEmpty()) data.removeFirst();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <P> String join(String separator, List<P> list, P1R1Function<P, String> fun) {
|
||||||
|
StringBuilder sb = StringUtil.stringBuilder();
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
P item = list.get(i);
|
||||||
|
if (i == 0) sb.append(fun == null ? item.toString() : fun.call(item));
|
||||||
|
else sb.append(separator).append(fun == null ? item.toString() : fun.call(item));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String join(String separator, List<String> list) {
|
||||||
|
return join(separator, list, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StringBuilder stringBuilder() {
|
||||||
|
return new StringBuilder();
|
||||||
|
}
|
||||||
|
}
|
40
src/main/java/com/mingliqiye/disk/util/SystemUtil.java
Normal file
40
src/main/java/com/mingliqiye/disk/util/SystemUtil.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package com.mingliqiye.disk.util;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public class SystemUtil {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private static final String osName = System.getProperties().getProperty("os.name");
|
||||||
|
|
||||||
|
public static boolean isWindows() {
|
||||||
|
return osName != null && osName.startsWith("Windows");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMac() {
|
||||||
|
return osName != null && osName.startsWith("Mac");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isUnix() {
|
||||||
|
return ((osName != null && osName.startsWith("Linux")) || (!isWindows() && !isMac()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getJdkVersion() {
|
||||||
|
return System.getProperty("java.specification.version");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer getJavaVersionFloat() {
|
||||||
|
String version = getJdkVersion();
|
||||||
|
String uversion;
|
||||||
|
if (version.startsWith("1.")) {
|
||||||
|
uversion = version.substring(2, 3);
|
||||||
|
} else {
|
||||||
|
uversion = version.substring(0, 2);
|
||||||
|
}
|
||||||
|
return Integer.parseInt(uversion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isjdk8plus() {
|
||||||
|
return getJavaVersionFloat() > 8;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
package com.mingliqiye.disk.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程局部变量工具类
|
||||||
|
*/
|
||||||
|
public class ThreadLocalDataHolderUtil {
|
||||||
|
|
||||||
|
// 静态实例,用于存储字符串类型的线程局部变量
|
||||||
|
public static final ThreadLocalDataHolder<String> LibrarysLocalDataHolderStatic = new ThreadLocalDataHolder<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 泛型线程局部变量持有器
|
||||||
|
*
|
||||||
|
* @param <T> 存储的数据类型
|
||||||
|
*/
|
||||||
|
public static class ThreadLocalDataHolder<T> {
|
||||||
|
|
||||||
|
private final ThreadLocal<T> threadLocal;
|
||||||
|
|
||||||
|
public ThreadLocalDataHolder() {
|
||||||
|
this.threadLocal = new ThreadLocal<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前线程存储的值
|
||||||
|
*
|
||||||
|
* @return 当前线程存储的值,如果没有则返回null
|
||||||
|
*/
|
||||||
|
public T get() {
|
||||||
|
return threadLocal.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置当前线程的值
|
||||||
|
*
|
||||||
|
* @param value 要存储的值
|
||||||
|
*/
|
||||||
|
public void set(T value) {
|
||||||
|
threadLocal.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除当前线程存储的值
|
||||||
|
*/
|
||||||
|
public void remove() {
|
||||||
|
threadLocal.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前线程存储的值,如果不存在则返回默认值
|
||||||
|
*
|
||||||
|
* @param defaultValue 默认值
|
||||||
|
* @return 当前线程存储的值或默认值
|
||||||
|
*/
|
||||||
|
public T getOrDefault(T defaultValue) {
|
||||||
|
T value = threadLocal.get();
|
||||||
|
return value != null ? value : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全获取值(避免NPE)
|
||||||
|
*
|
||||||
|
* @return 值或null
|
||||||
|
*/
|
||||||
|
public T safeGet() {
|
||||||
|
try {
|
||||||
|
return threadLocal.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查当前线程是否有值
|
||||||
|
*
|
||||||
|
* @return 是否有值
|
||||||
|
*/
|
||||||
|
public boolean isPresent() {
|
||||||
|
return threadLocal.get() != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
141
src/main/java/com/mingliqiye/disk/uuid/UUID.java
Normal file
141
src/main/java/com/mingliqiye/disk/uuid/UUID.java
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package com.mingliqiye.disk.uuid;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonView;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.github.f4b6a3.uuid.UuidCreator;
|
||||||
|
import com.mingliqiye.disk.time.DateTime;
|
||||||
|
import com.mingliqiye.disk.util.StringUtil;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@JsonSerialize(using = UUIDJsonSerializer.class)
|
||||||
|
@JsonDeserialize(using = UUIDJsonDeserializer.class)
|
||||||
|
@JsonView(String.class)
|
||||||
|
@Schema(type = "String")
|
||||||
|
public class UUID implements Serializable {
|
||||||
|
|
||||||
|
private java.util.UUID uuid;
|
||||||
|
|
||||||
|
public UUID(long msb, long lsb) {
|
||||||
|
uuid = new java.util.UUID(msb, lsb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID() {
|
||||||
|
uuid = UuidCreator.getTimeBased();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID(java.util.UUID uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID(String uuid) {
|
||||||
|
this.uuid = java.util.UUID.fromString(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID of(byte[] bytes) {
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
||||||
|
long msb = bb.getLong();
|
||||||
|
long lsb = bb.getLong();
|
||||||
|
return new UUID(msb, lsb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID ofString(String data) {
|
||||||
|
try {
|
||||||
|
java.util.UUID uuid1 = java.util.UUID.fromString(data);
|
||||||
|
UUID uuid = new UUID();
|
||||||
|
uuid.setUuid(uuid1);
|
||||||
|
return uuid;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new UUIDException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toBytes() {
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
||||||
|
bb.putLong(uuid.getMostSignificantBits());
|
||||||
|
bb.putLong(uuid.getLeastSignificantBits());
|
||||||
|
return bb.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public java.util.UUID GetUUID() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toUUIDString() {
|
||||||
|
return toUUIDString(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toUUIDString(boolean u) {
|
||||||
|
if (uuid == null) {
|
||||||
|
throw new UUIDException("uuid is null : NullPointerException");
|
||||||
|
}
|
||||||
|
if (u) {
|
||||||
|
return uuid.toString().toUpperCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
return uuid.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
UUID uuid = (UUID) o;
|
||||||
|
return Objects.equals(this.uuid, uuid.uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (uuid == null) return "UUID(null)";
|
||||||
|
if (uuid.version() == 1) return StringUtil.format(
|
||||||
|
"UUID(uuid={},time={},mac={},version={})",
|
||||||
|
toUUIDString(true),
|
||||||
|
getDateTime().format(DateTime.Formats.STANDARD_DATETIME),
|
||||||
|
extractMACFromUUID(),
|
||||||
|
uuid.version()
|
||||||
|
);
|
||||||
|
return StringUtil.format("UUID(uuid={},version={})", toUUIDString(true), uuid.version());
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime getDateTime() {
|
||||||
|
if (uuid == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DateTime.ofCurrentTimeMillis(uuid.timestamp() / 10_000).plusDays(-141427L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String extractMACFromUUID() {
|
||||||
|
return extractMACFromUUID(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String extractMACFromUUID(String spec) {
|
||||||
|
if (uuid == null) {
|
||||||
|
throw new UUIDException("uuid is null : NullPointerException");
|
||||||
|
}
|
||||||
|
if (spec == null) {
|
||||||
|
spec = ":";
|
||||||
|
}
|
||||||
|
long leastSigBits = uuid.getLeastSignificantBits();
|
||||||
|
long macLong = leastSigBits & 0xFFFFFFFFFFFFL;
|
||||||
|
byte[] macBytes = new byte[6];
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
macBytes[5 - i] = (byte) (macLong >> (8 * i));
|
||||||
|
}
|
||||||
|
StringBuilder mac = new StringBuilder();
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
mac.append(String.format("%02X", macBytes[i]));
|
||||||
|
if (i < 5) mac.append(spec);
|
||||||
|
}
|
||||||
|
return mac.toString();
|
||||||
|
}
|
||||||
|
}
|
12
src/main/java/com/mingliqiye/disk/uuid/UUIDException.java
Normal file
12
src/main/java/com/mingliqiye/disk/uuid/UUIDException.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.mingliqiye.disk.uuid;
|
||||||
|
|
||||||
|
public class UUIDException extends RuntimeException {
|
||||||
|
|
||||||
|
public UUIDException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUIDException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.mingliqiye.disk.uuid;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class UUIDJsonDeserializer extends JsonDeserializer<UUID> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||||
|
if (p.isNaN()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new UUID(p.getValueAsString());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.mingliqiye.disk.uuid;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.core.JsonToken;
|
||||||
|
import com.fasterxml.jackson.core.type.WritableTypeId;
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class UUIDJsonSerializer extends JsonSerializer<UUID> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(UUID uuid, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
|
||||||
|
throws UUIDException, IOException {
|
||||||
|
if (uuid == null) {
|
||||||
|
jsonGenerator.writeNull();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
jsonGenerator.writeString(uuid.toUUIDString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serializeWithType(
|
||||||
|
UUID value,
|
||||||
|
JsonGenerator gen,
|
||||||
|
SerializerProvider serializers,
|
||||||
|
TypeSerializer typeSer
|
||||||
|
) throws IOException {
|
||||||
|
WritableTypeId typeId = typeSer.writeTypePrefix(gen, typeSer.typeId(value, JsonToken.VALUE_STRING));
|
||||||
|
serialize(value, gen, serializers);
|
||||||
|
typeSer.writeTypeSuffix(gen, typeId);
|
||||||
|
}
|
||||||
|
}
|
90
src/main/resources/application.yaml
Normal file
90
src/main/resources/application.yaml
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
config:
|
||||||
|
port: 9963
|
||||||
|
app-name: maven-repository
|
||||||
|
app-version: 1.0
|
||||||
|
data-source:
|
||||||
|
mysql:
|
||||||
|
username: pan
|
||||||
|
password: PhXCRiCfEmiGesEm
|
||||||
|
host: 10.0.0.4
|
||||||
|
port: 3307
|
||||||
|
database: pan
|
||||||
|
redis:
|
||||||
|
host: 10.0.0.4
|
||||||
|
port: 6380
|
||||||
|
database: 0
|
||||||
|
username: ''
|
||||||
|
password: redis_kTJpsa
|
||||||
|
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: ${config.app-name}
|
||||||
|
version: ${config.app-version}
|
||||||
|
banner:
|
||||||
|
location: classpath:banner/banner.txt
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://${config.data-source.mysql.host}:${config.data-source.mysql.port}/${config.data-source.mysql.database}?rewriteBatchedStatements=true
|
||||||
|
username: ${config.data-source.mysql.username}
|
||||||
|
password: ${config.data-source.mysql.password}
|
||||||
|
type: com.alibaba.druid.pool.DruidDataSource
|
||||||
|
druid:
|
||||||
|
initial-size: 0
|
||||||
|
min-idle: 0
|
||||||
|
maxActive: 1024
|
||||||
|
connect-timeout: 1000
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: ${config.data-source.redis.host}
|
||||||
|
port: ${config.data-source.redis.port}
|
||||||
|
database: ${config.data-source.redis.database}
|
||||||
|
password: ${config.data-source.redis.password}
|
||||||
|
username: ${config.data-source.redis.username}
|
||||||
|
connect-timeout: 5s
|
||||||
|
timeout: 5s
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
min-idle: 0
|
||||||
|
max-idle: 16
|
||||||
|
max-active: 16
|
||||||
|
max-wait: 5s
|
||||||
|
mvc:
|
||||||
|
static-path-pattern: /static/**
|
||||||
|
web:
|
||||||
|
resources:
|
||||||
|
static-locations: classpath:/html/static
|
||||||
|
|
||||||
|
|
||||||
|
server:
|
||||||
|
port: ${config.port}
|
||||||
|
|
||||||
|
|
||||||
|
sa-token:
|
||||||
|
token-name: Authorization
|
||||||
|
timeout: 2592000
|
||||||
|
active-timeout: -1
|
||||||
|
is-concurrent: true
|
||||||
|
is-share: false
|
||||||
|
token-style: random-128
|
||||||
|
is-log: false
|
||||||
|
jwt-secret-key: pzmqljgvdyidabacksmoaghgyikxoasckjhgyuhij
|
||||||
|
token-prefix: Bearer
|
||||||
|
is-read-body: false
|
||||||
|
is-read-cookie: false
|
||||||
|
|
||||||
|
|
||||||
|
mybatis-plus:
|
||||||
|
type-handlers-package: com.mingliqiye.disk.typeHandlers
|
||||||
|
configuration:
|
||||||
|
cache-enabled: true
|
||||||
|
|
||||||
|
global-config:
|
||||||
|
db-config:
|
||||||
|
id-type: auto
|
||||||
|
|
||||||
|
springdoc:
|
||||||
|
swagger-ui:
|
||||||
|
path: /apis/swagger
|
||||||
|
api-docs:
|
||||||
|
path: /apis/swagger/api.json
|
6
src/main/resources/banner/banner.txt
Normal file
6
src/main/resources/banner/banner.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
__ __ _____ _ _ _____ _ _____ ____ _____ __ __ ______
|
||||||
|
| \/ | |_ _| | \ | | / ____| | | |_ _| / __ \ |_ _| \ \ / / | ____|
|
||||||
|
| \ / | | | | \| | | | __ | | | | | | | | | | \ \_/ / | |__
|
||||||
|
| |\/| | | | | . ` | | | |_ | | | | | | | | | | | \ / | __|
|
||||||
|
| | | | _| |_ | |\ | | |__| | | |____ _| |_ | |__| | _| |_ | | | |____
|
||||||
|
|_| |_| |_____| |_| \_| \_____| |______| |_____| \___\_\ |_____| |_| |______|
|
14
src/main/resources/html/index.html
Normal file
14
src/main/resources/html/index.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link href="/static/svg/BIklAcGo.svg" rel="icon">
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||||
|
<title>Vite App</title>
|
||||||
|
<script type="module" crossorigin src="/static/js/CcCh4Sgi.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="/static/css/B-HidlUH.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
src/main/resources/html/static/css/B-HidlUH.css
Normal file
1
src/main/resources/html/static/css/B-HidlUH.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
:root{--vt-c-white:#fff;--vt-c-white-soft:#f8f8f8;--vt-c-white-mute:#f2f2f2;--vt-c-black:#181818;--vt-c-black-soft:#222;--vt-c-black-mute:#282828;--vt-c-indigo:#2c3e50;--vt-c-divider-light-1:#3c3c3c4a;--vt-c-divider-light-2:#3c3c3c1f;--vt-c-divider-dark-1:#545454a6;--vt-c-divider-dark-2:#5454547a;--vt-c-text-light-1:var(--vt-c-indigo);--vt-c-text-light-2:#3c3c3ca8;--vt-c-text-dark-1:var(--vt-c-white);--vt-c-text-dark-2:#ebebeba3;--color-background:var(--vt-c-white);--color-background-soft:var(--vt-c-white-soft);--color-background-mute:var(--vt-c-white-mute);--color-border:var(--vt-c-divider-light-2);--color-border-hover:var(--vt-c-divider-light-1);--color-heading:var(--vt-c-text-light-1);--color-text:var(--vt-c-text-light-1);--section-gap:160px}@media (prefers-color-scheme:dark){:root{--color-background:var(--vt-c-black);--color-background-soft:var(--vt-c-black-soft);--color-background-mute:var(--vt-c-black-mute);--color-border:var(--vt-c-divider-dark-2);--color-border-hover:var(--vt-c-divider-dark-1);--color-heading:var(--vt-c-text-dark-1);--color-text:var(--vt-c-text-dark-2)}}*,:before,:after{box-sizing:border-box;margin:0;font-weight:400}body{min-height:100vh;color:var(--color-text);background:var(--color-background);text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:15px;line-height:1.6;transition:color .5s,background-color .5s}#app{max-width:1280px;margin:0 auto;padding:2rem;font-weight:400}a,.green{color:#00bd7e;padding:3px;text-decoration:none;transition:all .4s}@media (hover:hover){a:hover{background-color:#00bd7e33}}@media (min-width:1024px){body{place-items:center;display:flex}#app{grid-template-columns:1fr 1fr;padding:0 2rem;display:grid}}h1[data-v-bed32675]{font-size:2.6rem;font-weight:500;position:relative;top:-10px}h3[data-v-bed32675]{font-size:1.2rem}.greetings h1[data-v-bed32675],.greetings h3[data-v-bed32675]{text-align:center}@media (min-width:1024px){.greetings h1[data-v-bed32675],.greetings h3[data-v-bed32675]{text-align:left}}header[data-v-608be0ed]{max-height:100vh;line-height:1.5}.logo[data-v-608be0ed]{margin:0 auto 2rem;display:block}nav[data-v-608be0ed]{text-align:center;width:100%;margin-top:2rem;font-size:12px}nav a.router-link-exact-active[data-v-608be0ed]{color:var(--color-text)}nav a.router-link-exact-active[data-v-608be0ed]:hover{background-color:#0000}nav a[data-v-608be0ed]{border-left:1px solid var(--color-border);padding:0 1rem;display:inline-block}nav a[data-v-608be0ed]:first-of-type{border:0}@media (min-width:1024px){header[data-v-608be0ed]{padding-right:calc(var(--section-gap)/2);place-items:center;display:flex}.logo[data-v-608be0ed]{margin:0 2rem 0 0}header .wrapper[data-v-608be0ed]{flex-wrap:wrap;place-items:flex-start;display:flex}nav[data-v-608be0ed]{text-align:left;margin-top:1rem;margin-left:-1rem;padding:1rem 0;font-size:1rem}}.item[data-v-5369c01e]{margin-top:2rem;display:flex;position:relative}.details[data-v-5369c01e]{flex:1;margin-left:1rem}i[data-v-5369c01e]{width:32px;height:32px;color:var(--color-text);place-content:center;place-items:center;display:flex}h3[data-v-5369c01e]{color:var(--color-heading);margin-bottom:.4rem;font-size:1.2rem;font-weight:500}@media (min-width:1024px){.item[data-v-5369c01e]{padding:.4rem 0 1rem calc(var(--section-gap)/2);margin-top:0}i[data-v-5369c01e]{border:1px solid var(--color-border);background:var(--color-background);border-radius:8px;width:50px;height:50px;position:absolute;top:calc(50% - 25px);left:-26px}.item[data-v-5369c01e]:before{content:" ";border-left:1px solid var(--color-border);height:calc(50% - 25px);position:absolute;bottom:calc(50% + 25px);left:0}.item[data-v-5369c01e]:after{content:" ";border-left:1px solid var(--color-border);height:calc(50% - 25px);position:absolute;top:calc(50% + 25px);left:0}.item[data-v-5369c01e]:first-of-type:before,.item[data-v-5369c01e]:last-of-type:after{display:none}}
|
1
src/main/resources/html/static/css/E68f7lAM.css
Normal file
1
src/main/resources/html/static/css/E68f7lAM.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
@media (min-width:1024px){.about{align-items:center;min-height:100vh;display:flex}}
|
5362
src/main/resources/html/static/js/CcCh4Sgi.js
Normal file
5362
src/main/resources/html/static/js/CcCh4Sgi.js
Normal file
File diff suppressed because it is too large
Load Diff
13
src/main/resources/html/static/js/flDiIF0y.js
Normal file
13
src/main/resources/html/static/js/flDiIF0y.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import {
|
||||||
|
__plugin_vue_export_helper_default as e,
|
||||||
|
createBaseVNode as t,
|
||||||
|
createElementBlock as n,
|
||||||
|
openBlock as r,
|
||||||
|
} from './CcCh4Sgi.js';
|
||||||
|
const i = {},
|
||||||
|
a = { class: `about` };
|
||||||
|
function o(e, i) {
|
||||||
|
return r(), n(`div`, a, (i[0] ||= [t(`h1`, null, `This is an about page`, -1)]));
|
||||||
|
}
|
||||||
|
var s = e(i, [[`render`, o]]);
|
||||||
|
export { s as default };
|
9
src/main/resources/html/static/svg/BIklAcGo.svg
Normal file
9
src/main/resources/html/static/svg/BIklAcGo.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<svg viewBox="0 0 1650 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
data-spm-anchor-id="a313x.search_index.0.i10.3b363a81fRIAyG" width="256" height="256">
|
||||||
|
<path d="M1368.639733 314.94553a283.692506 283.692506 0 0 0-45.17515 3.532214 436.693118 436.693118 0 0 0-282.577071-295.032771 448.219288 448.219288 0 0 0-407.691787 63.022123 38.854347 38.854347 0 0 0-7.993956 55.771791 40.713407 40.713407 0 0 0 55.77179 7.80805 366.792474 366.792474 0 0 1 350.618655-46.290586 356.009928 356.009928 0 0 1 222.343537 269.563653 39.412065 39.412065 0 0 0 18.590597 27.142272 40.713407 40.713407 0 0 0 33.277168 3.718119 201.707975 201.707975 0 0 1 254.877081 122.697938 196.130796 196.130796 0 0 1-123.813374 249.671715 39.226159 39.226159 0 0 0-24.539588 50.194611 40.341595 40.341595 0 0 0 51.124141 23.98187 275.140832 275.140832 0 0 0 183.303284-306.558941 278.858951 278.858951 0 0 0-276.999891-229.036152z"
|
||||||
|
fill="#9aed7f"/>
|
||||||
|
<path d="M1368.639733 787.704406a238.703262 238.703262 0 0 1-240.748228 236.286485H240.934134a236.379438 236.379438 0 1 1 0-472.758876 151.69927 151.69927 0 0 1 22.494622 1.115436 402.858232 402.858232 0 0 1 788.984926 10.782546 244.652253 244.652253 0 0 1 217.13817 33.277168 234.985143 234.985143 0 0 1 99.831505 191.483147z"
|
||||||
|
fill="#9aed7f"/>
|
||||||
|
<path d="M1100.377422 632.472923a156.532825 156.532825 0 0 0-23.05234 5.763085l-43.873809 13.942948a40.713407 40.713407 0 0 1-33.277168-3.71812 39.412065 39.412065 0 0 1-18.590596-27.142271L973.589552 576.329321a322.175042 322.175042 0 0 0-632.08029-8.737581l-7.064426 34.764416a39.969783 39.969783 0 0 1-42.386561 31.232203l-35.879852-3.346308h-12.827512a157.64826 157.64826 0 1 0-2.974495 315.110615h3.346307l81.61272-1.301341v1.301341h799.395661a161.552286 161.552286 0 0 0 163.411345-151.141551 156.161013 156.161013 0 0 0-54.470448-125.114716 162.667722 162.667722 0 0 0-133.480485-37.181194z"
|
||||||
|
fill="#cdf8bf"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
29
src/main/resources/log4j2.yaml
Normal file
29
src/main/resources/log4j2.yaml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
Appenders:
|
||||||
|
Console: #输出到控制台
|
||||||
|
name: CONSOLE #Appender命名
|
||||||
|
target: SYSTEM_OUT
|
||||||
|
PatternLayout:
|
||||||
|
pattern: "%style{%d{yyyy-MM-dd HH:mm:ss,SSS}}{bright,magenta} [%highlight{%p}{FATAL=white, ERROR=bright_red, WARN=bright_yellow, INFO=bright_green, DEBUG=bright_cyan, TRACE=bright_blue}] [%style{%t}{bright,yellow}/%style{%c{1}}{bright,cyan}] -- %style{%m}{#EEDFCC}%n" #输出日志的格式
|
||||||
|
disableAnsi: "${env:DISABLECOLOR:-false}"
|
||||||
|
RollingFile:
|
||||||
|
- name: FILE
|
||||||
|
fileName: 'log/info.log'
|
||||||
|
filePattern: "log/$${date:yyyy-MM-dd}/%d{yyyy-MM-dd}-%i.log"
|
||||||
|
PatternLayout:
|
||||||
|
pattern: "%d{yyyy-MM-dd HH:mm:ss,SSS} [%p] [%t/%F:%L/%M/%c] -- %m%n" #输出日志的格式
|
||||||
|
Policies:
|
||||||
|
SizeBasedTriggeringPolicy:
|
||||||
|
size: "10 KB"
|
||||||
|
TimeBasedTriggeringPolicy:
|
||||||
|
modulate: true
|
||||||
|
interval: 1
|
||||||
|
DefaultRolloverStrategy: # 单目录下,文件最多20个,超过会删除最早之前的
|
||||||
|
max: 1000
|
||||||
|
Loggers:
|
||||||
|
Root:
|
||||||
|
level: INFO
|
||||||
|
additivity: true
|
||||||
|
AppenderRef:
|
||||||
|
- ref: CONSOLE
|
||||||
|
- ref: FILE
|
3
src/main/resources/sql/database.sql
Normal file
3
src/main/resources/sql/database.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
create database meven_repository collate utf8mb4_unicode_ci;
|
||||||
|
create user 'meven_repository_admin'@'%' IDENTIFIED BY 'meven_repository_admin_password';
|
||||||
|
GRANT ALL ON meven_repository.* TO 'meven_repository_admin'@'%'
|
18
src/main/resources/sql/users.sql
Normal file
18
src/main/resources/sql/users.sql
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
DROP TABLE IF EXISTS `users`;
|
||||||
|
create table `users`
|
||||||
|
(
|
||||||
|
id binary(16) default (uuid_to_bin(
|
||||||
|
uuid(),
|
||||||
|
1)) primary key,
|
||||||
|
username varchar(256) unique not null,
|
||||||
|
password varchar(128) not null,
|
||||||
|
nickname varchar(256) not null,
|
||||||
|
prermissions json,
|
||||||
|
roles json,
|
||||||
|
icon mediumblob,
|
||||||
|
admin bool default false,
|
||||||
|
creation_time timestamp(6) default current_timestamp(6) not null,
|
||||||
|
update_time timestamp(6) null
|
||||||
|
);
|
||||||
|
INSERT INTO users (id, username, password, nickname, prermissions, roles, icon, admin)
|
||||||
|
VALUES (0x689EFC204D1F11F08134DB0063E177A7, 'admin', 'admin', '管理员', '[]', '[]', null, 1);
|
@ -1,11 +1,26 @@
|
|||||||
package com.mingliqiye.disk;
|
package com.mingliqiye.disk;
|
||||||
|
|
||||||
|
import com.mingliqiye.disk.mappers.UserMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mindrot.jbcrypt.BCrypt;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
class DiskApplicationTests {
|
class DiskApplicationTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
UserMapper userMapper;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void contextLoads() {}
|
void contextLoads() {
|
||||||
|
log.info("{}", userMapper.selectList(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getBCryptPdw() {
|
||||||
|
log.info("{}", BCrypt.hashpw("admin", BCrypt.gensalt()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,13 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: path.resolve(__dirname, 'src/main/resources/static'),
|
outDir: path.resolve(__dirname, 'src/main/resources/html'),
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
chunkFileNames: 'js/[hash].js',
|
chunkFileNames: 'static/js/[hash].js',
|
||||||
entryFileNames: 'js/[hash].js',
|
entryFileNames: 'static/js/[hash].js',
|
||||||
assetFileNames: '[ext]/[hash].[ext]',
|
assetFileNames: 'static/[ext]/[hash].[ext]',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { RouterLink, RouterView } from 'vue-router';
|
import { RouterLink, RouterView } from 'vue-router';
|
||||||
import HelloWorld from './components/HelloWorld.vue';
|
import HelloWorld from './components/HelloWorld.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<header>
|
<header>
|
||||||
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
|
<img alt="Vue logo" class="logo" height="125" src="@/assets/icon.svg" width="125" />
|
||||||
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<HelloWorld msg="You did it!" />
|
<HelloWorld msg="You did it!" />
|
||||||
|
9
web-src/assets/icon.svg
Normal file
9
web-src/assets/icon.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<svg viewBox="0 0 1650 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
data-spm-anchor-id="a313x.search_index.0.i10.3b363a81fRIAyG" width="256" height="256">
|
||||||
|
<path d="M1368.639733 314.94553a283.692506 283.692506 0 0 0-45.17515 3.532214 436.693118 436.693118 0 0 0-282.577071-295.032771 448.219288 448.219288 0 0 0-407.691787 63.022123 38.854347 38.854347 0 0 0-7.993956 55.771791 40.713407 40.713407 0 0 0 55.77179 7.80805 366.792474 366.792474 0 0 1 350.618655-46.290586 356.009928 356.009928 0 0 1 222.343537 269.563653 39.412065 39.412065 0 0 0 18.590597 27.142272 40.713407 40.713407 0 0 0 33.277168 3.718119 201.707975 201.707975 0 0 1 254.877081 122.697938 196.130796 196.130796 0 0 1-123.813374 249.671715 39.226159 39.226159 0 0 0-24.539588 50.194611 40.341595 40.341595 0 0 0 51.124141 23.98187 275.140832 275.140832 0 0 0 183.303284-306.558941 278.858951 278.858951 0 0 0-276.999891-229.036152z"
|
||||||
|
fill="#9aed7f"/>
|
||||||
|
<path d="M1368.639733 787.704406a238.703262 238.703262 0 0 1-240.748228 236.286485H240.934134a236.379438 236.379438 0 1 1 0-472.758876 151.69927 151.69927 0 0 1 22.494622 1.115436 402.858232 402.858232 0 0 1 788.984926 10.782546 244.652253 244.652253 0 0 1 217.13817 33.277168 234.985143 234.985143 0 0 1 99.831505 191.483147z"
|
||||||
|
fill="#9aed7f"/>
|
||||||
|
<path d="M1100.377422 632.472923a156.532825 156.532825 0 0 0-23.05234 5.763085l-43.873809 13.942948a40.713407 40.713407 0 0 1-33.277168-3.71812 39.412065 39.412065 0 0 1-18.590596-27.142271L973.589552 576.329321a322.175042 322.175042 0 0 0-632.08029-8.737581l-7.064426 34.764416a39.969783 39.969783 0 0 1-42.386561 31.232203l-35.879852-3.346308h-12.827512a157.64826 157.64826 0 1 0-2.974495 315.110615h3.346307l81.61272-1.301341v1.301341h799.395661a161.552286 161.552286 0 0 0 163.411345-151.141551 156.161013 156.161013 0 0 0-54.470448-125.114716 162.667722 162.667722 0 0 0-133.480485-37.181194z"
|
||||||
|
fill="#cdf8bf"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
|
Before Width: | Height: | Size: 276 B |
@ -2,12 +2,12 @@
|
|||||||
<html lang="">
|
<html lang="">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/favicon.ico">
|
<link href="./assets/icon.svg" rel="icon">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||||
<title>Vite App</title>
|
<title>Vite App</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/main.ts"></script>
|
<script src="/main.ts" type="module"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user