diff --git a/application.yaml b/application.yaml deleted file mode 100644 index 4a17928..0000000 --- a/application.yaml +++ /dev/null @@ -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 diff --git a/build.gradle.kts b/build.gradle.kts index 58642e2..ddd556f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,7 +22,8 @@ version = VERSIONS val libDir = rootDir.resolve("build").resolve("libs") 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 { toolchain { languageVersion = JavaLanguageVersion.of(21) @@ -31,7 +32,10 @@ java { allprojects { 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-redis-jackson:1.44.0") 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 { @@ -126,7 +131,8 @@ tasks.bootJar { attributes["Implementation-Version"] = VERSIONS attributes["Email"] = "minglipro@163.com" 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["LICENSE"] = "Apache License 2.0" attributes["Created"] = "2025-06-26 09:13:51" diff --git a/src/main/java/com/mingliqiye/disk/DiskApplication.java b/src/main/java/com/mingliqiye/disk/DiskApplication.java index cc632d6..d9ad0eb 100644 --- a/src/main/java/com/mingliqiye/disk/DiskApplication.java +++ b/src/main/java/com/mingliqiye/disk/DiskApplication.java @@ -1,16 +1,33 @@ package com.mingliqiye.disk; +import com.mingliqiye.disk.configuration.Config; +import jakarta.annotation.PostConstruct; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; +import java.io.File; + @SpringBootApplication @ConfigurationPropertiesScan @MapperScan("com.mingliqiye.disk.mappers") public class DiskApplication { + private final Config config; + + public DiskApplication(Config config) { + this.config = config; + } public static void main(String[] args) { SpringApplication.run(DiskApplication.class, args); } + + @PostConstruct + void init() { + File bolbFileDir = new File(config.getBolbPath()); + if (!bolbFileDir.exists()) { + bolbFileDir.mkdirs(); + } + } } diff --git a/src/main/java/com/mingliqiye/disk/config/AsyncConfig.java b/src/main/java/com/mingliqiye/disk/config/AsyncConfig.java new file mode 100644 index 0000000..5f87653 --- /dev/null +++ b/src/main/java/com/mingliqiye/disk/config/AsyncConfig.java @@ -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; + } +} diff --git a/src/main/java/com/mingliqiye/disk/config/RedisConfig.java b/src/main/java/com/mingliqiye/disk/config/RedisConfig.java index e07bfb1..197464e 100644 --- a/src/main/java/com/mingliqiye/disk/config/RedisConfig.java +++ b/src/main/java/com/mingliqiye/disk/config/RedisConfig.java @@ -6,6 +6,7 @@ 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.GenericToStringSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @@ -13,11 +14,26 @@ public class RedisConfig { @Primary @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + public RedisTemplate redisTemplateObj( + RedisConnectionFactory connectionFactory + ) { RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } + + @Bean + public RedisTemplate redisTemplateInt( + RedisConnectionFactory connectionFactory + ) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer( + new GenericToStringSerializer<>(Integer.class)); + template.afterPropertiesSet(); + return template; + } } diff --git a/src/main/java/com/mingliqiye/disk/config/StpInterfaceImpl.java b/src/main/java/com/mingliqiye/disk/config/StpInterfaceImpl.java index fe97aeb..50c0284 100644 --- a/src/main/java/com/mingliqiye/disk/config/StpInterfaceImpl.java +++ b/src/main/java/com/mingliqiye/disk/config/StpInterfaceImpl.java @@ -2,13 +2,14 @@ package com.mingliqiye.disk.config; import cn.dev33.satoken.stp.StpInterface; 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 java.util.ArrayList; -import java.util.List; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; +import java.util.ArrayList; +import java.util.List; + @Slf4j @Configuration public class StpInterfaceImpl implements StpInterface { @@ -24,10 +25,12 @@ public class StpInterfaceImpl implements StpInterface { */ @Override public List getPermissionList(Object loginId, String loginType) { - User user = userMapper.selectById(UUID.ofString((String) loginId)); + UserModel user = userMapper.selectById(UUID.ofString((String) loginId)); List list = new ArrayList<>(user.getPrermissions()); - if (user.isAdmin()) list.add("*.*.*"); - user.getRoles().forEach(i -> list.add(String.format("%s.*.*", i))); + if (user.isAdmin()) { + list.add("*.*.*"); + } + user.getRoles().forEach((i) -> list.add(String.format("%s.*.*", i))); log.info(String.valueOf(list)); return list; } diff --git a/src/main/java/com/mingliqiye/disk/configuration/Config.java b/src/main/java/com/mingliqiye/disk/configuration/Config.java index efe4085..12fc539 100644 --- a/src/main/java/com/mingliqiye/disk/configuration/Config.java +++ b/src/main/java/com/mingliqiye/disk/configuration/Config.java @@ -10,5 +10,7 @@ public class Config { private String appName; private String appVersion; private Integer port; + private String bolbPath; + private String jwtSecretKey; private DataSource dataSource; } diff --git a/src/main/java/com/mingliqiye/disk/controller/AuthController.java b/src/main/java/com/mingliqiye/disk/controller/AuthController.java index 97fa8c7..ca4ff01 100644 --- a/src/main/java/com/mingliqiye/disk/controller/AuthController.java +++ b/src/main/java/com/mingliqiye/disk/controller/AuthController.java @@ -7,7 +7,7 @@ import com.mingliqiye.disk.dto.auth.Login; import com.mingliqiye.disk.exception.InternalServerException; import com.mingliqiye.disk.http.Respose; 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 io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; @@ -32,7 +32,7 @@ public class AuthController { @Operation(summary = "登陆") @PostMapping("/login") public Respose login(@Valid @RequestBody Login loginBody) { - User user = userMapper.selectOne(new QueryWrapper().eq("username", loginBody.getUsername())); + UserModel user = userMapper.selectOne(new QueryWrapper().eq("username", loginBody.getUsername())); if (user != null && BCrypt.checkpw(loginBody.getPassword(), user.getPassword())) { return Respose.builder(PanStpUtil.login(user.getId())); } @@ -42,7 +42,7 @@ public class AuthController { @Operation(summary = "获取当前登陆用户的信息") @SecurityRequirement(name = "Authorization-Bearer-Token") @GetMapping("/who-is-me") - public Respose whoIsMe() { + public Respose whoIsMe() { return Respose.builder().setData(userMapper.selectById(PanStpUtil.getLoginId()).setPasswordNull()); } diff --git a/src/main/java/com/mingliqiye/disk/controller/FileController.java b/src/main/java/com/mingliqiye/disk/controller/FileController.java new file mode 100644 index 0000000..c12b35d --- /dev/null +++ b/src/main/java/com/mingliqiye/disk/controller/FileController.java @@ -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 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(); + } +} diff --git a/src/main/java/com/mingliqiye/disk/dto/file/FileChunk.java b/src/main/java/com/mingliqiye/disk/dto/file/FileChunk.java new file mode 100644 index 0000000..309b425 --- /dev/null +++ b/src/main/java/com/mingliqiye/disk/dto/file/FileChunk.java @@ -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; +} diff --git a/src/main/java/com/mingliqiye/disk/dto/file/UploadProgress.java b/src/main/java/com/mingliqiye/disk/dto/file/UploadProgress.java new file mode 100644 index 0000000..10a2d6a --- /dev/null +++ b/src/main/java/com/mingliqiye/disk/dto/file/UploadProgress.java @@ -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; +} diff --git a/src/main/java/com/mingliqiye/disk/mappers/UserMapper.java b/src/main/java/com/mingliqiye/disk/mappers/UserMapper.java index 8e5d6c9..533503d 100644 --- a/src/main/java/com/mingliqiye/disk/mappers/UserMapper.java +++ b/src/main/java/com/mingliqiye/disk/mappers/UserMapper.java @@ -2,8 +2,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 com.mingliqiye.disk.model.UserModel; import org.apache.ibatis.annotations.CacheNamespace; @CacheNamespace(implementation = MyBatisRedisCache.class) -public interface UserMapper extends BaseMapper {} +public interface UserMapper extends BaseMapper { +} diff --git a/src/main/java/com/mingliqiye/disk/model/BlobModel.java b/src/main/java/com/mingliqiye/disk/model/BlobModel.java new file mode 100644 index 0000000..8c19988 --- /dev/null +++ b/src/main/java/com/mingliqiye/disk/model/BlobModel.java @@ -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; +} diff --git a/src/main/java/com/mingliqiye/disk/model/FileModel.java b/src/main/java/com/mingliqiye/disk/model/FileModel.java new file mode 100644 index 0000000..38b7193 --- /dev/null +++ b/src/main/java/com/mingliqiye/disk/model/FileModel.java @@ -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; +} diff --git a/src/main/java/com/mingliqiye/disk/model/User.java b/src/main/java/com/mingliqiye/disk/model/UserModel.java similarity index 95% rename from src/main/java/com/mingliqiye/disk/model/User.java rename to src/main/java/com/mingliqiye/disk/model/UserModel.java index 0b04e39..196cfc9 100644 --- a/src/main/java/com/mingliqiye/disk/model/User.java +++ b/src/main/java/com/mingliqiye/disk/model/UserModel.java @@ -6,18 +6,19 @@ 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; +import java.util.List; + @Data @AllArgsConstructor @NoArgsConstructor @Builder @TableName(value = "users", autoResultMap = true) -public class User { +public class UserModel { private UUID id; private String username; @@ -46,7 +47,7 @@ public class User { @TableField(fill = FieldFill.INSERT_UPDATE) private DateTime updateTime; - public User setPasswordNull() { + public UserModel setPasswordNull() { setPassword("***"); return this; } diff --git a/src/main/java/com/mingliqiye/disk/repository/RedisFileChunkRepository.java b/src/main/java/com/mingliqiye/disk/repository/RedisFileChunkRepository.java new file mode 100644 index 0000000..9037847 --- /dev/null +++ b/src/main/java/com/mingliqiye/disk/repository/RedisFileChunkRepository.java @@ -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 redisTemplate; + + public RedisFileChunkRepository( + RedisTemplate redisTemplate + ) { + this.redisTemplate = redisTemplate; + } + + public void saveChunkInfo(FileChunk chunk) { + redisTemplate.opsForSet() + .add("file:" + chunk.getFileId(), chunk.getChunkNumber()); + } + + public List getUploadedChunks(String fileId) { + Set 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); + } +} diff --git a/src/main/java/com/mingliqiye/disk/service/FileService.java b/src/main/java/com/mingliqiye/disk/service/FileService.java new file mode 100644 index 0000000..1ff9ca5 --- /dev/null +++ b/src/main/java/com/mingliqiye/disk/service/FileService.java @@ -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){ + + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index fe77836..874a899 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,7 +1,9 @@ config: port: 9963 app-name: pan-disk - app-version: 1.1 + app-version: 0.1 + jwt-secret-key: pzmqljgvdyidabacksmoaghgyikxoasckjhgyuhij + bolb-path: data/bolb data-source: mysql: username: pan @@ -54,6 +56,11 @@ spring: web: resources: static-locations: classpath:/html/static + config: + import: + - optional:file:data/cofing.yaml + - optional:file:data/cofing.yml + - optional:file:data/cofing.properties server: @@ -68,7 +75,7 @@ sa-token: is-share: false token-style: random-128 is-log: false - jwt-secret-key: pzmqljgvdyidabacksmoaghgyikxoasckjhgyuhij + jwt-secret-key: ${config.jwt-secret-key} token-prefix: Bearer is-read-body: false is-read-cookie: false diff --git a/src/main/resources/sql/blob.sql b/src/main/resources/sql/blob.sql new file mode 100644 index 0000000..74cd412 --- /dev/null +++ b/src/main/resources/sql/blob.sql @@ -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) +) diff --git a/src/main/resources/sql/database.sql b/src/main/resources/sql/database.sql index 3a21b7b..52a80cd 100644 --- a/src/main/resources/sql/database.sql +++ b/src/main/resources/sql/database.sql @@ -1,3 +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'@'%' \ No newline at end of file +create database pan collate utf8mb4_unicode_ci; +create user 'pan'@'%' IDENTIFIED BY 'PhXCRiCfEmiGesEm'; +GRANT ALL ON meven_repository.* TO 'pan'@'%' diff --git a/src/main/resources/sql/file.sql b/src/main/resources/sql/file.sql new file mode 100644 index 0000000..2df9154 --- /dev/null +++ b/src/main/resources/sql/file.sql @@ -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) +) diff --git a/web-src/layout/Head.vue b/web-src/layout/Head.vue index dd6cfe3..5a1dda8 100644 --- a/web-src/layout/Head.vue +++ b/web-src/layout/Head.vue @@ -201,6 +201,7 @@ html.dark { height: 100%; position: relative; --atc-color: rgba(255, 255, 255, 0); + margin-right: 10px; &:hover { --atc-color: #ffd56f;