Compare commits

..

1 Commits
master ... dev

Author SHA1 Message Date
a55990db40
完善翻译 2025-07-04 20:01:55 +08:00
22 changed files with 327 additions and 40 deletions

View File

@ -1,17 +0,0 @@
config:
port: 9963
app-name: pan-disk
app-version: 1.1
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

View File

@ -22,7 +22,8 @@ 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("html") val webDir =
rootDir.resolve("src").resolve("main").resolve("resources").resolve("html")
java { java {
toolchain { toolchain {
languageVersion = JavaLanguageVersion.of(21) languageVersion = JavaLanguageVersion.of(21)
@ -31,7 +32,10 @@ java {
allprojects { allprojects {
configurations.all { configurations.all {
exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging") exclude(
group = "org.springframework.boot",
module = "spring-boot-starter-logging"
)
} }
} }
@ -71,6 +75,7 @@ dependencies {
implementation("cn.dev33:sa-token-spring-boot3-starter: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("cn.dev33:sa-token-redis-jackson:1.44.0")
implementation("com.baomidou:mybatis-plus-spring-boot3-starter:3.5.12") implementation("com.baomidou:mybatis-plus-spring-boot3-starter:3.5.12")
implementation("org.apache.tika:tika-core:3.2.0")
} }
private fun generateHash(file: File, string: String): String { private fun generateHash(file: File, string: String): String {
@ -126,7 +131,8 @@ tasks.bootJar {
attributes["Implementation-Version"] = VERSIONS attributes["Implementation-Version"] = VERSIONS
attributes["Email"] = "minglipro@163.com" attributes["Email"] = "minglipro@163.com"
attributes["Implementation-Vendor"] = "minglipro|Armamem0t" attributes["Implementation-Vendor"] = "minglipro|Armamem0t"
attributes["Copyright"] = "Copyright 2026 minglipro All rights reserved." attributes["Copyright"] =
"Copyright 2026 minglipro All rights reserved."
attributes["Env"] = "prod" attributes["Env"] = "prod"
attributes["LICENSE"] = "Apache License 2.0" attributes["LICENSE"] = "Apache License 2.0"
attributes["Created"] = "2025-06-26 09:13:51" attributes["Created"] = "2025-06-26 09:13:51"

View File

@ -1,16 +1,33 @@
package com.mingliqiye.disk; package com.mingliqiye.disk;
import com.mingliqiye.disk.configuration.Config;
import jakarta.annotation.PostConstruct;
import org.mybatis.spring.annotation.MapperScan; 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; import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import java.io.File;
@SpringBootApplication @SpringBootApplication
@ConfigurationPropertiesScan @ConfigurationPropertiesScan
@MapperScan("com.mingliqiye.disk.mappers") @MapperScan("com.mingliqiye.disk.mappers")
public class DiskApplication { public class DiskApplication {
private final Config config;
public DiskApplication(Config config) {
this.config = config;
}
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(DiskApplication.class, args); SpringApplication.run(DiskApplication.class, args);
} }
@PostConstruct
void init() {
File bolbFileDir = new File(config.getBolbPath());
if (!bolbFileDir.exists()) {
bolbFileDir.mkdirs();
}
}
} }

View File

@ -0,0 +1,27 @@
package com.mingliqiye.disk.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("fileUploadTaskExecutor")
public Executor fileUploadTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 核心线程数
executor.setMaxPoolSize(10); // 最大线程数
executor.setQueueCapacity(100); // 队列容量
executor.setThreadNamePrefix("upload-"); // 线程前缀
executor.setRejectedExecutionHandler(
new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}

View File

@ -6,6 +6,7 @@ import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration @Configuration
@ -13,11 +14,26 @@ public class RedisConfig {
@Primary @Primary
@Bean @Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { public RedisTemplate<String, Object> redisTemplateObj(
RedisConnectionFactory connectionFactory
) {
RedisTemplate<String, Object> template = new RedisTemplate<>(); RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory); template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer()); template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template; return template;
} }
@Bean
public RedisTemplate<String, Integer> redisTemplateInt(
RedisConnectionFactory connectionFactory
) {
RedisTemplate<String, Integer> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(
new GenericToStringSerializer<>(Integer.class));
template.afterPropertiesSet();
return template;
}
} }

View File

@ -2,13 +2,14 @@ package com.mingliqiye.disk.config;
import cn.dev33.satoken.stp.StpInterface; import cn.dev33.satoken.stp.StpInterface;
import com.mingliqiye.disk.mappers.UserMapper; import com.mingliqiye.disk.mappers.UserMapper;
import com.mingliqiye.disk.model.User; import com.mingliqiye.disk.model.UserModel;
import com.mingliqiye.disk.uuid.UUID; import com.mingliqiye.disk.uuid.UUID;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
@Slf4j @Slf4j
@Configuration @Configuration
public class StpInterfaceImpl implements StpInterface { public class StpInterfaceImpl implements StpInterface {
@ -24,10 +25,12 @@ public class StpInterfaceImpl implements StpInterface {
*/ */
@Override @Override
public List<String> getPermissionList(Object loginId, String loginType) { public List<String> getPermissionList(Object loginId, String loginType) {
User user = userMapper.selectById(UUID.ofString((String) loginId)); UserModel user = userMapper.selectById(UUID.ofString((String) loginId));
List<String> list = new ArrayList<>(user.getPrermissions()); List<String> list = new ArrayList<>(user.getPrermissions());
if (user.isAdmin()) list.add("*.*.*"); if (user.isAdmin()) {
user.getRoles().forEach(i -> list.add(String.format("%s.*.*", i))); list.add("*.*.*");
}
user.getRoles().forEach((i) -> list.add(String.format("%s.*.*", i)));
log.info(String.valueOf(list)); log.info(String.valueOf(list));
return list; return list;
} }

View File

@ -10,5 +10,7 @@ public class Config {
private String appName; private String appName;
private String appVersion; private String appVersion;
private Integer port; private Integer port;
private String bolbPath;
private String jwtSecretKey;
private DataSource dataSource; private DataSource dataSource;
} }

View File

@ -7,7 +7,7 @@ import com.mingliqiye.disk.dto.auth.Login;
import com.mingliqiye.disk.exception.InternalServerException; import com.mingliqiye.disk.exception.InternalServerException;
import com.mingliqiye.disk.http.Respose; import com.mingliqiye.disk.http.Respose;
import com.mingliqiye.disk.mappers.UserMapper; import com.mingliqiye.disk.mappers.UserMapper;
import com.mingliqiye.disk.model.User; import com.mingliqiye.disk.model.UserModel;
import com.mingliqiye.disk.util.PanStpUtil; import com.mingliqiye.disk.util.PanStpUtil;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.security.SecurityRequirement;
@ -32,7 +32,7 @@ public class AuthController {
@Operation(summary = "登陆") @Operation(summary = "登陆")
@PostMapping("/login") @PostMapping("/login")
public Respose<String> login(@Valid @RequestBody Login loginBody) { public Respose<String> login(@Valid @RequestBody Login loginBody) {
User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", loginBody.getUsername())); UserModel user = userMapper.selectOne(new QueryWrapper<UserModel>().eq("username", loginBody.getUsername()));
if (user != null && BCrypt.checkpw(loginBody.getPassword(), user.getPassword())) { if (user != null && BCrypt.checkpw(loginBody.getPassword(), user.getPassword())) {
return Respose.builder(PanStpUtil.login(user.getId())); return Respose.builder(PanStpUtil.login(user.getId()));
} }
@ -42,7 +42,7 @@ public class AuthController {
@Operation(summary = "获取当前登陆用户的信息") @Operation(summary = "获取当前登陆用户的信息")
@SecurityRequirement(name = "Authorization-Bearer-Token") @SecurityRequirement(name = "Authorization-Bearer-Token")
@GetMapping("/who-is-me") @GetMapping("/who-is-me")
public Respose<User> whoIsMe() { public Respose<UserModel> whoIsMe() {
return Respose.builder().setData(userMapper.selectById(PanStpUtil.getLoginId()).setPasswordNull()); return Respose.builder().setData(userMapper.selectById(PanStpUtil.getLoginId()).setPasswordNull());
} }

View File

@ -0,0 +1,50 @@
package com.mingliqiye.disk.controller;
import com.mingliqiye.disk.dto.file.FileChunk;
import com.mingliqiye.disk.http.Respose;
import com.mingliqiye.disk.service.FileService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.SchemaProperty;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/apis/file")
public class FileController {
private final FileService fileService;
public FileController(FileService fileService) {
this.fileService = fileService;
}
@RequestBody(content = @Content(mediaType = "multipart/form-data", schema = @Schema(type = "object"), schemaProperties = {
@SchemaProperty(name = "file", schema = @Schema(type = "string", format = "binary"))}))
@Operation(summary = "上传文件")
@PostMapping("upload")
public Respose<Object> uploadMulti(
@RequestParam("chunkNumber") Integer chunkNumber,
@RequestParam("totalChunks") Integer totalChunks,
@RequestParam("fileId") String fileId,
@RequestParam("filename") String filename,
@RequestParam("file") MultipartFile file
) {
FileChunk chunk = new FileChunk();
chunk.setFileId(fileId);
chunk.setChunkNumber(chunkNumber);
chunk.setTotalChunks(totalChunks);
chunk.setFilename(filename);
chunk.setFile(file);
chunk.setChunkSize(file.getSize());
fileService.uploadChunk(chunk);
return Respose.builder();
}
}

View File

@ -0,0 +1,22 @@
package com.mingliqiye.disk.dto.file;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class FileChunk {
private String fileId;
private Integer chunkNumber;
private Long chunkSize;
private Long totalSize;
private Integer totalChunks;
private String filename;
private MultipartFile file;
}

View File

@ -0,0 +1,12 @@
package com.mingliqiye.disk.dto.file;
import lombok.Data;
@Data
public class UploadProgress {
private String fileId;
private int uploadedChunks;
private int totalChunks;
private double progress;
}

View File

@ -2,8 +2,9 @@ package com.mingliqiye.disk.mappers;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mingliqiye.disk.cache.MyBatisRedisCache; import com.mingliqiye.disk.cache.MyBatisRedisCache;
import com.mingliqiye.disk.model.User; import com.mingliqiye.disk.model.UserModel;
import org.apache.ibatis.annotations.CacheNamespace; import org.apache.ibatis.annotations.CacheNamespace;
@CacheNamespace(implementation = MyBatisRedisCache.class) @CacheNamespace(implementation = MyBatisRedisCache.class)
public interface UserMapper extends BaseMapper<User> {} public interface UserMapper extends BaseMapper<UserModel> {
}

View File

@ -0,0 +1,22 @@
package com.mingliqiye.disk.model;
import com.baomidou.mybatisplus.annotation.TableName;
import com.mingliqiye.disk.uuid.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("`blob`")
public class BlobModel {
private UUID id;
private String name;
private String sha256;
private Long size;
private UUID father;
}

View File

@ -0,0 +1,27 @@
package com.mingliqiye.disk.model;
import com.baomidou.mybatisplus.annotation.TableName;
import com.mingliqiye.disk.uuid.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@TableName("file")
public class FileModel {
private UUID id;
private String path;
private String mimeType;
private String sha256;
private Long size;
private UUID blobId;
private UUID createUserId;
private UUID createTime;
private UUID updateUserId;
private UUID updateTime;
}

View File

@ -6,18 +6,19 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.mingliqiye.disk.time.DateTime; import com.mingliqiye.disk.time.DateTime;
import com.mingliqiye.disk.uuid.UUID; import com.mingliqiye.disk.uuid.UUID;
import java.util.List;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.util.List;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@Builder @Builder
@TableName(value = "users", autoResultMap = true) @TableName(value = "users", autoResultMap = true)
public class User { public class UserModel {
private UUID id; private UUID id;
private String username; private String username;
@ -46,7 +47,7 @@ public class User {
@TableField(fill = FieldFill.INSERT_UPDATE) @TableField(fill = FieldFill.INSERT_UPDATE)
private DateTime updateTime; private DateTime updateTime;
public User setPasswordNull() { public UserModel setPasswordNull() {
setPassword("***"); setPassword("***");
return this; return this;
} }

View File

@ -0,0 +1,47 @@
package com.mingliqiye.disk.repository;
import com.mingliqiye.disk.dto.file.FileChunk;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@Repository
public class RedisFileChunkRepository {
private final RedisTemplate<String, Integer> redisTemplate;
public RedisFileChunkRepository(
RedisTemplate<String, Integer> redisTemplate
) {
this.redisTemplate = redisTemplate;
}
public void saveChunkInfo(FileChunk chunk) {
redisTemplate.opsForSet()
.add("file:" + chunk.getFileId(), chunk.getChunkNumber());
}
public List<Integer> getUploadedChunks(String fileId) {
Set<Integer> members =
redisTemplate.opsForSet().members("file:" + fileId);
return members != null ? new ArrayList<>(members) : new ArrayList<>();
}
public int getUploadedChunksCount(String fileId) {
Long count = redisTemplate.opsForSet().size("file:" + fileId);
return count != null ? count.intValue() : 0;
}
public boolean existsChunk(String fileId, Integer chunkNumber) {
Boolean exists =
redisTemplate.opsForSet().isMember("file:" + fileId, chunkNumber);
return exists != null && exists;
}
public void deleteByFileId(String fileId) {
redisTemplate.delete("file:" + fileId);
}
}

View File

@ -0,0 +1,12 @@
package com.mingliqiye.disk.service;
import com.mingliqiye.disk.dto.file.FileChunk;
import org.springframework.stereotype.Service;
@Service
public class FileService {
public void uploadChunk(FileChunk fileChunk){
}
}

View File

@ -1,7 +1,9 @@
config: config:
port: 9963 port: 9963
app-name: pan-disk app-name: pan-disk
app-version: 1.1 app-version: 0.1
jwt-secret-key: pzmqljgvdyidabacksmoaghgyikxoasckjhgyuhij
bolb-path: data/bolb
data-source: data-source:
mysql: mysql:
username: pan username: pan
@ -54,6 +56,11 @@ spring:
web: web:
resources: resources:
static-locations: classpath:/html/static static-locations: classpath:/html/static
config:
import:
- optional:file:data/cofing.yaml
- optional:file:data/cofing.yml
- optional:file:data/cofing.properties
server: server:
@ -68,7 +75,7 @@ sa-token:
is-share: false is-share: false
token-style: random-128 token-style: random-128
is-log: false is-log: false
jwt-secret-key: pzmqljgvdyidabacksmoaghgyikxoasckjhgyuhij jwt-secret-key: ${config.jwt-secret-key}
token-prefix: Bearer token-prefix: Bearer
is-read-body: false is-read-body: false
is-read-cookie: false is-read-cookie: false

View File

@ -0,0 +1,12 @@
DROP TABLE IF EXISTS `blob`;
create table `blob`
(
id binary(16) default (uuid_to_bin(
uuid(),
1)) primary key,
name varchar(256) unique,
`path` varchar(256) unique,
sha256 varchar(96) unique,
`size` bigint,
father binary(16)
)

View File

@ -1,3 +1,3 @@
create database meven_repository collate utf8mb4_unicode_ci; create database pan collate utf8mb4_unicode_ci;
create user 'meven_repository_admin'@'%' IDENTIFIED BY 'meven_repository_admin_password'; create user 'pan'@'%' IDENTIFIED BY 'PhXCRiCfEmiGesEm';
GRANT ALL ON meven_repository.* TO 'meven_repository_admin'@'%' GRANT ALL ON meven_repository.* TO 'pan'@'%'

View File

@ -0,0 +1,19 @@
DROP TABLE IF EXISTS `file`;
create table `file`
(
id binary(16) default (uuid_to_bin(
uuid(),
1)) primary key,
path varchar(767) unique,
mime_type varchar(100),
sha256 varchar(96),
blob_id binary(16) null,
create_user_id binary(16) not null,
create_time timestamp(6) default now(6) not null,
update_user_id binary(16) null,
update_time timestamp(6) default now(6),
constraint foreign key (create_user_id) references users (id),
constraint foreign key (update_user_id) references users (id),
constraint foreign key (blob_id) references `blob` (id),
index (mime_type)
)

View File

@ -201,6 +201,7 @@ html.dark {
height: 100%; height: 100%;
position: relative; position: relative;
--atc-color: rgba(255, 255, 255, 0); --atc-color: rgba(255, 255, 255, 0);
margin-right: 10px;
&:hover { &:hover {
--atc-color: #ffd56f; --atc-color: #ffd56f;