commit
78a6dfa230
76 changed files with 7707 additions and 0 deletions
@ -0,0 +1,38 @@ |
|||||
|
target/ |
||||
|
!.mvn/wrapper/maven-wrapper.jar |
||||
|
!**/src/main/**/target/ |
||||
|
!**/src/test/**/target/ |
||||
|
|
||||
|
### IntelliJ IDEA ### |
||||
|
.idea/modules.xml |
||||
|
.idea/jarRepositories.xml |
||||
|
.idea/compiler.xml |
||||
|
.idea/libraries/ |
||||
|
*.iws |
||||
|
*.iml |
||||
|
*.ipr |
||||
|
|
||||
|
### Eclipse ### |
||||
|
.apt_generated |
||||
|
.classpath |
||||
|
.factorypath |
||||
|
.project |
||||
|
.settings |
||||
|
.springBeans |
||||
|
.sts4-cache |
||||
|
|
||||
|
### NetBeans ### |
||||
|
/nbproject/private/ |
||||
|
/nbbuild/ |
||||
|
/dist/ |
||||
|
/nbdist/ |
||||
|
/.nb-gradle/ |
||||
|
build/ |
||||
|
!**/src/main/**/build/ |
||||
|
!**/src/test/**/build/ |
||||
|
|
||||
|
### VS Code ### |
||||
|
.vscode/ |
||||
|
|
||||
|
### Mac OS ### |
||||
|
.DS_Store |
File diff suppressed because it is too large
@ -0,0 +1,256 @@ |
|||||
|
SET NAMES utf8mb4; |
||||
|
SET FOREIGN_KEY_CHECKS = 0; |
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Table structure for account |
||||
|
-- ---------------------------- |
||||
|
DROP TABLE IF EXISTS `account`; |
||||
|
CREATE TABLE `account` ( |
||||
|
`id` bigint NOT NULL COMMENT 'ID', |
||||
|
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名', |
||||
|
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码', |
||||
|
`avatar_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户头像', |
||||
|
`phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '手机号', |
||||
|
`role` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'COMMON' COMMENT '用户角色 COMMON, ADMIN', |
||||
|
`del` tinyint DEFAULT '0' COMMENT '逻辑删除(1删除 0未删除)', |
||||
|
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
||||
|
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
||||
|
PRIMARY KEY (`id`) USING BTREE |
||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户信息表'; |
||||
|
|
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Table structure for account_file |
||||
|
-- ---------------------------- |
||||
|
DROP TABLE IF EXISTS `account_file`; |
||||
|
CREATE TABLE `account_file` ( |
||||
|
`id` bigint NOT NULL COMMENT 'id', |
||||
|
`account_id` bigint DEFAULT NULL COMMENT '用户ID', |
||||
|
`is_dir` int NOT NULL COMMENT '状态 0不是文件夹,1是文件夹', |
||||
|
`parent_id` bigint DEFAULT NULL COMMENT '上层文件夹ID,顶层文件夹为0', |
||||
|
`file_id` bigint DEFAULT NULL COMMENT '文件ID,真正存储的文件', |
||||
|
`file_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '文件名称', |
||||
|
`file_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '文件类型:普通文件common 、压缩文件compress 、 excel 、 word 、 pdf 、 txt 、 图片img 、音频audio 、视频video 、ppt 、源码文件code 、 csv', |
||||
|
`file_suffix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '文件的后缀拓展名', |
||||
|
`file_size` bigint DEFAULT NULL COMMENT '文件大小,字节', |
||||
|
`del` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除(0未删除,1已删除)', |
||||
|
`del_time` datetime DEFAULT NULL COMMENT '删除日期', |
||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
||||
|
PRIMARY KEY (`id`) USING BTREE |
||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户文件表'; |
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Table structure for file |
||||
|
-- ---------------------------- |
||||
|
DROP TABLE IF EXISTS `file`; |
||||
|
CREATE TABLE `file` ( |
||||
|
`id` bigint NOT NULL COMMENT '文件id', |
||||
|
`account_id` bigint DEFAULT NULL COMMENT '用户id,是哪个用户初次上传的', |
||||
|
`file_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '文件名称,秒传需要用到,冗余存储', |
||||
|
`file_suffix` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '文件的后缀拓展名,冗余存储', |
||||
|
`file_size` bigint DEFAULT NULL COMMENT '文件大小,字节,冗余存储', |
||||
|
`object_key` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '文件的key, 格式 日期/md5.拓展名,比如 2024-11-13/921674fd-cdaf-459a-be7b-109469e7050d.png', |
||||
|
`identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '唯一标识,文件MD5', |
||||
|
`del` tinyint NOT NULL DEFAULT '0' COMMENT '逻辑删除(0未删除,1已删除)', |
||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
||||
|
PRIMARY KEY (`id`) USING BTREE |
||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='用户文件表'; |
||||
|
|
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Table structure for file_chunk |
||||
|
-- ---------------------------- |
||||
|
DROP TABLE IF EXISTS `file_chunk`; |
||||
|
CREATE TABLE `file_chunk` ( |
||||
|
`id` bigint NOT NULL, |
||||
|
`identifier` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '文件唯一标识(md5)', |
||||
|
`upload_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分片上传ID', |
||||
|
`file_name` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '文件名', |
||||
|
`bucket_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '所属桶名', |
||||
|
`object_key` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '文件的key', |
||||
|
`total_size` bigint NOT NULL COMMENT '总文件大小(byte)', |
||||
|
`chunk_size` bigint NOT NULL COMMENT '每个分片大小(byte)', |
||||
|
`chunk_num` int NOT NULL COMMENT '分片数量', |
||||
|
`account_id` bigint NOT NULL COMMENT '用户ID', |
||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP, |
||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
||||
|
PRIMARY KEY (`id`) USING BTREE, |
||||
|
UNIQUE KEY `uq_file_identifier` (`identifier`) USING BTREE |
||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='文件分片信息表'; |
||||
|
|
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Table structure for file_suffix |
||||
|
-- ---------------------------- |
||||
|
DROP TABLE IF EXISTS `file_suffix`; |
||||
|
CREATE TABLE `file_suffix` ( |
||||
|
`id` int NOT NULL AUTO_INCREMENT, |
||||
|
`file_suffix` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '文件扩展名', |
||||
|
`file_type_id` int NOT NULL COMMENT '文件类型ID', |
||||
|
PRIMARY KEY (`id`) USING BTREE, |
||||
|
KEY `fk_file_type_id` (`file_type_id`) USING BTREE, |
||||
|
CONSTRAINT `fk_file_type_id` FOREIGN KEY (`file_type_id`) REFERENCES `file_type` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT |
||||
|
) ENGINE=InnoDB AUTO_INCREMENT=80 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='文件分类表'; |
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Records of file_suffix |
||||
|
-- ---------------------------- |
||||
|
BEGIN; |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (1, 'jpg', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (2, 'jpeg', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (3, 'png', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (4, 'gif', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (5, 'bmp', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (6, 'tiff', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (7, 'svg', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (8, 'ico', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (9, 'webp', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (10, 'heic', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (11, 'psd', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (12, 'ai', 1); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (13, 'mp4', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (14, 'avi', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (15, 'mkv', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (16, 'flv', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (17, 'mov', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (18, 'wmv', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (19, 'mpeg', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (20, 'rmvb', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (21, '3gp', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (22, 'webm', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (23, 'm4v', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (24, 'ts', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (25, 'vob', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (26, 'm2ts', 2); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (27, 'mp3', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (28, 'wav', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (29, 'flac', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (30, 'aac', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (31, 'ogg', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (32, 'wma', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (33, 'm4a', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (34, 'mid', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (35, 'aiff', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (36, 'alac', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (37, 'pcm', 3); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (38, 'doc', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (39, 'docx', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (40, 'pdf', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (41, 'txt', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (42, 'ppt', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (43, 'pptx', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (44, 'xls', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (45, 'xlsx', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (46, 'odt', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (47, 'rtf', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (48, 'csv', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (49, 'md', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (50, 'epub', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (51, 'mobi', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (52, 'tex', 4); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (53, 'zip', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (54, 'rar', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (55, '7z', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (56, 'tar', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (57, 'gz', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (58, 'bz2', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (59, 'xz', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (60, 'iso', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (61, 'z', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (62, 'tgz', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (63, 'dmg', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (64, 'cbr', 5); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (65, 'exe', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (66, 'bat', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (67, 'sh', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (68, 'apk', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (69, 'iso', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (70, 'bin', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (71, 'torrent', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (72, 'bak', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (73, 'dll', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (74, 'deb', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (75, 'rpm', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (76, 'msi', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (77, 'vmdk', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (78, 'vdi', 6); |
||||
|
INSERT INTO `file_suffix` (`id`, `file_suffix`, `file_type_id`) VALUES (79, 'qcow2', 6); |
||||
|
COMMIT; |
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Table structure for file_type |
||||
|
-- ---------------------------- |
||||
|
DROP TABLE IF EXISTS `file_type`; |
||||
|
CREATE TABLE `file_type` ( |
||||
|
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', |
||||
|
`file_type_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '文件类型名', |
||||
|
PRIMARY KEY (`id`) USING BTREE |
||||
|
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='文件类型表'; |
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Records of file_type |
||||
|
-- ---------------------------- |
||||
|
BEGIN; |
||||
|
INSERT INTO `file_type` (`id`, `file_type_name`) VALUES (1, '图片'); |
||||
|
INSERT INTO `file_type` (`id`, `file_type_name`) VALUES (2, '视频'); |
||||
|
INSERT INTO `file_type` (`id`, `file_type_name`) VALUES (3, '音频'); |
||||
|
INSERT INTO `file_type` (`id`, `file_type_name`) VALUES (4, '文档'); |
||||
|
INSERT INTO `file_type` (`id`, `file_type_name`) VALUES (5, '压缩'); |
||||
|
INSERT INTO `file_type` (`id`, `file_type_name`) VALUES (6, '其他'); |
||||
|
INSERT INTO `file_type` (`id`, `file_type_name`) VALUES (7, '全部'); |
||||
|
COMMIT; |
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Table structure for share |
||||
|
-- ---------------------------- |
||||
|
DROP TABLE IF EXISTS `share`; |
||||
|
CREATE TABLE `share` ( |
||||
|
`id` bigint NOT NULL COMMENT '分享id', |
||||
|
`share_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '分享名称', |
||||
|
`share_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '0' COMMENT '分享类型(no_code没有提取码 ,need_code有提取码)', |
||||
|
`share_day_type` int NOT NULL DEFAULT '0' COMMENT '分享类型(0 永久有效;1: 7天有效;2: 30天有效)', |
||||
|
`share_day` int NOT NULL DEFAULT '0' COMMENT '分享有效天数(永久有效为0)', |
||||
|
`share_end_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '分享结束时间', |
||||
|
`share_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '分享链接地址', |
||||
|
`share_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '分享提取码', |
||||
|
`share_status` varchar(255) COLLATE utf8mb4_bin NOT NULL DEFAULT '0' COMMENT '分享状态 used正常, expired已失效, cancled取消', |
||||
|
`account_id` bigint NOT NULL COMMENT '分享创建人', |
||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
||||
|
PRIMARY KEY (`id`) USING BTREE, |
||||
|
UNIQUE KEY `uk_create_user_time` (`account_id`,`gmt_create`) USING BTREE COMMENT '创建人、创建时间唯一索引' |
||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='用户分享表'; |
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Table structure for share_file |
||||
|
-- ---------------------------- |
||||
|
DROP TABLE IF EXISTS `share_file`; |
||||
|
CREATE TABLE `share_file` ( |
||||
|
`id` bigint NOT NULL COMMENT '主键ID', |
||||
|
`share_id` bigint NOT NULL COMMENT '分享id', |
||||
|
`account_file_id` bigint NOT NULL COMMENT '用户文件的ID', |
||||
|
`account_id` bigint NOT NULL COMMENT '创建者id', |
||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '分享时间', |
||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
||||
|
PRIMARY KEY (`id`) USING BTREE |
||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='文件分享表'; |
||||
|
|
||||
|
-- ---------------------------- |
||||
|
-- Table structure for storage |
||||
|
-- ---------------------------- |
||||
|
DROP TABLE IF EXISTS `storage`; |
||||
|
CREATE TABLE `storage` ( |
||||
|
`id` bigint NOT NULL, |
||||
|
`account_id` bigint DEFAULT NULL COMMENT '所属用户', |
||||
|
`used_size` bigint DEFAULT NULL COMMENT '占用存储大小', |
||||
|
`total_size` bigint DEFAULT NULL COMMENT '总容量大小,字节存储', |
||||
|
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', |
||||
|
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
||||
|
PRIMARY KEY (`id`) USING BTREE, |
||||
|
UNIQUE KEY `userid_index` (`account_id`) USING BTREE |
||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC COMMENT='存储信息表'; |
||||
|
|
||||
|
|
||||
|
SET FOREIGN_KEY_CHECKS = 1; |
@ -0,0 +1,149 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
<parent> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-parent</artifactId> |
||||
|
<version>3.2.4</version> |
||||
|
</parent> |
||||
|
|
||||
|
<groupId>org.ycloud.aipan</groupId> |
||||
|
<artifactId>ycloud-aipan</artifactId> |
||||
|
<version>1.0-SNAPSHOT</version> |
||||
|
<description>智能云盘</description> |
||||
|
|
||||
|
<properties> |
||||
|
<java.version>21</java.version> |
||||
|
<aws-java-sdk-s3.version>1.12.730</aws-java-sdk-s3.version> |
||||
|
<mybatisplus.version>3.5.6</mybatisplus.version> |
||||
|
<hutool-all.version>5.8.27</hutool-all.version> |
||||
|
<common-io.version>2.8.0</common-io.version> |
||||
|
<fastjson.version>2.0.42</fastjson.version> |
||||
|
<mysql.version>8.0.27</mysql.version> |
||||
|
</properties> |
||||
|
<dependencies> |
||||
|
<!-- Spring Boot Web依赖 --> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-web</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- 切面 --> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-aop</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- 数据库连接 --> |
||||
|
<dependency> |
||||
|
<groupId>mysql</groupId> |
||||
|
<artifactId>mysql-connector-java</artifactId> |
||||
|
<version>${mysql.version}</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- Lombok依赖 --> |
||||
|
<dependency> |
||||
|
<groupId>org.projectlombok</groupId> |
||||
|
<artifactId>lombok</artifactId> |
||||
|
<version>1.18.30</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- 测试依赖 --> |
||||
|
<dependency> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-starter-test</artifactId> |
||||
|
<scope>test</scope> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- AWS S3 SDK --> |
||||
|
<dependency> |
||||
|
<groupId>com.amazonaws</groupId> |
||||
|
<artifactId>aws-java-sdk-s3</artifactId> |
||||
|
<version>${aws-java-sdk-s3.version}</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- JWT支持 --> |
||||
|
<dependency> |
||||
|
<groupId>io.jsonwebtoken</groupId> |
||||
|
<artifactId>jjwt</artifactId> |
||||
|
<version>0.12.3</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- MyBatis-Plus依赖 --> |
||||
|
<dependency> |
||||
|
<groupId>com.baomidou</groupId> |
||||
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId> |
||||
|
<version>${mybatisplus.version}</version> |
||||
|
</dependency> |
||||
|
<!-- 代码自动生成依赖 begin --> |
||||
|
<dependency> |
||||
|
<groupId>com.baomidou</groupId> |
||||
|
<artifactId>mybatis-plus-generator</artifactId> |
||||
|
<version>${mybatisplus.version}</version> |
||||
|
</dependency> |
||||
|
<!-- velocity --> |
||||
|
<dependency> |
||||
|
<groupId>org.apache.velocity</groupId> |
||||
|
<artifactId>velocity-engine-core</artifactId> |
||||
|
<version>2.0</version> |
||||
|
</dependency> |
||||
|
<!-- 代码自动生成依赖 end--> |
||||
|
|
||||
|
<!-- Hutool依赖 --> |
||||
|
<dependency> |
||||
|
<groupId>cn.hutool</groupId> |
||||
|
<artifactId>hutool-all</artifactId> |
||||
|
<version>${hutool-all.version}</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- Fastjson依赖 --> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba</groupId> |
||||
|
<artifactId>fastjson</artifactId> |
||||
|
<version>${fastjson.version}</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- knife4j 依赖,接口文档工具 --> |
||||
|
<dependency> |
||||
|
<groupId>com.github.xiaoymin</groupId> |
||||
|
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> |
||||
|
<version>4.4.0</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- AWS S3 SDK --> |
||||
|
<dependency> |
||||
|
<groupId>com.amazonaws</groupId> |
||||
|
<artifactId>aws-java-sdk-s3</artifactId> |
||||
|
<version>${aws-java-sdk-s3.version}</version> |
||||
|
</dependency> |
||||
|
</dependencies> |
||||
|
|
||||
|
<build> |
||||
|
<plugins> |
||||
|
<plugin> |
||||
|
<groupId>org.springframework.boot</groupId> |
||||
|
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
|
</plugin> |
||||
|
|
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||
|
<version>3.1</version> |
||||
|
<configuration> |
||||
|
<source>${java.version}</source> |
||||
|
<target>${java.version}</target> |
||||
|
</configuration> |
||||
|
</plugin> |
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-surefire-plugin</artifactId> |
||||
|
<version>2.19.1</version> |
||||
|
<configuration> |
||||
|
<skipTests>true</skipTests> |
||||
|
</configuration> |
||||
|
</plugin> |
||||
|
</plugins> |
||||
|
</build> |
||||
|
</project> |
@ -0,0 +1,36 @@ |
|||||
|
package org.ycloud.aipan; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.mybatis.spring.annotation.MapperScan; |
||||
|
import org.springframework.boot.SpringApplication; |
||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
|
import org.springframework.context.ConfigurableApplicationContext; |
||||
|
import org.springframework.core.env.Environment; |
||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement; |
||||
|
|
||||
|
import java.net.InetAddress; |
||||
|
|
||||
|
|
||||
|
@Slf4j |
||||
|
@SpringBootApplication |
||||
|
@EnableTransactionManagement |
||||
|
@MapperScan("org.ycloud.aipan.mapper") |
||||
|
public class CloudApplication { |
||||
|
|
||||
|
public static void main(String[] args) throws Exception { |
||||
|
ConfigurableApplicationContext application = SpringApplication.run(CloudApplication.class, args); |
||||
|
Environment env = application.getEnvironment(); |
||||
|
log.info("\n----------------------------------------------------------\n\t" + |
||||
|
"Application '{}' is running! Access URLs:\n\t" + |
||||
|
"Local: \t\thttp://localhost:{}\n\t" + |
||||
|
"External: \thttp://{}:{}\n\t" + |
||||
|
"API文档: \thttp://{}:{}/doc.html\n" + |
||||
|
"----------------------------------------------------------", |
||||
|
env.getProperty("spring.application.name"), |
||||
|
env.getProperty("server.port"), |
||||
|
InetAddress.getLocalHost().getHostAddress(), |
||||
|
env.getProperty("server.port"), |
||||
|
InetAddress.getLocalHost().getHostAddress(), |
||||
|
env.getProperty("server.port")); |
||||
|
} |
||||
|
} |
@ -0,0 +1,68 @@ |
|||||
|
package org.ycloud.aipan.component; |
||||
|
|
||||
|
import com.amazonaws.services.s3.model.Bucket; |
||||
|
import com.amazonaws.services.s3.model.S3ObjectSummary; |
||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
//@Component
|
||||
|
public class LocalFileStoreEngine implements StoreEngine{ |
||||
|
@Override |
||||
|
public boolean bucketExists(String bucketName) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean removeBucket(String bucketName) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void createBucket(String bucketName) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public List<Bucket> getAllBucket() { |
||||
|
return List.of(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public List<S3ObjectSummary> listObjects(String bucketName) { |
||||
|
return List.of(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean doesObjectExist(String bucketName, String objectKey) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean upload(String bucketName, String objectKey, String localFileName) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean upload(String bucketName, String objectKey, MultipartFile file) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean delete(String bucketName, String objectKey) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getDownloadUrl(String bucketName, String remoteFileName, long timeout, TimeUnit unit) { |
||||
|
return ""; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void download2Response(String bucketName, String objectKey, HttpServletResponse response) { |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,226 @@ |
|||||
|
package org.ycloud.aipan.component; |
||||
|
|
||||
|
import com.amazonaws.services.s3.AmazonS3Client; |
||||
|
import com.amazonaws.services.s3.model.Bucket; |
||||
|
import com.amazonaws.services.s3.model.ObjectMetadata; |
||||
|
import com.amazonaws.services.s3.model.S3Object; |
||||
|
import com.amazonaws.services.s3.model.S3ObjectSummary; |
||||
|
import jakarta.annotation.Resource; |
||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.tomcat.util.http.fileupload.IOUtils; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.io.IOException; |
||||
|
import java.util.Date; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class MinIOFileStoreEngine implements StoreEngine { |
||||
|
|
||||
|
@Resource |
||||
|
private AmazonS3Client amazonS3Client; |
||||
|
|
||||
|
// 缓存 bucket 存在性
|
||||
|
private final Map<String, Boolean> bucketCache = new ConcurrentHashMap<>(); |
||||
|
|
||||
|
/** |
||||
|
* 检查 bucket 是否存在 |
||||
|
* |
||||
|
* @param bucketName bucket 名称 |
||||
|
* @return 如果 bucket 存在返回 true,否则返回 false |
||||
|
*/ |
||||
|
@Override |
||||
|
public boolean bucketExists(String bucketName) { |
||||
|
return bucketCache.computeIfAbsent(bucketName, key -> amazonS3Client.doesBucketExistV2(key)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 bucket |
||||
|
* |
||||
|
* @param bucketName bucket 名称 |
||||
|
* @return 如果删除成功返回 true,否则返回 false |
||||
|
*/ |
||||
|
@Override |
||||
|
public boolean removeBucket(String bucketName) { |
||||
|
if (bucketExists(bucketName)) { |
||||
|
try { |
||||
|
amazonS3Client.deleteBucket(bucketName); |
||||
|
bucketCache.remove(bucketName); |
||||
|
return true; |
||||
|
} catch (Exception e) { |
||||
|
log.error("删除 bucket {} 失败: {}", bucketName, e.getMessage(), e); |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建 bucket |
||||
|
* |
||||
|
* @param bucketName bucket 名称 |
||||
|
*/ |
||||
|
@Override |
||||
|
public void createBucket(String bucketName) { |
||||
|
log.info("创建 bucket: {}", bucketName); |
||||
|
if (!bucketExists(bucketName)) { |
||||
|
try { |
||||
|
amazonS3Client.createBucket(bucketName); |
||||
|
bucketCache.put(bucketName, true); |
||||
|
} catch (Exception e) { |
||||
|
log.error("创建 bucket {} 失败: {}", bucketName, e.getMessage(), e); |
||||
|
} |
||||
|
} else { |
||||
|
log.info("bucket 已存在"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取所有 bucket 列表 |
||||
|
* |
||||
|
* @return bucket 列表 |
||||
|
*/ |
||||
|
@Override |
||||
|
public List<Bucket> getAllBucket() { |
||||
|
return amazonS3Client.listBuckets(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 列出 bucket 中的所有对象 |
||||
|
* |
||||
|
* @param bucketName bucket 名称 |
||||
|
* @return 对象列表 |
||||
|
*/ |
||||
|
@Override |
||||
|
public List<S3ObjectSummary> listObjects(String bucketName) { |
||||
|
if (bucketExists(bucketName)) { |
||||
|
return amazonS3Client.listObjects(bucketName).getObjectSummaries(); |
||||
|
} |
||||
|
return List.of(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查对象是否存在 |
||||
|
* |
||||
|
* @param bucketName bucket 名称 |
||||
|
* @param objectKey 对象键 |
||||
|
* @return 如果对象存在返回 true,否则返回 false |
||||
|
*/ |
||||
|
@Override |
||||
|
public boolean doesObjectExist(String bucketName, String objectKey) { |
||||
|
if (bucketExists(bucketName)) { |
||||
|
return amazonS3Client.doesObjectExist(bucketName, objectKey); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传本地文件到 bucket |
||||
|
* |
||||
|
* @param bucketName bucket 名称 |
||||
|
* @param objectKey 对象键 |
||||
|
* @param localFileName 本地文件路径 |
||||
|
* @return 如果上传成功返回 true,否则返回 false |
||||
|
*/ |
||||
|
@Override |
||||
|
public boolean upload(String bucketName, String objectKey, String localFileName) { |
||||
|
if (bucketExists(bucketName)) { |
||||
|
try { |
||||
|
amazonS3Client.putObject(bucketName, objectKey, new File(localFileName)); |
||||
|
return true; |
||||
|
} catch (Exception e) { |
||||
|
log.error("上传文件到 bucket {} 失败: {}", bucketName, e.getMessage(), e); |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传 MultipartFile 到 bucket |
||||
|
* |
||||
|
* @param bucketName bucket 名称 |
||||
|
* @param objectKey 对象键 |
||||
|
* @param file MultipartFile 对象 |
||||
|
* @return 如果上传成功返回 true,否则返回 false |
||||
|
*/ |
||||
|
@Override |
||||
|
public boolean upload(String bucketName, String objectKey, MultipartFile file) { |
||||
|
if (bucketExists(bucketName)) { |
||||
|
try { |
||||
|
ObjectMetadata objectMetadata = new ObjectMetadata(); |
||||
|
objectMetadata.setContentType(file.getContentType()); |
||||
|
objectMetadata.setContentLength(file.getSize()); |
||||
|
amazonS3Client.putObject(bucketName, objectKey, file.getInputStream(), objectMetadata); |
||||
|
return true; |
||||
|
} catch (Exception e) { |
||||
|
log.error("上传文件到 bucket {} 失败: {}", bucketName, e.getMessage(), e); |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 bucket 中的对象 |
||||
|
* |
||||
|
* @param bucketName bucket 名称 |
||||
|
* @param objectKey 对象键 |
||||
|
* @return 如果删除成功返回 true,否则返回 false |
||||
|
*/ |
||||
|
@Override |
||||
|
public boolean delete(String bucketName, String objectKey) { |
||||
|
if (bucketExists(bucketName)) { |
||||
|
try { |
||||
|
amazonS3Client.deleteObject(bucketName, objectKey); |
||||
|
return true; |
||||
|
} catch (Exception e) { |
||||
|
log.error("删除 bucket {} 中的对象 {} 失败: {}", bucketName, objectKey, e.getMessage(), e); |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取对象的下载 URL |
||||
|
* |
||||
|
* @param bucketName bucket 名称 |
||||
|
* @param objectKey 对象键 |
||||
|
* @param timeout 过期时间 |
||||
|
* @param unit 时间单位 |
||||
|
* @return 下载 URL 字符串 |
||||
|
*/ |
||||
|
@Override |
||||
|
public String getDownloadUrl(String bucketName, String objectKey, long timeout, TimeUnit unit) { |
||||
|
try { |
||||
|
Date expiration = new Date(System.currentTimeMillis() + unit.toMillis(timeout)); |
||||
|
return amazonS3Client.generatePresignedUrl(bucketName, objectKey, expiration).toString(); |
||||
|
} catch (Exception e) { |
||||
|
log.error("生成 bucket {} 中对象 {} 的下载 URL 失败: {}", bucketName, objectKey, e.getMessage(), e); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 下载对象到 HttpServletResponse |
||||
|
* |
||||
|
* @param bucketName bucket 名称 |
||||
|
* @param objectKey 对象键 |
||||
|
* @param response HttpServletResponse 对象 |
||||
|
*/ |
||||
|
@Override |
||||
|
public void download2Response(String bucketName, String objectKey, HttpServletResponse response) { |
||||
|
try (S3Object s3Object = amazonS3Client.getObject(bucketName, objectKey)) { |
||||
|
response.setHeader("Content-Disposition", "attachment;filename=" + objectKey.substring(objectKey.lastIndexOf("/") + 1)); |
||||
|
response.setContentType("application/force-download"); |
||||
|
response.setCharacterEncoding("UTF-8"); |
||||
|
IOUtils.copy(s3Object.getObjectContent(), response.getOutputStream()); |
||||
|
} catch (IOException e) { |
||||
|
log.error("下载 bucket {} 中对象 {} 失败: {}", bucketName, objectKey, e.getMessage(), e); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,68 @@ |
|||||
|
package org.ycloud.aipan.component; |
||||
|
|
||||
|
import com.amazonaws.services.s3.model.Bucket; |
||||
|
import com.amazonaws.services.s3.model.S3ObjectSummary; |
||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
|
||||
|
//@Component
|
||||
|
public class OSSFileStoreEngine implements StoreEngine{ |
||||
|
@Override |
||||
|
public boolean bucketExists(String bucketName) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean removeBucket(String bucketName) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void createBucket(String bucketName) { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public List<Bucket> getAllBucket() { |
||||
|
return List.of(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public List<S3ObjectSummary> listObjects(String bucketName) { |
||||
|
return List.of(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean doesObjectExist(String bucketName, String objectKey) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean upload(String bucketName, String objectKey, String localFileName) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean upload(String bucketName, String objectKey, MultipartFile file) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean delete(String bucketName, String objectKey) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getDownloadUrl(String bucketName, String remoteFileName, long timeout, TimeUnit unit) { |
||||
|
return ""; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void download2Response(String bucketName, String objectKey, HttpServletResponse response) { |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,110 @@ |
|||||
|
package org.ycloud.aipan.component; |
||||
|
|
||||
|
import com.amazonaws.services.s3.model.Bucket; |
||||
|
import com.amazonaws.services.s3.model.S3ObjectSummary; |
||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
public interface StoreEngine { |
||||
|
|
||||
|
/*=====================Bucket相关===========================*/ |
||||
|
|
||||
|
/** |
||||
|
* 检查指定的存储桶是否存在于当前的存储系统中 |
||||
|
* |
||||
|
* @param bucketName 存储桶的名称 |
||||
|
* @return 如果存储桶存在,则返回true;否则返回false |
||||
|
*/ |
||||
|
boolean bucketExists(String bucketName); |
||||
|
|
||||
|
/** |
||||
|
* 删除指定名称的存储桶 |
||||
|
* |
||||
|
* @param bucketName 存储桶的名称 |
||||
|
* @return 如果存储桶删除成功,则返回true;否则返回false |
||||
|
*/ |
||||
|
boolean removeBucket(String bucketName); |
||||
|
|
||||
|
/** |
||||
|
* 创建一个新的存储桶 |
||||
|
* |
||||
|
* @param bucketName 新存储桶的名称 |
||||
|
*/ |
||||
|
void createBucket(String bucketName); |
||||
|
|
||||
|
/** |
||||
|
* 获取当前存储系统中的所有存储桶列表 |
||||
|
* |
||||
|
* @return 包含所有存储桶的列表 |
||||
|
*/ |
||||
|
List<Bucket> getAllBucket(); |
||||
|
|
||||
|
/*===================文件处理相关=============================*/ |
||||
|
|
||||
|
/** |
||||
|
* 列出指定桶中的所有对象 |
||||
|
* |
||||
|
* @param bucketName 桶名称 |
||||
|
* @return 包含桶中所有对象摘要的列表 |
||||
|
*/ |
||||
|
List<S3ObjectSummary> listObjects(String bucketName); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 判断文件是否存在 |
||||
|
*/ |
||||
|
boolean doesObjectExist(String bucketName, String objectKey); |
||||
|
|
||||
|
/** |
||||
|
* 将本地文件上传到指定桶 |
||||
|
* |
||||
|
* @param bucketName 桶名称 |
||||
|
* @param objectKey 上传后对象的名称 |
||||
|
* @param localFileName 本地文件的路径 |
||||
|
* @return 上传是否成功 |
||||
|
*/ |
||||
|
boolean upload(String bucketName, String objectKey, String localFileName); |
||||
|
|
||||
|
/** |
||||
|
* 将multipart文件上传到指定桶 |
||||
|
* |
||||
|
* @param bucketName 桶名称 |
||||
|
* @param objectKey 上传后对象的名称 |
||||
|
* @param file 要上传的multipart文件 |
||||
|
* @return 上传是否成功 |
||||
|
*/ |
||||
|
boolean upload(String bucketName, String objectKey, MultipartFile file); |
||||
|
|
||||
|
/** |
||||
|
* 从指定桶中删除对象 |
||||
|
* |
||||
|
* @param bucketName 桶名称 |
||||
|
* @param objectKey 要删除的对象的名称 |
||||
|
* @return 删除是否成功 |
||||
|
*/ |
||||
|
boolean delete(String bucketName, String objectKey); |
||||
|
|
||||
|
/*===================下载相关=============================*/ |
||||
|
/** |
||||
|
* 获取指定对象的下载URL |
||||
|
* |
||||
|
* @param bucketName 桶名称 |
||||
|
* @param remoteFileName 对象的名称 |
||||
|
* @param timeout URL的有效时长 |
||||
|
* @param unit URL有效时长的时间单位 |
||||
|
* @return 对象的下载URL |
||||
|
*/ |
||||
|
String getDownloadUrl(String bucketName, String remoteFileName, long timeout, TimeUnit unit); |
||||
|
|
||||
|
/** |
||||
|
* 将指定对象下载到HTTP响应中 |
||||
|
* |
||||
|
* @param bucketName 桶名称 |
||||
|
* @param objectKey 对象的名称 |
||||
|
* @param response HTTP响应对象,用于输出下载的对象 |
||||
|
*/ |
||||
|
void download2Response(String bucketName, String objectKey, HttpServletResponse response); |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
package org.ycloud.aipan.config; |
||||
|
|
||||
|
|
||||
|
public class AccountConfig { |
||||
|
|
||||
|
/** |
||||
|
* 账号密码加密的盐 |
||||
|
*/ |
||||
|
public static final String ACCOUNT_SALT = "cn.yuan"; |
||||
|
|
||||
|
/** |
||||
|
* 默认存储空间大小 100MB |
||||
|
*/ |
||||
|
public static final Long DEFAULT_STORAGE_SIZE = 1024 * 1024 * 100L; |
||||
|
|
||||
|
/** |
||||
|
* 根文件夹名称 |
||||
|
*/ |
||||
|
public static final String ROOT_FOLDER_NAME = "全部文件夹"; |
||||
|
|
||||
|
/** |
||||
|
* 根文件夹的父ID |
||||
|
*/ |
||||
|
public static final Long ROOT_PARENT_ID = 0L; |
||||
|
} |
@ -0,0 +1,56 @@ |
|||||
|
package org.ycloud.aipan.config; |
||||
|
|
||||
|
import com.amazonaws.ClientConfiguration; |
||||
|
import com.amazonaws.Protocol; |
||||
|
import com.amazonaws.auth.AWSCredentials; |
||||
|
import com.amazonaws.auth.AWSStaticCredentialsProvider; |
||||
|
import com.amazonaws.auth.BasicAWSCredentials; |
||||
|
import com.amazonaws.client.builder.AwsClientBuilder; |
||||
|
import com.amazonaws.regions.Regions; |
||||
|
import com.amazonaws.services.s3.AmazonS3; |
||||
|
import com.amazonaws.services.s3.AmazonS3ClientBuilder; |
||||
|
import jakarta.annotation.Resource; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
|
||||
|
/** |
||||
|
* 配置类,用于定义Bean并配置Amazon S3客户端 |
||||
|
*/ |
||||
|
@Configuration |
||||
|
public class AmazonS3Config { |
||||
|
|
||||
|
// 注入Minio配置类,用于获取访问密钥和Endpoint等信息
|
||||
|
@Resource |
||||
|
private MinioConfig minioConfig; |
||||
|
|
||||
|
/** |
||||
|
* 创建并配置Amazon S3客户端 |
||||
|
* |
||||
|
* @return AmazonS3 实例,用于与Amazon S3服务进行交互 |
||||
|
*/ |
||||
|
@Bean(name = "amazonS3Client") |
||||
|
public AmazonS3 amazonS3Client() { |
||||
|
// 设置连接时的参数
|
||||
|
ClientConfiguration config = new ClientConfiguration(); |
||||
|
// 设置连接方式为HTTP,可选参数为HTTP和HTTPS
|
||||
|
config.setProtocol(Protocol.HTTP); |
||||
|
// 设置网络访问超时时间
|
||||
|
config.setConnectionTimeout(5000); |
||||
|
config.setUseExpectContinue(true); |
||||
|
|
||||
|
// 使用Minio配置中的访问密钥和秘密密钥创建AWS凭证
|
||||
|
AWSCredentials credentials = new BasicAWSCredentials(minioConfig.getAccessKey(), minioConfig.getAccessSecret()); |
||||
|
|
||||
|
// 设置Endpoint
|
||||
|
AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder |
||||
|
.EndpointConfiguration(minioConfig.getEndpoint(), Regions.US_EAST_1.name()); |
||||
|
|
||||
|
// 使用以上配置创建并返回Amazon S3客户端实例
|
||||
|
return AmazonS3ClientBuilder.standard() |
||||
|
.withClientConfiguration(config) |
||||
|
.withCredentials(new AWSStaticCredentialsProvider(credentials)) |
||||
|
.withEndpointConfiguration(endpointConfiguration) |
||||
|
.withPathStyleAccessEnabled(true).build(); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,29 @@ |
|||||
|
package org.ycloud.aipan.config; |
||||
|
|
||||
|
import jakarta.annotation.Resource; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||
|
import org.ycloud.aipan.interceptor.LoginInterceptor; |
||||
|
|
||||
|
|
||||
|
@Configuration |
||||
|
@Slf4j |
||||
|
public class InterceptorConfig implements WebMvcConfigurer { |
||||
|
|
||||
|
@Resource |
||||
|
private LoginInterceptor loginInterceptor; |
||||
|
|
||||
|
@Override |
||||
|
public void addInterceptors(InterceptorRegistry registry) { |
||||
|
registry.addInterceptor(loginInterceptor) |
||||
|
//添加拦截的路径
|
||||
|
.addPathPatterns("/api/account/*/**","/api/file/*/**","/api/share/*/**") |
||||
|
|
||||
|
//排除不拦截
|
||||
|
.excludePathPatterns("/api/account/*/register","/api/account/*/login","/api/account/*/upload_avatar", |
||||
|
"/api/share/*/check_share_code","/api/share/*/visit","/api/share/*/detail_no_code","/api/share/*/detail_with_code"); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
package org.ycloud.aipan.config; |
||||
|
|
||||
|
import io.swagger.v3.oas.models.OpenAPI; |
||||
|
import io.swagger.v3.oas.models.info.Contact; |
||||
|
import io.swagger.v3.oas.models.info.Info; |
||||
|
import io.swagger.v3.oas.models.info.License; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.context.annotation.Configuration; |
||||
|
|
||||
|
/** |
||||
|
* Knife4j配置 ,默认是下面 |
||||
|
* <p> |
||||
|
* knife4j 访问地址:http://localhost:8080/doc.html
|
||||
|
* Swagger2.0访问地址:http://localhost:8080/swagger-ui.html
|
||||
|
* Swagger3.0访问地址:http://localhost:8080/swagger-ui/index.html
|
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@Configuration |
||||
|
public class Knife4jConfig { |
||||
|
@Bean |
||||
|
public OpenAPI customOpenAPI() { |
||||
|
return new OpenAPI() |
||||
|
.info(new Info() |
||||
|
.title("AI智能云盘系统 API") |
||||
|
.version("1.0-SNAPSHOT") |
||||
|
.description("AI智能云盘系统") |
||||
|
.termsOfService("https://www.xxx.net") |
||||
|
.license(new License().name("Apache 2.0").url("https://www.xxx.net")) |
||||
|
// 添加作者信息
|
||||
|
.contact(new Contact() |
||||
|
.name("anonymity") // 替换为作者的名字
|
||||
|
.email("anonymity@qq.com") // 替换为作者的电子邮件
|
||||
|
.url("https://www.xxx.net") // 替换为作者的网站或个人资料链接
|
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
package org.ycloud.aipan.config; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
@Data |
||||
|
@Component |
||||
|
@ConfigurationProperties(prefix = "minio") |
||||
|
public class MinioConfig { |
||||
|
|
||||
|
@Value("endpoint") |
||||
|
private String endpoint; |
||||
|
|
||||
|
@Value("access-key") |
||||
|
private String accessKey; |
||||
|
|
||||
|
@Value("access-secret") |
||||
|
private String accessSecret; |
||||
|
|
||||
|
@Value("bucket-name") |
||||
|
private String bucketName; |
||||
|
|
||||
|
@Value("avatar-bucket-name") |
||||
|
private String avatarBucketName; |
||||
|
|
||||
|
// 预签名url过期时间(ms)
|
||||
|
private Long PRE_SIGN_URL_EXPIRE = 60 * 10 * 1000L; |
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
package org.ycloud.aipan.controller; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.Operation; |
||||
|
import io.swagger.v3.oas.annotations.Parameter; |
||||
|
import io.swagger.v3.oas.annotations.tags.Tag; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
import org.ycloud.aipan.controller.req.AccountLoginReq; |
||||
|
import org.ycloud.aipan.controller.req.AccountRegisterReq; |
||||
|
import org.ycloud.aipan.dto.AccountDTO; |
||||
|
import org.ycloud.aipan.interceptor.LoginInterceptor; |
||||
|
import org.ycloud.aipan.service.AccountService; |
||||
|
import org.ycloud.aipan.util.JsonData; |
||||
|
import org.ycloud.aipan.util.JwtUtil; |
||||
|
|
||||
|
@RestController |
||||
|
@RequestMapping("/api/account/v1") |
||||
|
@Tag(name = "账户管理接口", description = "账户管理相关的接口") |
||||
|
public class AccountController { |
||||
|
|
||||
|
@Autowired |
||||
|
private AccountService accountService; |
||||
|
|
||||
|
/** |
||||
|
* 注册接口 |
||||
|
*/ |
||||
|
@PostMapping("register") |
||||
|
@Operation(summary = "用户注册", description = "用户注册接口") |
||||
|
public JsonData register(@RequestBody @Parameter(description = "注册请求对象", required = true) AccountRegisterReq req) { |
||||
|
accountService.register(req); |
||||
|
return JsonData.buildSuccess(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 头像上传接口 |
||||
|
*/ |
||||
|
@PostMapping("upload_avatar") |
||||
|
@Operation(summary = "上传头像", description = "上传头像接口") |
||||
|
public JsonData uploadAvatar(@RequestParam("file") @Parameter(description = "上传的头像文件", required = true) MultipartFile file) { |
||||
|
String url = accountService.uploadAvatar(file); |
||||
|
return JsonData.buildSuccess(url); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 登录模块 |
||||
|
*/ |
||||
|
@PostMapping("login") |
||||
|
@Operation(summary = "用户登录", description = "用户登录接口") |
||||
|
public JsonData login(@RequestBody @Parameter(description = "登录请求对象", required = true) AccountLoginReq req) { |
||||
|
AccountDTO accountDTO = accountService.login(req); |
||||
|
//生成token jwt ssm 一般前端存储在localStorage里面,或 sessionStorage里面
|
||||
|
String token = JwtUtil.geneLoginJWT(accountDTO); |
||||
|
return JsonData.buildSuccess(token); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取用户详情接口 |
||||
|
*/ |
||||
|
@GetMapping("detail") |
||||
|
@Operation(summary = "获取用户详情", description = "获取当前登录用户详情接口") |
||||
|
public JsonData detail() { |
||||
|
AccountDTO accountDTO = accountService.queryDetail(LoginInterceptor.threadLocal.get().getId()); |
||||
|
return JsonData.buildSuccess(accountDTO); |
||||
|
} |
||||
|
} |
@ -0,0 +1,97 @@ |
|||||
|
package org.ycloud.aipan.controller; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.Operation; |
||||
|
import io.swagger.v3.oas.annotations.Parameter; |
||||
|
import io.swagger.v3.oas.annotations.tags.Tag; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
import org.ycloud.aipan.controller.req.FileBatchReq; |
||||
|
import org.ycloud.aipan.controller.req.FileUpdateReq; |
||||
|
import org.ycloud.aipan.controller.req.FileUploadReq; |
||||
|
import org.ycloud.aipan.dto.AccountFileDTO; |
||||
|
import org.ycloud.aipan.dto.FolderTreeNodeDTO; |
||||
|
import org.ycloud.aipan.interceptor.LoginInterceptor; |
||||
|
import org.ycloud.aipan.service.AccountFileService; |
||||
|
import org.ycloud.aipan.controller.req.FolderCreateReq; |
||||
|
import org.ycloud.aipan.util.JsonData; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@RestController |
||||
|
@RequestMapping("/api/file/v1") |
||||
|
@Tag(name = "文件管理", description = "文件和文件夹操作相关的接口") |
||||
|
public class FileController { |
||||
|
|
||||
|
@Autowired |
||||
|
private AccountFileService accountFileService; |
||||
|
|
||||
|
/** |
||||
|
* 查询文件列表接口 |
||||
|
*/ |
||||
|
@GetMapping("list") |
||||
|
@Operation(summary = "查询文件列表", description = "根据父文件夹ID查询文件列表") |
||||
|
public JsonData list(@RequestParam(value = "parent_id") Long parentId) { |
||||
|
Long accountId = LoginInterceptor.threadLocal.get().getId(); |
||||
|
List<AccountFileDTO> list = accountFileService.listFile(accountId, parentId); |
||||
|
return JsonData.buildSuccess(list); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建文件夹 |
||||
|
*/ |
||||
|
@PostMapping("create_folder") |
||||
|
@Operation(summary = "创建文件夹", description = "创建一个新的文件夹") |
||||
|
public JsonData createFolder(@RequestBody FolderCreateReq req) { |
||||
|
Long accountId = LoginInterceptor.threadLocal.get().getId(); |
||||
|
req.setAccountId(accountId); |
||||
|
return JsonData.buildSuccess(accountFileService.createFolder(req)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 文件重命名 |
||||
|
*/ |
||||
|
@PostMapping("rename_file") |
||||
|
@Operation(summary = "文件重命名", description = "重命名指定的文件") |
||||
|
public JsonData renameFile(@RequestBody FileUpdateReq req) { |
||||
|
Long accountId = LoginInterceptor.threadLocal.get().getId(); |
||||
|
req.setAccountId(accountId); |
||||
|
accountFileService.renameFile(req); |
||||
|
return JsonData.buildSuccess(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 文件树接口 |
||||
|
*/ |
||||
|
@GetMapping("/folder/tree") |
||||
|
@Operation(summary = "获取文件树", description = "获取文件和文件夹的树形结构") |
||||
|
public JsonData folderTree() { |
||||
|
Long accountId = LoginInterceptor.threadLocal.get().getId(); |
||||
|
List<FolderTreeNodeDTO> list = accountFileService.folderTree(accountId); |
||||
|
return JsonData.buildSuccess(list); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 普通小文件上传接口 |
||||
|
*/ |
||||
|
@PostMapping("upload") |
||||
|
@Operation(summary = "普通小文件上传", description = "普通小文件上传") |
||||
|
public JsonData upload(FileUploadReq req) { |
||||
|
req.setAccountId(LoginInterceptor.threadLocal.get().getId()); |
||||
|
accountFileService.fileUpload(req); |
||||
|
return JsonData.buildSuccess(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 文件批量移动 |
||||
|
*/ |
||||
|
@PostMapping("move_batch") |
||||
|
@Operation(summary = "文件批量移动", description = "文件批量移动") |
||||
|
public JsonData moveBatch( |
||||
|
@Parameter(description = "文件批量移动请求对象", required = true) @RequestBody FileBatchReq req) { |
||||
|
req.setAccountId(LoginInterceptor.threadLocal.get().getId()); |
||||
|
accountFileService.moveBatch(req); |
||||
|
return JsonData.buildSuccess(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
package org.ycloud.aipan.controller.req; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Builder; |
||||
|
import lombok.Data; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
|
||||
|
@Data |
||||
|
@Builder |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@Schema(description = "用户登录请求对象", requiredProperties = {"password", "phone"}) |
||||
|
public class AccountLoginReq { |
||||
|
|
||||
|
/** |
||||
|
* 密码 |
||||
|
*/ |
||||
|
@Schema(description = "密码", example = "123456") |
||||
|
private String password; |
||||
|
|
||||
|
/** |
||||
|
* 手机号 |
||||
|
*/ |
||||
|
@Schema(description = "手机号", example = "15600000000") |
||||
|
private String phone; |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
package org.ycloud.aipan.controller.req; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Builder; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
@Builder |
||||
|
@Schema(description = "用户注册请求对象", requiredProperties = {"username", "password", "phone"}) |
||||
|
public class AccountRegisterReq { |
||||
|
|
||||
|
/** |
||||
|
* 用户名 |
||||
|
*/ |
||||
|
@Schema(description = "用户名", example = "exampleUser") |
||||
|
private String username; |
||||
|
|
||||
|
/** |
||||
|
* 密码 |
||||
|
*/ |
||||
|
@Schema(description = "密码", example = "password123") |
||||
|
private String password; |
||||
|
|
||||
|
/** |
||||
|
* 手机号 |
||||
|
*/ |
||||
|
@Schema(description = "手机号", example = "13800138000") |
||||
|
private String phone; |
||||
|
|
||||
|
/** |
||||
|
* 头像 |
||||
|
*/ |
||||
|
@Schema(description = "头像URL", example = "http://example.com/avatar.jpg") |
||||
|
private String avatarUrl; |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
package org.ycloud.aipan.controller.req; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
@Schema(description = "批量文件操作请求") |
||||
|
public class FileBatchReq { |
||||
|
|
||||
|
@Schema(description = "文件ID列表", example = "[1, 2, 3]") |
||||
|
private List<Long> fileIds; |
||||
|
|
||||
|
@Schema(description = "目标父级ID", example = "100") |
||||
|
private Long targetParentId; |
||||
|
|
||||
|
@Schema(description = "用户ID", example = "12345") |
||||
|
private Long accountId; |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
package org.ycloud.aipan.controller.req; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@Data |
||||
|
@Schema(description = "文件更新请求对象") |
||||
|
public class FileUpdateReq { |
||||
|
|
||||
|
/** |
||||
|
* 用户id |
||||
|
*/ |
||||
|
@Schema(description = "用户ID", example = "12345") |
||||
|
private Long accountId; |
||||
|
|
||||
|
/** |
||||
|
* 文件id |
||||
|
*/ |
||||
|
@Schema(description = "文件ID", example = "67890") |
||||
|
private Long fileId; |
||||
|
|
||||
|
/** |
||||
|
* 新的文件名 |
||||
|
*/ |
||||
|
@Schema(description = "新的文件名", example = "new_filename.txt") |
||||
|
private String newFilename; |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
package org.ycloud.aipan.controller.req; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Data; |
||||
|
import lombok.experimental.Accessors; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
|
||||
|
@Data |
||||
|
@Accessors(chain = true) |
||||
|
@Schema(description = "文件上传请求") |
||||
|
public class FileUploadReq { |
||||
|
|
||||
|
@Schema(description = "文件名", example = "example.txt") |
||||
|
private String filename; |
||||
|
|
||||
|
@Schema(description = "文件唯一标识(MD5)", example = "d41d8cd98f00b204e9800998ecf8427e") |
||||
|
private String identifier; |
||||
|
|
||||
|
@Schema(description = "用户ID", example = "12345") |
||||
|
private Long accountId; |
||||
|
|
||||
|
@Schema(description = "父级目录ID", example = "100") |
||||
|
private Long parentId; |
||||
|
|
||||
|
@Schema(description = "文件大小(字节)", example = "1024") |
||||
|
private Long fileSize; |
||||
|
|
||||
|
@Schema(description = "文件对象") |
||||
|
private MultipartFile file; |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
package org.ycloud.aipan.controller.req; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Builder; |
||||
|
import lombok.Data; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
|
||||
|
@Data |
||||
|
@Builder |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@Schema(description = "文件夹创建请求对象", requiredProperties = {"folderName", "parentId", "accountId"}) |
||||
|
public class FolderCreateReq { |
||||
|
|
||||
|
/** |
||||
|
* 文件夹名称 |
||||
|
*/ |
||||
|
@Schema(description = "文件夹名称", example = "MyFolder") |
||||
|
private String folderName; |
||||
|
|
||||
|
/** |
||||
|
* 上级文件夹ID |
||||
|
*/ |
||||
|
@Schema(description = "上级文件夹ID", example = "12345") |
||||
|
private Long parentId; |
||||
|
|
||||
|
/** |
||||
|
* 用户ID |
||||
|
*/ |
||||
|
@Schema(description = "用户ID", example = "67890") |
||||
|
private Long accountId; |
||||
|
} |
@ -0,0 +1,52 @@ |
|||||
|
package org.ycloud.aipan.dto; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.*; |
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
/** |
||||
|
* 用户信息表 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@Schema(name = "AccountDTO", description = "用户信息") |
||||
|
@Builder |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
public class AccountDTO implements Serializable { |
||||
|
|
||||
|
@Schema(description = "ID", example = "12345") |
||||
|
private Long id; |
||||
|
|
||||
|
@Schema(description = "用户名", example = "exampleUser") |
||||
|
private String username; |
||||
|
|
||||
|
@Schema(description = "用户头像", example = "http://example.com/avatar.jpg") |
||||
|
private String avatarUrl; |
||||
|
|
||||
|
@Schema(description = "手机号", example = "13800138000") |
||||
|
private String phone; |
||||
|
|
||||
|
@Schema(description = "用户角色 (COMMON, ADMIN)", example = "COMMON") |
||||
|
private String role; |
||||
|
|
||||
|
@Schema(description = "逻辑删除(1删除 0未删除)", example = "false") |
||||
|
private Boolean del; |
||||
|
|
||||
|
@Schema(description = "创建时间", example = "2023-10-01T12:34:56Z") |
||||
|
private Date gmtCreate; |
||||
|
|
||||
|
@Schema(description = "更新时间", example = "2023-10-02T12:34:56Z") |
||||
|
private Date gmtModified; |
||||
|
|
||||
|
@Schema(description = "根文件夹ID", example = "67890") |
||||
|
private Long rootFileId; |
||||
|
|
||||
|
@Schema(description = "根文件夹名称", example = "MyRootFolder") |
||||
|
private String rootFileName; |
||||
|
|
||||
|
@Schema(description = "存储信息") |
||||
|
private StorageDTO storageDTO; |
||||
|
} |
@ -0,0 +1,60 @@ |
|||||
|
package org.ycloud.aipan.dto; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.*; |
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
/** |
||||
|
* 用户文件表 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@Builder |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@Schema(name = "AccountFileDO", description = "用户文件表") |
||||
|
public class AccountFileDTO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@Schema(description = "id") |
||||
|
private Long id; |
||||
|
|
||||
|
@Schema(description = "用户ID") |
||||
|
private Long accountId; |
||||
|
|
||||
|
@Schema(description = "状态 0不是文件夹,1是文件夹") |
||||
|
private Integer isDir; |
||||
|
|
||||
|
@Schema(description = "上层文件夹ID,顶层文件夹为0") |
||||
|
private Long parentId; |
||||
|
|
||||
|
@Schema(description = "文件ID,真正存储的文件") |
||||
|
private Long fileId; |
||||
|
|
||||
|
@Schema(description = "文件名称") |
||||
|
private String fileName; |
||||
|
|
||||
|
@Schema(description = "文件类型:普通文件common 、压缩文件compress 、 excel 、 word 、 pdf 、 txt 、 图片img 、音频audio 、视频video 、ppt 、源码文件code 、 csv") |
||||
|
private String fileType; |
||||
|
|
||||
|
@Schema(description = "文件的后缀拓展名") |
||||
|
private String fileSuffix; |
||||
|
|
||||
|
@Schema(description = "文件大小,字节") |
||||
|
private Long fileSize; |
||||
|
|
||||
|
@Schema(description = "逻辑删除(0未删除,1已删除)") |
||||
|
private Boolean del; |
||||
|
|
||||
|
@Schema(description = "删除日期") |
||||
|
private Date delTime; |
||||
|
|
||||
|
@Schema(description = "更新时间") |
||||
|
private Date gmtModified; |
||||
|
|
||||
|
@Schema(description = "创建时间") |
||||
|
private Date gmtCreate; |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
package org.ycloud.aipan.dto; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Builder; |
||||
|
import lombok.Data; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Data |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
@Builder |
||||
|
public class FolderTreeNodeDTO { |
||||
|
|
||||
|
/** |
||||
|
* 文件id |
||||
|
*/ |
||||
|
private Long id ; |
||||
|
|
||||
|
/** |
||||
|
* 父文件ID |
||||
|
*/ |
||||
|
private Long parentId; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 文件名称 |
||||
|
*/ |
||||
|
private String label; |
||||
|
|
||||
|
/** |
||||
|
* 子节点列表 |
||||
|
*/ |
||||
|
private List<FolderTreeNodeDTO> children = new ArrayList<>(); |
||||
|
|
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
package org.ycloud.aipan.dto; |
||||
|
|
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
/** |
||||
|
* 存储信息表 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@Schema(name = "StorageDTO", description = "存储信息表") |
||||
|
public class StorageDTO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@Schema(description = "ID", example = "12345") |
||||
|
private Long id; |
||||
|
|
||||
|
@Schema(description = "所属用户", example = "67890") |
||||
|
private Long accountId; |
||||
|
|
||||
|
@Schema(description = "占用存储大小(字节)", example = "104857600") |
||||
|
private Long usedSize; |
||||
|
|
||||
|
@Schema(description = "总容量大小(字节)", example = "536870912") |
||||
|
private Long totalSize; |
||||
|
|
||||
|
@Schema(description = "创建时间", example = "2023-10-01T12:34:56Z") |
||||
|
private Date gmtCreate; |
||||
|
|
||||
|
@Schema(description = "更新时间", example = "2023-10-02T12:34:56Z") |
||||
|
private Date gmtModified; |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
package org.ycloud.aipan.enums; |
||||
|
|
||||
|
public enum AccountRoleEnum { |
||||
|
|
||||
|
/** |
||||
|
* 普通用户 |
||||
|
*/ |
||||
|
COMMON, |
||||
|
|
||||
|
/** |
||||
|
* 管理员 |
||||
|
*/ |
||||
|
ADMIN; |
||||
|
|
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
package org.ycloud.aipan.enums; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
|
||||
|
@Getter |
||||
|
@AllArgsConstructor |
||||
|
public enum BizCodeEnum { |
||||
|
/** |
||||
|
* 账号 |
||||
|
*/ |
||||
|
ACCOUNT_REPEAT(250001, "账号已经存在"), |
||||
|
ACCOUNT_UNREGISTER(250002, "账号不存在"), |
||||
|
ACCOUNT_PWD_ERROR(250003, "账号或者密码错误"), |
||||
|
ACCOUNT_UNLOGIN(250004, "账号未登录"), |
||||
|
|
||||
|
/** |
||||
|
* 文件操作相关 |
||||
|
*/ |
||||
|
FILE_NOT_EXISTS(220404, "文件不存在"), |
||||
|
FILE_RENAME_REPEAT(220405, "文件名重复"), |
||||
|
FILE_DEL_BATCH_ILLEGAL(220406, "文件删除参数错误"), |
||||
|
FILE_TYPE_ERROR(220407, "文件类型错误"), |
||||
|
FILE_CHUNK_TASK_NOT_EXISTS(230408, "分片任务不存在"), |
||||
|
FILE_CHUNK_NOT_ENOUGH(230409, "分片数量不匹配,合并不够"), |
||||
|
FILE_STORAGE_NOT_ENOUGH(240403, "存储空间不足"), |
||||
|
FILE_TARGET_PARENT_ILLEGAL(250403, "目标父级目录不合法"), |
||||
|
SHARE_CANCEL_ILLEGAL(260403, "取消分享失败,参数不合法"), |
||||
|
SHARE_CODE_ILLEGAL(260404, "分享码不合法"), |
||||
|
SHARE_NOT_EXIST(260405, "分享不存在"), |
||||
|
SHARE_CANCEL(260406, "分享已取消"), |
||||
|
SHARE_EXPIRED(260407, "分享已过期"), |
||||
|
SHARE_FILE_ILLEGAL(260408, "分享的文件不合规"), |
||||
|
FILE_BATCH_UPDATE_ERROR(270101,"文件批量操作错误" ); |
||||
|
private final int code; |
||||
|
private final String message; |
||||
|
|
||||
|
} |
@ -0,0 +1,110 @@ |
|||||
|
package org.ycloud.aipan.enums; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
|
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
@Getter |
||||
|
public enum FileTypeEnum { |
||||
|
COMMON("common"), |
||||
|
COMPRESS("compress"), |
||||
|
EXCEL("excel"), |
||||
|
WORD("word"), |
||||
|
PDF("pdf"), |
||||
|
TXT("txt"), |
||||
|
IMG("img"), |
||||
|
AUDIO("audio"), |
||||
|
VIDEO("video"), |
||||
|
PPT("ppt"), |
||||
|
CODE("code"), |
||||
|
CSV("csv"); |
||||
|
|
||||
|
private final String type; |
||||
|
|
||||
|
private static final Map<String, FileTypeEnum> EXTENSION_MAP = new HashMap<>(); |
||||
|
|
||||
|
static { |
||||
|
for (FileTypeEnum fileType : values()) { |
||||
|
switch (fileType) { |
||||
|
case COMPRESS: |
||||
|
EXTENSION_MAP.put("zip", fileType); |
||||
|
EXTENSION_MAP.put("rar", fileType); |
||||
|
EXTENSION_MAP.put("7z", fileType); |
||||
|
break; |
||||
|
case EXCEL: |
||||
|
EXTENSION_MAP.put("xls", fileType); |
||||
|
EXTENSION_MAP.put("xlsx", fileType); |
||||
|
break; |
||||
|
case WORD: |
||||
|
EXTENSION_MAP.put("doc", fileType); |
||||
|
EXTENSION_MAP.put("docx", fileType); |
||||
|
break; |
||||
|
case PDF: |
||||
|
EXTENSION_MAP.put("pdf", fileType); |
||||
|
break; |
||||
|
case TXT: |
||||
|
EXTENSION_MAP.put("txt", fileType); |
||||
|
break; |
||||
|
case IMG: |
||||
|
EXTENSION_MAP.put("jpg", fileType); |
||||
|
EXTENSION_MAP.put("jpeg", fileType); |
||||
|
EXTENSION_MAP.put("png", fileType); |
||||
|
EXTENSION_MAP.put("gif", fileType); |
||||
|
EXTENSION_MAP.put("bmp", fileType); |
||||
|
break; |
||||
|
case AUDIO: |
||||
|
EXTENSION_MAP.put("mp3", fileType); |
||||
|
EXTENSION_MAP.put("wav", fileType); |
||||
|
EXTENSION_MAP.put("aac", fileType); |
||||
|
break; |
||||
|
case VIDEO: |
||||
|
EXTENSION_MAP.put("mp4", fileType); |
||||
|
EXTENSION_MAP.put("avi", fileType); |
||||
|
EXTENSION_MAP.put("mkv", fileType); |
||||
|
break; |
||||
|
case PPT: |
||||
|
EXTENSION_MAP.put("ppt", fileType); |
||||
|
EXTENSION_MAP.put("pptx", fileType); |
||||
|
break; |
||||
|
case CODE: |
||||
|
EXTENSION_MAP.put("java", fileType); |
||||
|
EXTENSION_MAP.put("c", fileType); |
||||
|
EXTENSION_MAP.put("cpp", fileType); |
||||
|
EXTENSION_MAP.put("py", fileType); |
||||
|
EXTENSION_MAP.put("js", fileType); |
||||
|
EXTENSION_MAP.put("html", fileType); |
||||
|
EXTENSION_MAP.put("css", fileType); |
||||
|
break; |
||||
|
case CSV: |
||||
|
EXTENSION_MAP.put("csv", fileType); |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
FileTypeEnum(String type) { |
||||
|
this.type = type; |
||||
|
} |
||||
|
|
||||
|
public static FileTypeEnum fromExtension(String extension) { |
||||
|
if (extension == null || extension.isEmpty() || !isValidExtension(extension)) { |
||||
|
return COMMON; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
return EXTENSION_MAP.getOrDefault(extension.toLowerCase(), COMMON); |
||||
|
} catch (NullPointerException e) { |
||||
|
// 记录日志
|
||||
|
System.err.println("Unexpected null pointer exception: " + e.getMessage()); |
||||
|
return COMMON; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static boolean isValidExtension(String extension) { |
||||
|
// 确保扩展名只包含字母和数字
|
||||
|
return extension.matches("[a-zA-Z0-9]+"); |
||||
|
} |
||||
|
} |
@ -0,0 +1,22 @@ |
|||||
|
package org.ycloud.aipan.enums; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Getter; |
||||
|
|
||||
|
@AllArgsConstructor |
||||
|
@Getter |
||||
|
public enum FolderFlagEnum { |
||||
|
|
||||
|
/** |
||||
|
* 非文件夹 |
||||
|
*/ |
||||
|
NO(0), |
||||
|
|
||||
|
/** |
||||
|
* 是文件夹 |
||||
|
*/ |
||||
|
YES(1); |
||||
|
|
||||
|
private final Integer code; |
||||
|
|
||||
|
} |
@ -0,0 +1,71 @@ |
|||||
|
package org.ycloud.aipan.exception; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import org.ycloud.aipan.enums.BizCodeEnum; |
||||
|
|
||||
|
/** |
||||
|
* 业务异常类,继承自 RuntimeException |
||||
|
* 用于封装业务逻辑中的异常信息 |
||||
|
*/ |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Data |
||||
|
public class BizException extends RuntimeException { |
||||
|
/** |
||||
|
* 异常代码 |
||||
|
*/ |
||||
|
private int code; |
||||
|
/** |
||||
|
* 异常消息 |
||||
|
*/ |
||||
|
private String msg; |
||||
|
/** |
||||
|
* 异常详细信息 |
||||
|
*/ |
||||
|
private String detail; |
||||
|
/** |
||||
|
* 构造函数,使用自定义的异常代码和消息 |
||||
|
* |
||||
|
* @param code 异常代码 |
||||
|
* @param message 异常消息 |
||||
|
*/ |
||||
|
public BizException(Integer code, String message) { |
||||
|
// 调用父类构造函数,设置异常消息
|
||||
|
super(message); |
||||
|
// 设置异常代码
|
||||
|
this.code = code; |
||||
|
// 设置异常消息
|
||||
|
this.msg = message; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 构造函数,使用 BizCodeEnum 枚举中的异常代码和消息 |
||||
|
* |
||||
|
* @param bizCodeEnum 业务代码枚举 |
||||
|
*/ |
||||
|
public BizException(BizCodeEnum bizCodeEnum) { |
||||
|
// 调用父类构造函数,设置异常消息
|
||||
|
super(bizCodeEnum.getMessage()); |
||||
|
// 设置异常代码
|
||||
|
this.code = bizCodeEnum.getCode(); |
||||
|
// 设置异常消息
|
||||
|
this.msg = bizCodeEnum.getMessage(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 构造函数,使用 BizCodeEnum 枚举中的异常代码和消息,并包含原始异常的详细信息 |
||||
|
* |
||||
|
* @param bizCodeEnum 业务代码枚举 |
||||
|
* @param e 原始异常 |
||||
|
*/ |
||||
|
public BizException(BizCodeEnum bizCodeEnum, Exception e) { |
||||
|
// 调用父类构造函数,设置异常消息
|
||||
|
super(bizCodeEnum.getMessage()); |
||||
|
// 设置异常代码
|
||||
|
this.code = bizCodeEnum.getCode(); |
||||
|
// 设置异常消息
|
||||
|
this.msg = bizCodeEnum.getMessage(); |
||||
|
// 设置异常详细信息
|
||||
|
this.detail = e.toString(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
package org.ycloud.aipan.exception; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.web.bind.annotation.ControllerAdvice; |
||||
|
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||
|
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
import org.ycloud.aipan.util.JsonData; |
||||
|
|
||||
|
/** |
||||
|
* 自定义异常处理器 |
||||
|
* 用于捕获并处理全局异常,返回统一的JSON格式响应 |
||||
|
*/ |
||||
|
@ControllerAdvice |
||||
|
@Slf4j |
||||
|
public class CustomExceptionHandler { |
||||
|
|
||||
|
/** |
||||
|
* 处理所有异常的方法 |
||||
|
* |
||||
|
* @param e 捕获到的异常对象 |
||||
|
* @return JsonData对象,包含错误码和错误信息 |
||||
|
*/ |
||||
|
@ExceptionHandler(value = Exception.class) |
||||
|
@ResponseBody |
||||
|
public JsonData handler(Exception e){ |
||||
|
|
||||
|
// 判断异常是否为业务异常
|
||||
|
if(e instanceof BizException bizException){ |
||||
|
// 记录业务异常日志
|
||||
|
log.error("[业务异常]",e); |
||||
|
// 返回业务异常的错误码和错误信息
|
||||
|
return JsonData.buildCodeAndMsg(bizException.getCode(),bizException.getMsg()); |
||||
|
}else { |
||||
|
// 记录系统异常日志
|
||||
|
log.error("[系统异常]",e); |
||||
|
// 返回系统异常的错误信息
|
||||
|
return JsonData.buildError("系统异常"); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,72 @@ |
|||||
|
package org.ycloud.aipan.interceptor; |
||||
|
|
||||
|
import io.jsonwebtoken.Claims; |
||||
|
import jakarta.servlet.http.HttpServletRequest; |
||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.springframework.http.HttpMethod; |
||||
|
import org.springframework.http.HttpStatus; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.web.servlet.HandlerInterceptor; |
||||
|
import org.springframework.web.servlet.ModelAndView; |
||||
|
import org.ycloud.aipan.dto.AccountDTO; |
||||
|
import org.ycloud.aipan.enums.BizCodeEnum; |
||||
|
import org.ycloud.aipan.util.CommonUtil; |
||||
|
import org.ycloud.aipan.util.JsonData; |
||||
|
import org.ycloud.aipan.util.JwtUtil; |
||||
|
|
||||
|
|
||||
|
@Slf4j |
||||
|
@Component |
||||
|
public class LoginInterceptor implements HandlerInterceptor { |
||||
|
|
||||
|
public static ThreadLocal<AccountDTO> threadLocal = new ThreadLocal<>(); |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
||||
|
//处理options请求
|
||||
|
if(HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())){ |
||||
|
response.setStatus(HttpStatus.NO_CONTENT.value()); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
String token = request.getHeader("token"); |
||||
|
if(StringUtils.isBlank(token)){ |
||||
|
token = request.getParameter("token"); |
||||
|
} |
||||
|
|
||||
|
//如果存在token,就解析
|
||||
|
if(StringUtils.isNotBlank(token)){ |
||||
|
Claims claims = JwtUtil.checkLoginJWT(token); |
||||
|
if(claims == null){ |
||||
|
log.info("token 解析失败"); |
||||
|
CommonUtil.sendJsonMessage(response, JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN)); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
Long accountId = Long.valueOf(claims.get("accountId")+""); |
||||
|
String username = (String)claims.get("username"); |
||||
|
//创建accountDTO
|
||||
|
AccountDTO accountDTO = AccountDTO.builder().id(accountId).username(username).build(); |
||||
|
threadLocal.set(accountDTO); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
//没有token,未登录
|
||||
|
CommonUtil.sendJsonMessage(response, JsonData.buildResult(BizCodeEnum.ACCOUNT_UNLOGIN)); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { |
||||
|
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { |
||||
|
//清理threadLocal,避免内存泄漏
|
||||
|
threadLocal.remove(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package org.ycloud.aipan.mapper; |
||||
|
|
||||
|
import org.ycloud.aipan.model.AccountFileDO; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 用户文件表 Mapper 接口 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
public interface AccountFileMapper extends BaseMapper<AccountFileDO> { |
||||
|
|
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package org.ycloud.aipan.mapper; |
||||
|
|
||||
|
import org.ycloud.aipan.model.AccountDO; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 用户信息表 Mapper 接口 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
public interface AccountMapper extends BaseMapper<AccountDO> { |
||||
|
|
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package org.ycloud.aipan.mapper; |
||||
|
|
||||
|
import org.ycloud.aipan.model.FileChunkDO; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 文件分片信息表 Mapper 接口 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
public interface FileChunkMapper extends BaseMapper<FileChunkDO> { |
||||
|
|
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package org.ycloud.aipan.mapper; |
||||
|
|
||||
|
import org.ycloud.aipan.model.FileDO; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 用户文件表 Mapper 接口 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
public interface FileMapper extends BaseMapper<FileDO> { |
||||
|
|
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package org.ycloud.aipan.mapper; |
||||
|
|
||||
|
import org.ycloud.aipan.model.FileSuffixDO; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 文件分类表 Mapper 接口 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
public interface FileSuffixMapper extends BaseMapper<FileSuffixDO> { |
||||
|
|
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package org.ycloud.aipan.mapper; |
||||
|
|
||||
|
import org.ycloud.aipan.model.FileTypeDO; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 文件类型表 Mapper 接口 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
public interface FileTypeMapper extends BaseMapper<FileTypeDO> { |
||||
|
|
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package org.ycloud.aipan.mapper; |
||||
|
|
||||
|
import org.ycloud.aipan.model.ShareFileDO; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 文件分享表 Mapper 接口 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
public interface ShareFileMapper extends BaseMapper<ShareFileDO> { |
||||
|
|
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package org.ycloud.aipan.mapper; |
||||
|
|
||||
|
import org.ycloud.aipan.model.ShareDO; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 用户分享表 Mapper 接口 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
public interface ShareMapper extends BaseMapper<ShareDO> { |
||||
|
|
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package org.ycloud.aipan.mapper; |
||||
|
|
||||
|
import org.ycloud.aipan.model.StorageDO; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 存储信息表 Mapper 接口 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
public interface StorageMapper extends BaseMapper<StorageDO> { |
||||
|
|
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
package org.ycloud.aipan.model; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableLogic; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 用户信息表 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@TableName("account") |
||||
|
@Schema(name = "AccountDO", description = "用户信息表") |
||||
|
public class AccountDO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@Schema(description = "ID") |
||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
|
private Long id; |
||||
|
|
||||
|
@Schema(description = "用户名") |
||||
|
@TableField("username") |
||||
|
private String username; |
||||
|
|
||||
|
@Schema(description = "密码") |
||||
|
@TableField("password") |
||||
|
private String password; |
||||
|
|
||||
|
@Schema(description = "用户头像") |
||||
|
@TableField("avatar_url") |
||||
|
private String avatarUrl; |
||||
|
|
||||
|
@Schema(description = "手机号") |
||||
|
@TableField("phone") |
||||
|
private String phone; |
||||
|
|
||||
|
@Schema(description = "用户角色 COMMON, ADMIN") |
||||
|
@TableField("role") |
||||
|
private String role; |
||||
|
|
||||
|
@Schema(description = "逻辑删除(1删除 0未删除)") |
||||
|
@TableField("del") |
||||
|
@TableLogic |
||||
|
private Boolean del; |
||||
|
|
||||
|
@Schema(description = "创建时间") |
||||
|
@TableField("gmt_create") |
||||
|
private Date gmtCreate; |
||||
|
|
||||
|
@Schema(description = "更新时间") |
||||
|
@TableField("gmt_modified") |
||||
|
private Date gmtModified; |
||||
|
} |
@ -0,0 +1,82 @@ |
|||||
|
package org.ycloud.aipan.model; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableLogic; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 用户文件表 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@TableName("account_file") |
||||
|
@Schema(name = "AccountFileDO", description = "用户文件表") |
||||
|
public class AccountFileDO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@Schema(description = "id") |
||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
|
private Long id; |
||||
|
|
||||
|
@Schema(description = "用户ID") |
||||
|
@TableField("account_id") |
||||
|
private Long accountId; |
||||
|
|
||||
|
@Schema(description = "状态 0不是文件夹,1是文件夹") |
||||
|
@TableField("is_dir") |
||||
|
private Integer isDir; |
||||
|
|
||||
|
@Schema(description = "上层文件夹ID,顶层文件夹为0") |
||||
|
@TableField("parent_id") |
||||
|
private Long parentId; |
||||
|
|
||||
|
@Schema(description = "文件ID,真正存储的文件") |
||||
|
@TableField("file_id") |
||||
|
private Long fileId; |
||||
|
|
||||
|
@Schema(description = "文件名称") |
||||
|
@TableField("file_name") |
||||
|
private String fileName; |
||||
|
|
||||
|
@Schema(description = "文件类型:普通文件common 、压缩文件compress 、 excel 、 word 、 pdf 、 txt 、 图片img 、音频audio 、视频video 、ppt 、源码文件code 、 csv") |
||||
|
@TableField("file_type") |
||||
|
private String fileType; |
||||
|
|
||||
|
@Schema(description = "文件的后缀拓展名") |
||||
|
@TableField("file_suffix") |
||||
|
private String fileSuffix; |
||||
|
|
||||
|
@Schema(description = "文件大小,字节") |
||||
|
@TableField("file_size") |
||||
|
private Long fileSize; |
||||
|
|
||||
|
@Schema(description = "逻辑删除(0未删除,1已删除)") |
||||
|
@TableField("del") |
||||
|
@TableLogic |
||||
|
private Boolean del; |
||||
|
|
||||
|
@Schema(description = "删除日期") |
||||
|
@TableField("del_time") |
||||
|
private Date delTime; |
||||
|
|
||||
|
@Schema(description = "更新时间") |
||||
|
@TableField("gmt_modified") |
||||
|
private Date gmtModified; |
||||
|
|
||||
|
@Schema(description = "创建时间") |
||||
|
@TableField("gmt_create") |
||||
|
private Date gmtCreate; |
||||
|
} |
@ -0,0 +1,73 @@ |
|||||
|
package org.ycloud.aipan.model; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 文件分片信息表 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@TableName("file_chunk") |
||||
|
@Schema(name = "FileChunkDO", description = "文件分片信息表") |
||||
|
public class FileChunkDO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
|
private Long id; |
||||
|
|
||||
|
@Schema(description = "文件唯一标识(md5)") |
||||
|
@TableField("identifier") |
||||
|
private String identifier; |
||||
|
|
||||
|
@Schema(description = "分片上传ID") |
||||
|
@TableField("upload_id") |
||||
|
private String uploadId; |
||||
|
|
||||
|
@Schema(description = "文件名") |
||||
|
@TableField("file_name") |
||||
|
private String fileName; |
||||
|
|
||||
|
@Schema(description = "所属桶名") |
||||
|
@TableField("bucket_name") |
||||
|
private String bucketName; |
||||
|
|
||||
|
@Schema(description = "文件的key") |
||||
|
@TableField("object_key") |
||||
|
private String objectKey; |
||||
|
|
||||
|
@Schema(description = "总文件大小(byte)") |
||||
|
@TableField("total_size") |
||||
|
private Long totalSize; |
||||
|
|
||||
|
@Schema(description = "每个分片大小(byte)") |
||||
|
@TableField("chunk_size") |
||||
|
private Long chunkSize; |
||||
|
|
||||
|
@Schema(description = "分片数量") |
||||
|
@TableField("chunk_num") |
||||
|
private Integer chunkNum; |
||||
|
|
||||
|
@Schema(description = "用户ID") |
||||
|
@TableField("account_id") |
||||
|
private Long accountId; |
||||
|
|
||||
|
@TableField("gmt_create") |
||||
|
private Date gmtCreate; |
||||
|
|
||||
|
@TableField("gmt_modified") |
||||
|
private Date gmtModified; |
||||
|
} |
@ -0,0 +1,70 @@ |
|||||
|
package org.ycloud.aipan.model; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableLogic; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 用户文件表 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@TableName("file") |
||||
|
@Schema(name = "FileDO", description = "用户文件表") |
||||
|
public class FileDO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@Schema(description = "文件id") |
||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
|
private Long id; |
||||
|
|
||||
|
@Schema(description = "用户id,是哪个用户初次上传的") |
||||
|
@TableField("account_id") |
||||
|
private Long accountId; |
||||
|
|
||||
|
@Schema(description = "文件名称,秒传需要用到,冗余存储") |
||||
|
@TableField("file_name") |
||||
|
private String fileName; |
||||
|
|
||||
|
@Schema(description = "文件的后缀拓展名,冗余存储") |
||||
|
@TableField("file_suffix") |
||||
|
private String fileSuffix; |
||||
|
|
||||
|
@Schema(description = "文件大小,字节,冗余存储") |
||||
|
@TableField("file_size") |
||||
|
private Long fileSize; |
||||
|
|
||||
|
@Schema(description = "文件的key, 格式 日期/md5.拓展名,比如 2024-11-13/921674fd-cdaf-459a-be7b-109469e7050d.png") |
||||
|
@TableField("object_key") |
||||
|
private String objectKey; |
||||
|
|
||||
|
@Schema(description = "唯一标识,文件MD5") |
||||
|
@TableField("identifier") |
||||
|
private String identifier; |
||||
|
|
||||
|
@Schema(description = "逻辑删除(0未删除,1已删除)") |
||||
|
@TableField("del") |
||||
|
@TableLogic |
||||
|
private Boolean del; |
||||
|
|
||||
|
@Schema(description = "更新时间") |
||||
|
@TableField("gmt_modified") |
||||
|
private Date gmtModified; |
||||
|
|
||||
|
@Schema(description = "创建时间") |
||||
|
@TableField("gmt_create") |
||||
|
private Date gmtCreate; |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
package org.ycloud.aipan.model; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import java.io.Serializable; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 文件分类表 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@TableName("file_suffix") |
||||
|
@Schema(name = "FileSuffixDO", description = "文件分类表") |
||||
|
public class FileSuffixDO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@TableId(value = "id", type = IdType.AUTO) |
||||
|
private Integer id; |
||||
|
|
||||
|
@Schema(description = "文件扩展名") |
||||
|
@TableField("file_suffix") |
||||
|
private String fileSuffix; |
||||
|
|
||||
|
@Schema(description = "文件类型ID") |
||||
|
@TableField("file_type_id") |
||||
|
private Integer fileTypeId; |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
package org.ycloud.aipan.model; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import java.io.Serializable; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 文件类型表 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@TableName("file_type") |
||||
|
@Schema(name = "FileTypeDO", description = "文件类型表") |
||||
|
public class FileTypeDO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@Schema(description = "主键ID") |
||||
|
@TableId(value = "id", type = IdType.AUTO) |
||||
|
private Integer id; |
||||
|
|
||||
|
@Schema(description = "文件类型名") |
||||
|
@TableField("file_type_name") |
||||
|
private String fileTypeName; |
||||
|
} |
@ -0,0 +1,75 @@ |
|||||
|
package org.ycloud.aipan.model; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 用户分享表 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@TableName("share") |
||||
|
@Schema(name = "ShareDO", description = "用户分享表") |
||||
|
public class ShareDO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@Schema(description = "分享id") |
||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
|
private Long id; |
||||
|
|
||||
|
@Schema(description = "分享名称") |
||||
|
@TableField("share_name") |
||||
|
private String shareName; |
||||
|
|
||||
|
@Schema(description = "分享类型(no_code没有提取码 ,need_code有提取码)") |
||||
|
@TableField("share_type") |
||||
|
private String shareType; |
||||
|
|
||||
|
@Schema(description = "分享类型(0 永久有效;1: 7天有效;2: 30天有效)") |
||||
|
@TableField("share_day_type") |
||||
|
private Integer shareDayType; |
||||
|
|
||||
|
@Schema(description = "分享有效天数(永久有效为0)") |
||||
|
@TableField("share_day") |
||||
|
private Integer shareDay; |
||||
|
|
||||
|
@Schema(description = "分享结束时间") |
||||
|
@TableField("share_end_time") |
||||
|
private Date shareEndTime; |
||||
|
|
||||
|
@Schema(description = "分享链接地址") |
||||
|
@TableField("share_url") |
||||
|
private String shareUrl; |
||||
|
|
||||
|
@Schema(description = "分享提取码") |
||||
|
@TableField("share_code") |
||||
|
private String shareCode; |
||||
|
|
||||
|
@Schema(description = "分享状态 used正常, expired已失效, cancled取消") |
||||
|
@TableField("share_status") |
||||
|
private String shareStatus; |
||||
|
|
||||
|
@Schema(description = "分享创建人") |
||||
|
@TableField("account_id") |
||||
|
private Long accountId; |
||||
|
|
||||
|
@Schema(description = "创建时间") |
||||
|
@TableField("gmt_create") |
||||
|
private Date gmtCreate; |
||||
|
|
||||
|
@TableField("gmt_modified") |
||||
|
private Date gmtModified; |
||||
|
} |
@ -0,0 +1,52 @@ |
|||||
|
package org.ycloud.aipan.model; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 文件分享表 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@TableName("share_file") |
||||
|
@Schema(name = "ShareFileDO", description = "文件分享表") |
||||
|
public class ShareFileDO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@Schema(description = "主键ID") |
||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
|
private Long id; |
||||
|
|
||||
|
@Schema(description = "分享id") |
||||
|
@TableField("share_id") |
||||
|
private Long shareId; |
||||
|
|
||||
|
@Schema(description = "用户文件的ID") |
||||
|
@TableField("account_file_id") |
||||
|
private Long accountFileId; |
||||
|
|
||||
|
@Schema(description = "创建者id") |
||||
|
@TableField("account_id") |
||||
|
private Long accountId; |
||||
|
|
||||
|
@Schema(description = "分享时间") |
||||
|
@TableField("gmt_create") |
||||
|
private Date gmtCreate; |
||||
|
|
||||
|
@Schema(description = "更新时间") |
||||
|
@TableField("gmt_modified") |
||||
|
private Date gmtModified; |
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
package org.ycloud.aipan.model; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.annotation.TableField; |
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import java.io.Serializable; |
||||
|
import java.util.Date; |
||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||
|
import lombok.Getter; |
||||
|
import lombok.Setter; |
||||
|
|
||||
|
/** |
||||
|
* <p> |
||||
|
* 存储信息表 |
||||
|
* </p> |
||||
|
* |
||||
|
* @author everyone |
||||
|
* @since 2025-02-12 |
||||
|
*/ |
||||
|
@Getter |
||||
|
@Setter |
||||
|
@TableName("storage") |
||||
|
@Schema(name = "StorageDO", description = "存储信息表") |
||||
|
public class StorageDO implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID) |
||||
|
private Long id; |
||||
|
|
||||
|
@Schema(description = "所属用户") |
||||
|
@TableField("account_id") |
||||
|
private Long accountId; |
||||
|
|
||||
|
@Schema(description = "占用存储大小") |
||||
|
@TableField("used_size") |
||||
|
private Long usedSize; |
||||
|
|
||||
|
@Schema(description = "总容量大小,字节存储") |
||||
|
@TableField("total_size") |
||||
|
private Long totalSize; |
||||
|
|
||||
|
@Schema(description = "创建时间") |
||||
|
@TableField("gmt_create") |
||||
|
private Date gmtCreate; |
||||
|
|
||||
|
@Schema(description = "更新时间") |
||||
|
@TableField("gmt_modified") |
||||
|
private Date gmtModified; |
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
package org.ycloud.aipan.service; |
||||
|
|
||||
|
|
||||
|
import org.ycloud.aipan.controller.req.FileBatchReq; |
||||
|
import org.ycloud.aipan.controller.req.FileUpdateReq; |
||||
|
import org.ycloud.aipan.controller.req.FileUploadReq; |
||||
|
import org.ycloud.aipan.controller.req.FolderCreateReq; |
||||
|
import org.ycloud.aipan.dto.AccountFileDTO; |
||||
|
import org.ycloud.aipan.dto.FolderTreeNodeDTO; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface AccountFileService { |
||||
|
/** |
||||
|
* 获取文件列表 |
||||
|
*/ |
||||
|
List<AccountFileDTO> listFile(Long accountId, Long parentId); |
||||
|
/** |
||||
|
* 创建文件夹 |
||||
|
*/ |
||||
|
Long createFolder(FolderCreateReq createRootFolderReq); |
||||
|
|
||||
|
/** |
||||
|
* 重命名文件 |
||||
|
* 1、检查ID是否存在 |
||||
|
* 2、新旧文件名称不能一样 |
||||
|
* 3、同层文件名称不能一样 |
||||
|
* @param req |
||||
|
*/ |
||||
|
void renameFile(FileUpdateReq req); |
||||
|
|
||||
|
/** |
||||
|
* 查询文件树接口 (非递归方式) |
||||
|
* 1、查询用户全部文件夹 |
||||
|
* 2、拼装文件树 |
||||
|
*/ |
||||
|
List<FolderTreeNodeDTO> folderTree(Long accountId); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 普通小文件上传 |
||||
|
* @param req |
||||
|
*/ |
||||
|
void fileUpload(FileUploadReq req); |
||||
|
|
||||
|
/** |
||||
|
* 批量移动目标文件夹 |
||||
|
* @param req |
||||
|
*/ |
||||
|
void moveBatch(FileBatchReq req); |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
package org.ycloud.aipan.service; |
||||
|
|
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
import org.ycloud.aipan.controller.req.AccountLoginReq; |
||||
|
import org.ycloud.aipan.controller.req.AccountRegisterReq; |
||||
|
import org.ycloud.aipan.dto.AccountDTO; |
||||
|
|
||||
|
public interface AccountService { |
||||
|
|
||||
|
/** |
||||
|
* 1、查询手机号是否重复 |
||||
|
* 2、加密密码 |
||||
|
* 3、插入数据库 |
||||
|
* 4、其他相关初始化操作 |
||||
|
*/ |
||||
|
void register(AccountRegisterReq req); |
||||
|
|
||||
|
|
||||
|
String uploadAvatar(MultipartFile file); |
||||
|
|
||||
|
AccountDTO login(AccountLoginReq req); |
||||
|
|
||||
|
AccountDTO queryDetail(Long id); |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
package org.ycloud.aipan.service; |
||||
|
|
||||
|
import org.ycloud.aipan.dto.AccountFileDTO; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public interface FileService { |
||||
|
|
||||
|
} |
@ -0,0 +1,389 @@ |
|||||
|
package org.ycloud.aipan.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
import org.springframework.util.CollectionUtils; |
||||
|
import org.ycloud.aipan.component.StoreEngine; |
||||
|
import org.ycloud.aipan.config.MinioConfig; |
||||
|
import org.ycloud.aipan.controller.req.FileBatchReq; |
||||
|
import org.ycloud.aipan.controller.req.FileUpdateReq; |
||||
|
import org.ycloud.aipan.controller.req.FileUploadReq; |
||||
|
import org.ycloud.aipan.controller.req.FolderCreateReq; |
||||
|
import org.ycloud.aipan.dto.AccountFileDTO; |
||||
|
import org.ycloud.aipan.dto.FolderTreeNodeDTO; |
||||
|
import org.ycloud.aipan.enums.BizCodeEnum; |
||||
|
import org.ycloud.aipan.enums.FileTypeEnum; |
||||
|
import org.ycloud.aipan.enums.FolderFlagEnum; |
||||
|
import org.ycloud.aipan.exception.BizException; |
||||
|
import org.ycloud.aipan.mapper.AccountFileMapper; |
||||
|
import org.ycloud.aipan.mapper.FileMapper; |
||||
|
import org.ycloud.aipan.model.AccountFileDO; |
||||
|
import org.ycloud.aipan.model.FileDO; |
||||
|
import org.ycloud.aipan.service.AccountFileService; |
||||
|
import org.ycloud.aipan.util.CommonUtil; |
||||
|
import org.ycloud.aipan.util.SpringBeanUtil; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.Objects; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
|
||||
|
@Slf4j |
||||
|
@Service |
||||
|
public class AccountFileServiceImpl implements AccountFileService { |
||||
|
@Autowired |
||||
|
private MinioConfig minioConfig; |
||||
|
@Autowired |
||||
|
private StoreEngine fileStoreEngine; |
||||
|
@Autowired |
||||
|
private AccountFileMapper accountFileMapper; |
||||
|
@Autowired |
||||
|
private FileMapper fileMapper; |
||||
|
|
||||
|
@Override |
||||
|
public List<AccountFileDTO> listFile(Long accountId, Long parentId) { |
||||
|
List<AccountFileDO> accountFileDOList = accountFileMapper.selectList(new QueryWrapper<AccountFileDO>() |
||||
|
.eq("account_id", accountId).eq("parent_id", parentId) |
||||
|
.orderByDesc("is_dir") |
||||
|
.orderByDesc("gmt_create") |
||||
|
); |
||||
|
return SpringBeanUtil.copyProperties(accountFileDOList, AccountFileDTO.class); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Long createFolder(FolderCreateReq req) { |
||||
|
AccountFileDTO accountFileDTO = AccountFileDTO.builder().accountId(req.getAccountId()) |
||||
|
.parentId(req.getParentId()) |
||||
|
.fileName(req.getFolderName()) |
||||
|
.isDir(FolderFlagEnum.YES.getCode()).build(); |
||||
|
return saveAccountFile(accountFileDTO); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public void renameFile(FileUpdateReq req) { |
||||
|
//检查ID是否存在
|
||||
|
AccountFileDO accountFileDO = accountFileMapper.selectOne(new QueryWrapper<AccountFileDO>() |
||||
|
.eq("id", req.getFileId()).eq("account_id", req.getAccountId())); |
||||
|
|
||||
|
if (accountFileDO == null) { |
||||
|
log.error("文件不存在,{}", req); |
||||
|
throw new BizException(BizCodeEnum.FILE_NOT_EXISTS); |
||||
|
} else { |
||||
|
//新旧文件名称不能一样
|
||||
|
if (Objects.equals(accountFileDO.getFileName(), req.getNewFilename())) { |
||||
|
log.error("文件名称重复,{}", req); |
||||
|
throw new BizException(BizCodeEnum.FILE_RENAME_REPEAT); |
||||
|
} |
||||
|
//同层文件名称不能一样
|
||||
|
Long selectCount = accountFileMapper.selectCount(new QueryWrapper<AccountFileDO>() |
||||
|
.eq("account_id", req.getAccountId()) |
||||
|
.eq("parent_id", accountFileDO.getParentId()) |
||||
|
.eq("file_name", req.getNewFilename())); |
||||
|
if (selectCount > 0) { |
||||
|
log.error("文件名称重复,{}", req); |
||||
|
throw new BizException(BizCodeEnum.FILE_RENAME_REPEAT); |
||||
|
} else { |
||||
|
accountFileDO.setFileName(req.getNewFilename()); |
||||
|
accountFileMapper.updateById(accountFileDO); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public List<FolderTreeNodeDTO> folderTree(Long accountId) { |
||||
|
//查询用户全部文件夹
|
||||
|
List<AccountFileDO> folderList = accountFileMapper.selectList(new QueryWrapper<AccountFileDO>() |
||||
|
.eq("account_id", accountId) |
||||
|
.eq("is_dir", FolderFlagEnum.YES.getCode()) |
||||
|
); |
||||
|
|
||||
|
if(CollectionUtils.isEmpty(folderList)){ |
||||
|
return List.of(); |
||||
|
} |
||||
|
//构建一个map, key是文件ID,value是文件对象 相当于一个数据源
|
||||
|
Map<Long, FolderTreeNodeDTO> folderMap = folderList.stream() |
||||
|
.collect(Collectors.toMap(AccountFileDO::getId, accountFileDO -> |
||||
|
FolderTreeNodeDTO.builder() |
||||
|
.id(accountFileDO.getId()) |
||||
|
.parentId(accountFileDO.getParentId()) |
||||
|
.label(accountFileDO.getFileName()) |
||||
|
.children(new ArrayList<>()) |
||||
|
.build() |
||||
|
)); |
||||
|
|
||||
|
//构建文件树,遍历数据源,为每个文件夹找到子文件夹
|
||||
|
for (FolderTreeNodeDTO node : folderMap.values()) { |
||||
|
Long parentId = node.getParentId(); |
||||
|
if(parentId!=null && folderMap.containsKey(parentId)){ |
||||
|
//获取父文件
|
||||
|
FolderTreeNodeDTO parentNode = folderMap.get(parentId); |
||||
|
//获取父文件夹的子节点位置
|
||||
|
List<FolderTreeNodeDTO> children = parentNode.getChildren(); |
||||
|
//将当前节点添加到对应的文件夹里面
|
||||
|
children.add(node); |
||||
|
} |
||||
|
} |
||||
|
//过滤根节点,即parentID是0的
|
||||
|
List<FolderTreeNodeDTO> rootFolderList = folderMap.values().stream() |
||||
|
.filter(node -> Objects.equals(node.getParentId(), 0L)) |
||||
|
.collect(Collectors.toList()); |
||||
|
|
||||
|
|
||||
|
return rootFolderList; |
||||
|
|
||||
|
} |
||||
|
/** |
||||
|
* 查询文件树接口 (非递归方式) |
||||
|
* 1、查询用户全部文件夹 |
||||
|
* 2、拼装文件树 |
||||
|
* @param accountId |
||||
|
* @return |
||||
|
*/ |
||||
|
//@Override
|
||||
|
public List<FolderTreeNodeDTO> folderTreeV2(Long accountId) { |
||||
|
//查询用户全部文件夹
|
||||
|
List<AccountFileDO> folderList = accountFileMapper.selectList(new QueryWrapper<AccountFileDO>() |
||||
|
.eq("account_id", accountId) |
||||
|
.eq("is_dir", FolderFlagEnum.YES.getCode()) |
||||
|
); |
||||
|
|
||||
|
if(CollectionUtils.isEmpty(folderList)){ |
||||
|
return List.of(); |
||||
|
} |
||||
|
|
||||
|
List<FolderTreeNodeDTO> folderTreeNodeDTOList = folderList.stream().map(file -> FolderTreeNodeDTO.builder() |
||||
|
.id(file.getId()) |
||||
|
.parentId(file.getParentId()) |
||||
|
.label(file.getFileName()) |
||||
|
.children(new ArrayList<>()) |
||||
|
.build()).toList(); |
||||
|
|
||||
|
//根据父文件ID进行分组,key是当前文件夹ID,value是对应的子文件夹列表,也是数据源
|
||||
|
Map<Long, List<FolderTreeNodeDTO>> folderMap = folderTreeNodeDTOList |
||||
|
.stream().collect(Collectors.groupingBy(FolderTreeNodeDTO::getParentId)); |
||||
|
|
||||
|
//处理拼装文件树
|
||||
|
for(FolderTreeNodeDTO node : folderTreeNodeDTOList){ |
||||
|
List<FolderTreeNodeDTO> children = folderMap.get(node.getId()); |
||||
|
//判断是否为空
|
||||
|
if(!CollectionUtils.isEmpty(children)){ |
||||
|
node.getChildren().addAll(children); |
||||
|
} |
||||
|
} |
||||
|
//过滤根节点,即parentID是0的
|
||||
|
List<FolderTreeNodeDTO> folderTreeNodeDTOS = folderTreeNodeDTOList.stream().filter(node -> Objects.equals(node.getParentId(), 0L)) |
||||
|
.collect(Collectors.toList()); |
||||
|
|
||||
|
return folderTreeNodeDTOS; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 文件上传 |
||||
|
* 1、上传到存储引擎 |
||||
|
* 2、保存文件关系 |
||||
|
* 3、保存账号和文件的关系 |
||||
|
*/ |
||||
|
@Override |
||||
|
@Transactional(rollbackFor = Exception.class) |
||||
|
public void fileUpload(FileUploadReq req) { |
||||
|
//上传到存储引擎
|
||||
|
String storeFileObjectKey = storeFile(req); |
||||
|
//保存文件关系 + 保存账号和文件的关系
|
||||
|
saveFileAndAccountFile( req,storeFileObjectKey); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 批量移动文件 |
||||
|
* 1、检查被移动的文件ID是否合法 |
||||
|
* 2、检查目标文件夹ID是否合法 |
||||
|
* 3、批量移动文件到目标文件夹(重复名称处理) |
||||
|
* |
||||
|
* @param req |
||||
|
*/ |
||||
|
@Override |
||||
|
public void moveBatch(FileBatchReq req) { |
||||
|
|
||||
|
// 检查被移动的文件ID是否合法
|
||||
|
List<AccountFileDO> accountFileDOList = checkFileIdLegal(req.getFileIds(),req.getAccountId()); |
||||
|
|
||||
|
//检查目标文件夹ID是否合法,包括子文件夹
|
||||
|
checkTargetParentIdLegal(req); |
||||
|
|
||||
|
//批量移动文件到目标文件夹(重复名称处理)
|
||||
|
accountFileDOList.forEach(this::processFileNameDuplicate); |
||||
|
|
||||
|
//更新文件或者文件夹的parent_id为目标文件夹的ID
|
||||
|
UpdateWrapper<AccountFileDO> updateWrapper = new UpdateWrapper<>(); |
||||
|
updateWrapper.in("id",req.getFileIds()) |
||||
|
.set("parent_id",req.getTargetParentId()); |
||||
|
int updateCount = accountFileMapper.update(null, updateWrapper); |
||||
|
if(updateCount!=req.getFileIds().size()){ |
||||
|
throw new BizException(BizCodeEnum.FILE_BATCH_UPDATE_ERROR); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 检查目标文件夹ID是否合法,包括子文件夹 |
||||
|
* 1、目标的文件ID不能是文件 |
||||
|
* 2、要操作的文件列表不能包括目标文件ID |
||||
|
* @param req |
||||
|
*/ |
||||
|
private void checkTargetParentIdLegal(FileBatchReq req) { |
||||
|
//目标的文件ID不能是文件
|
||||
|
AccountFileDO targetAccountFileDO = accountFileMapper.selectOne(new QueryWrapper<AccountFileDO>() |
||||
|
.eq("id", req.getTargetParentId()) |
||||
|
.eq("is_dir", FolderFlagEnum.YES.getCode()) |
||||
|
.eq("account_id", req.getAccountId())); |
||||
|
if (targetAccountFileDO == null) { |
||||
|
log.error("目标文件ID不是文件,需要是文件夹,targetParentId={}", req.getTargetParentId()); |
||||
|
throw new BizException(BizCodeEnum.FILE_TARGET_PARENT_ILLEGAL); |
||||
|
} |
||||
|
} |
||||
|
/** |
||||
|
* 检查被移动的文件ID是否合法 |
||||
|
* @param fileIds |
||||
|
* @param accountId |
||||
|
* @return |
||||
|
*/ |
||||
|
private List<AccountFileDO> checkFileIdLegal(List<Long> fileIds, Long accountId) { |
||||
|
|
||||
|
List<AccountFileDO> accountFileDOList = accountFileMapper |
||||
|
.selectList(new QueryWrapper<AccountFileDO>().in("id", fileIds).eq("account_id", accountId)); |
||||
|
|
||||
|
if(accountFileDOList.size()!=fileIds.size()){ |
||||
|
log.error("文件ID数量不合法,ids={}", fileIds); |
||||
|
throw new BizException(BizCodeEnum.FILE_BATCH_UPDATE_ERROR); |
||||
|
} |
||||
|
//进一步完善的话,可以加个set,防止重复元素
|
||||
|
|
||||
|
return accountFileDOList; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 保存文件和账号文件的关系到数据库 |
||||
|
* @param req |
||||
|
* @param storeFileObjectKey |
||||
|
*/ |
||||
|
public void saveFileAndAccountFile(FileUploadReq req, String storeFileObjectKey) { |
||||
|
//保存文件
|
||||
|
FileDO fileDO = saveFile(req,storeFileObjectKey); |
||||
|
|
||||
|
//保存文件账号关系
|
||||
|
AccountFileDTO accountFileDTO = AccountFileDTO.builder() |
||||
|
.accountId(req.getAccountId()) |
||||
|
.parentId(req.getParentId()) |
||||
|
.fileId(fileDO.getId()) |
||||
|
.fileName(req.getFilename()) |
||||
|
.isDir(FolderFlagEnum.NO.getCode()) |
||||
|
.fileSuffix(fileDO.getFileSuffix()) |
||||
|
.fileSize(req.getFileSize()) |
||||
|
.fileType(FileTypeEnum.fromExtension(fileDO.getFileSuffix()).name()) |
||||
|
.build(); |
||||
|
saveAccountFile(accountFileDTO); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
private FileDO saveFile(FileUploadReq req, String storeFileObjectKey) { |
||||
|
FileDO fileDO = new FileDO(); |
||||
|
fileDO.setAccountId(req.getAccountId()); |
||||
|
fileDO.setFileName(req.getFilename()); |
||||
|
fileDO.setFileSize(req.getFile() !=null ? req.getFile().getSize():req.getFileSize()); |
||||
|
fileDO.setFileSuffix(CommonUtil.getFileSuffix(req.getFilename())); |
||||
|
fileDO.setObjectKey(storeFileObjectKey); |
||||
|
fileDO.setIdentifier(req.getIdentifier()); |
||||
|
fileMapper.insert(fileDO); |
||||
|
return fileDO; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传文件到存储引擎,返回存储的文件路径 |
||||
|
* @param req |
||||
|
* @return |
||||
|
*/ |
||||
|
private String storeFile(FileUploadReq req) { |
||||
|
|
||||
|
String objectKey = CommonUtil.getFilePath(req.getFilename()); |
||||
|
fileStoreEngine.upload(minioConfig.getBucketName(), objectKey, req.getFile()); |
||||
|
return objectKey; |
||||
|
} |
||||
|
/** |
||||
|
* 处理用户和文件的关系,存储文件和文件夹都是可以的 |
||||
|
* <p> |
||||
|
* 1、检查父文件是否存在 |
||||
|
* 2、检查文件是否重复 |
||||
|
* 3、保存相关文件关系 |
||||
|
* |
||||
|
* @param accountFileDTO |
||||
|
* @return |
||||
|
*/ |
||||
|
private Long saveAccountFile(AccountFileDTO accountFileDTO) { |
||||
|
//检查父文件是否存在
|
||||
|
checkParentFileId(accountFileDTO); |
||||
|
AccountFileDO accountFileDO = SpringBeanUtil.copyProperties(accountFileDTO, AccountFileDO.class); |
||||
|
//检查文件是否重复 aa aa(1) aa(2)
|
||||
|
processFileNameDuplicate(accountFileDO); |
||||
|
//保存相关文件关系
|
||||
|
accountFileMapper.insert(accountFileDO); |
||||
|
|
||||
|
return accountFileDO.getId(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查父文件是否存在 |
||||
|
* |
||||
|
* @param accountFileDTO |
||||
|
*/ |
||||
|
private void checkParentFileId(AccountFileDTO accountFileDTO) { |
||||
|
if (accountFileDTO.getParentId() != 0) { |
||||
|
AccountFileDO accountFileDO = accountFileMapper.selectOne( |
||||
|
new QueryWrapper<AccountFileDO>() |
||||
|
.eq("id", accountFileDTO.getParentId()) |
||||
|
.eq("account_id", accountFileDTO.getAccountId())); |
||||
|
|
||||
|
if (accountFileDO == null) { |
||||
|
throw new BizException(BizCodeEnum.FILE_NOT_EXISTS); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理文件是否重复, |
||||
|
* 文件夹重复和文件名重复处理规则不一样 |
||||
|
* |
||||
|
* @param accountFileDO |
||||
|
*/ |
||||
|
private void processFileNameDuplicate(AccountFileDO accountFileDO) { |
||||
|
Long selectCount = accountFileMapper.selectCount(new QueryWrapper<AccountFileDO>() |
||||
|
.eq("account_id", accountFileDO.getAccountId()) |
||||
|
.eq("parent_id", accountFileDO.getParentId()) |
||||
|
.eq("is_dir", accountFileDO.getIsDir()) |
||||
|
.eq("file_name", accountFileDO.getFileName())); |
||||
|
|
||||
|
if (selectCount > 0) { |
||||
|
//处理重复文件夹
|
||||
|
if (Objects.equals(accountFileDO.getIsDir(), FolderFlagEnum.YES.getCode())) { |
||||
|
accountFileDO.setFileName(accountFileDO.getFileName() + "_" + System.currentTimeMillis()); |
||||
|
} else { |
||||
|
//处理重复文件名,提取文件拓展名
|
||||
|
String[] split = accountFileDO.getFileName().split("\\."); |
||||
|
accountFileDO.setFileName(split[0] + "_" + System.currentTimeMillis() + "." + split[1]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,124 @@ |
|||||
|
package org.ycloud.aipan.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.util.DigestUtils; |
||||
|
import org.springframework.web.multipart.MultipartFile; |
||||
|
import org.ycloud.aipan.component.StoreEngine; |
||||
|
import org.ycloud.aipan.config.AccountConfig; |
||||
|
import org.ycloud.aipan.config.MinioConfig; |
||||
|
import org.ycloud.aipan.controller.req.AccountLoginReq; |
||||
|
import org.ycloud.aipan.controller.req.AccountRegisterReq; |
||||
|
import org.ycloud.aipan.controller.req.FolderCreateReq; |
||||
|
import org.ycloud.aipan.dto.AccountDTO; |
||||
|
import org.ycloud.aipan.dto.StorageDTO; |
||||
|
import org.ycloud.aipan.enums.AccountRoleEnum; |
||||
|
import org.ycloud.aipan.enums.BizCodeEnum; |
||||
|
import org.ycloud.aipan.exception.BizException; |
||||
|
import org.ycloud.aipan.mapper.AccountFileMapper; |
||||
|
import org.ycloud.aipan.mapper.AccountMapper; |
||||
|
import org.ycloud.aipan.mapper.StorageMapper; |
||||
|
import org.ycloud.aipan.model.AccountDO; |
||||
|
import org.ycloud.aipan.model.AccountFileDO; |
||||
|
import org.ycloud.aipan.model.StorageDO; |
||||
|
import org.ycloud.aipan.service.AccountFileService; |
||||
|
import org.ycloud.aipan.service.AccountService; |
||||
|
import org.ycloud.aipan.util.CommonUtil; |
||||
|
import org.ycloud.aipan.util.SpringBeanUtil; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class AccountServiceImpl implements AccountService { |
||||
|
|
||||
|
@Autowired |
||||
|
private AccountMapper accountMapper; |
||||
|
@Autowired |
||||
|
private StoreEngine fileStoreEngine; |
||||
|
@Autowired |
||||
|
private MinioConfig minioConfig; |
||||
|
@Autowired |
||||
|
private AccountFileService accountFileService; |
||||
|
@Autowired |
||||
|
private StorageMapper storageMapper; |
||||
|
@Autowired |
||||
|
private AccountFileMapper accountFileMapper; |
||||
|
|
||||
|
@Override |
||||
|
public void register(AccountRegisterReq req) { |
||||
|
|
||||
|
//1、查询手机号是否重复
|
||||
|
List<AccountDO> accountDOList = accountMapper.selectList(new QueryWrapper<AccountDO>().eq("phone", req.getPhone())); |
||||
|
if (!accountDOList.isEmpty()) { |
||||
|
throw new BizException(BizCodeEnum.ACCOUNT_REPEAT); |
||||
|
} |
||||
|
|
||||
|
AccountDO accountDO = SpringBeanUtil.copyProperties(req, AccountDO.class); |
||||
|
|
||||
|
//加密密码
|
||||
|
String digestAsHex = DigestUtils.md5DigestAsHex((AccountConfig.ACCOUNT_SALT + req.getPassword()).getBytes()); |
||||
|
accountDO.setPassword(digestAsHex); |
||||
|
accountDO.setRole(AccountRoleEnum.COMMON.name()); |
||||
|
accountMapper.insert(accountDO); |
||||
|
|
||||
|
//其他操作 TODO
|
||||
|
//创建默认的存储空间
|
||||
|
StorageDO storageDO = new StorageDO(); |
||||
|
storageDO.setAccountId(accountDO.getId()); |
||||
|
storageDO.setUsedSize(0L); |
||||
|
storageDO.setTotalSize(AccountConfig.DEFAULT_STORAGE_SIZE); |
||||
|
storageMapper.insert(storageDO); |
||||
|
|
||||
|
//初始化根目录
|
||||
|
FolderCreateReq createRootFolderReq = FolderCreateReq.builder() |
||||
|
.accountId(accountDO.getId()) |
||||
|
.parentId(AccountConfig.ROOT_PARENT_ID) |
||||
|
.folderName(AccountConfig.ROOT_FOLDER_NAME) |
||||
|
.build(); |
||||
|
accountFileService.createFolder(createRootFolderReq); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String uploadAvatar(MultipartFile file) { |
||||
|
String filename = CommonUtil.getFilePath(file.getOriginalFilename()); |
||||
|
fileStoreEngine.upload(minioConfig.getAvatarBucketName(), filename, file); |
||||
|
return minioConfig.getEndpoint() + "/" + minioConfig.getAvatarBucketName() + "/" + filename; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AccountDTO login(AccountLoginReq req) { |
||||
|
//处理密码
|
||||
|
String digestAsHex = DigestUtils.md5DigestAsHex((AccountConfig.ACCOUNT_SALT + req.getPassword()).getBytes()); |
||||
|
AccountDO accountDO = accountMapper.selectOne(new QueryWrapper<AccountDO>().eq("phone", req.getPhone()).eq("password", digestAsHex)); |
||||
|
if(accountDO == null){ |
||||
|
throw new BizException(BizCodeEnum.ACCOUNT_PWD_ERROR); |
||||
|
} |
||||
|
return SpringBeanUtil.copyProperties(accountDO,AccountDTO.class); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AccountDTO queryDetail(Long id) { |
||||
|
|
||||
|
//账号详情
|
||||
|
AccountDO accountDO = accountMapper.selectById(id); |
||||
|
AccountDTO accountDTO = SpringBeanUtil.copyProperties(accountDO, AccountDTO.class); |
||||
|
|
||||
|
//获取存储信息
|
||||
|
StorageDO storageDO = storageMapper.selectOne(new QueryWrapper<StorageDO>().eq("account_id", id)); |
||||
|
accountDTO.setStorageDTO(SpringBeanUtil.copyProperties(storageDO, StorageDTO.class)); |
||||
|
|
||||
|
//获取文件信息
|
||||
|
AccountFileDO accountFileDO = accountFileMapper.selectOne(new QueryWrapper<AccountFileDO>() |
||||
|
.eq("account_id", id).eq("parent_id", AccountConfig.ROOT_PARENT_ID)); |
||||
|
if (accountFileDO != null) { |
||||
|
accountDTO.setRootFileId(accountFileDO.getId()); |
||||
|
accountDTO.setRootFileName(accountFileDO.getFileName()); |
||||
|
} |
||||
|
return accountDTO; |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
package org.ycloud.aipan.service.impl; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.ycloud.aipan.service.FileService; |
||||
|
|
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class FileServiceImpl implements FileService { |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,61 @@ |
|||||
|
package org.ycloud.aipan.util; |
||||
|
|
||||
|
import cn.hutool.core.date.DateUtil; |
||||
|
import cn.hutool.core.util.IdUtil; |
||||
|
import cn.hutool.core.util.StrUtil; |
||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.io.PrintWriter; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class CommonUtil { |
||||
|
|
||||
|
/** |
||||
|
* 响应json数据给前端 |
||||
|
* |
||||
|
* @param response HttpServletResponse对象,用于向客户端发送响应 |
||||
|
* @param obj 需要转换为json格式的对象 |
||||
|
*/ |
||||
|
public static void sendJsonMessage(HttpServletResponse response, Object obj) { |
||||
|
// 设置响应内容类型为json,并指定字符编码为utf-8
|
||||
|
response.setContentType("application/json; charset=utf-8"); |
||||
|
|
||||
|
try (PrintWriter writer = response.getWriter()) { |
||||
|
// 将对象转换为json字符串并写入响应输出流
|
||||
|
writer.print(JsonUtil.obj2Json(obj)); |
||||
|
// 刷新缓冲区,确保数据被发送到客户端
|
||||
|
response.flushBuffer(); |
||||
|
|
||||
|
} catch (IOException e) { |
||||
|
// 捕获并记录异常信息
|
||||
|
log.warn("响应json数据给前端异常:{}", e.getMessage()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据文件名称获取文件后缀 |
||||
|
* |
||||
|
* @param fileName 文件名 |
||||
|
* @return 文件后缀名 |
||||
|
*/ |
||||
|
public static String getFileSuffix(String fileName) { |
||||
|
// 从文件名中提取后缀名
|
||||
|
return fileName.substring(fileName.lastIndexOf(".") + 1); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据文件后缀,生成文件存储路径:年/月/日/uuid.suffix 格式 |
||||
|
* |
||||
|
* @param fileName 文件名 |
||||
|
* @return 生成的文件存储路径 |
||||
|
*/ |
||||
|
public static String getFilePath(String fileName) { |
||||
|
// 获取文件后缀名
|
||||
|
String suffix = getFileSuffix(fileName); |
||||
|
// 生成文件在存储桶中的唯一键
|
||||
|
return StrUtil.format("{}/{}/{}/{}.{}", DateUtil.thisYear(), DateUtil.thisMonth() + 1, DateUtil.thisDayOfMonth(), IdUtil.randomUUID(), suffix); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,102 @@ |
|||||
|
package org.ycloud.aipan.util; |
||||
|
|
||||
|
import com.alibaba.fastjson2.JSON; |
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Data; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import org.ycloud.aipan.enums.BizCodeEnum; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 响应类 |
||||
|
*/ |
||||
|
@Data |
||||
|
@AllArgsConstructor |
||||
|
@NoArgsConstructor |
||||
|
public class JsonData { |
||||
|
|
||||
|
/** |
||||
|
* 状态码 0 表示成功 |
||||
|
*/ |
||||
|
|
||||
|
private Integer code; |
||||
|
/** |
||||
|
* 数据 |
||||
|
*/ |
||||
|
private Object data; |
||||
|
/** |
||||
|
* 描述 |
||||
|
*/ |
||||
|
private String msg; |
||||
|
|
||||
|
/** |
||||
|
* 获取远程调用数据 |
||||
|
* |
||||
|
* @param typeReference 数据类型的引用 |
||||
|
* @param <T> 泛型类型 |
||||
|
* @return 返回解析后的对象 |
||||
|
*/ |
||||
|
public <T> T getData(Class<T> typeReference) { |
||||
|
return JSON.parseObject(JSON.toJSONString(data), typeReference); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 成功,不传入数据 |
||||
|
* |
||||
|
* @return 返回一个状态码为0的JsonData对象 |
||||
|
*/ |
||||
|
public static JsonData buildSuccess() { |
||||
|
return new JsonData(0, null, null); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 成功,传入数据 |
||||
|
* |
||||
|
* @param data 成功时返回的数据 |
||||
|
* @return 返回一个JsonData对象,其中包含状态码0和传入的数据 |
||||
|
*/ |
||||
|
public static JsonData buildSuccess(Object data) { |
||||
|
return new JsonData(0, data, null); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 失败,传入描述信息 |
||||
|
* |
||||
|
* @param msg 失败时的描述信息 |
||||
|
* @return 返回一个JsonData对象,其中包含状态码-1和传入的描述信息 |
||||
|
*/ |
||||
|
public static JsonData buildError(String msg) { |
||||
|
return new JsonData(-1, null, msg); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 自定义状态码和错误信息 |
||||
|
* |
||||
|
* @param code 自定义的状态码 |
||||
|
* @param msg 自定义的错误信息 |
||||
|
* @return 返回一个JsonData对象,其中包含传入的状态码和错误信息 |
||||
|
*/ |
||||
|
public static JsonData buildCodeAndMsg(int code, String msg) { |
||||
|
return new JsonData(code, null, msg); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 自定义状态码和错误信息 |
||||
|
* |
||||
|
* @param codeEnum 自定义的状态码枚举 |
||||
|
* @return 返回一个JsonData对象,其中包含传入的状态码枚举对应的状态码和错误信息 |
||||
|
*/ |
||||
|
public static JsonData buildResult(BizCodeEnum codeEnum) { |
||||
|
return JsonData.buildCodeAndMsg(codeEnum.getCode(), codeEnum.getMessage()); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断当前JsonData对象是否表示成功 |
||||
|
* |
||||
|
* @return 如果状态码为0,则返回true,表示成功;否则返回false,表示失败 |
||||
|
*/ |
||||
|
public boolean isSuccess() { |
||||
|
return code == 0; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,99 @@ |
|||||
|
package org.ycloud.aipan.util; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonInclude; |
||||
|
import com.fasterxml.jackson.core.JsonParser; |
||||
|
import com.fasterxml.jackson.core.JsonProcessingException; |
||||
|
import com.fasterxml.jackson.databind.DeserializationFeature; |
||||
|
import com.fasterxml.jackson.databind.JavaType; |
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import com.fasterxml.jackson.databind.SerializationFeature; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
|
||||
|
import java.text.SimpleDateFormat; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class JsonUtil { |
||||
|
// 创建一个ObjectMapper对象,用于处理JSON数据的序列化和反序列化
|
||||
|
private static final ObjectMapper MAPPER = new ObjectMapper(); |
||||
|
|
||||
|
// 静态代码块,用于初始化ObjectMapper对象的配置
|
||||
|
static { |
||||
|
//设置可用单引号
|
||||
|
MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); |
||||
|
//序列化的时候序列对象的所有属性
|
||||
|
MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS); |
||||
|
//反序列化的时候如果多了其他属性,不抛出异常
|
||||
|
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); |
||||
|
//下划线和驼峰互转
|
||||
|
//mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||
|
//如果是空对象的时候,不抛异常
|
||||
|
MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); |
||||
|
//取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
|
||||
|
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); |
||||
|
MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取ObjectMapper对象 |
||||
|
* |
||||
|
* @return ObjectMapper对象 |
||||
|
*/ |
||||
|
public static ObjectMapper get() { |
||||
|
return MAPPER; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将对象转换为JSON字符串 |
||||
|
* |
||||
|
* @param obj 要转换的对象 |
||||
|
* @return JSON字符串 |
||||
|
*/ |
||||
|
public static String obj2Json(Object obj) { |
||||
|
String jsonStr = null; |
||||
|
try { |
||||
|
jsonStr = MAPPER.writeValueAsString(obj); |
||||
|
} catch (JsonProcessingException e) { |
||||
|
log.error("json格式化异常", e); |
||||
|
} |
||||
|
return jsonStr; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将JSON字符串转换为对象 |
||||
|
* |
||||
|
* @param jsonStr 要转换的JSON字符串 |
||||
|
* @param beanType 目标对象的类型 |
||||
|
* @return 转换后的对象 |
||||
|
*/ |
||||
|
public static <T> T json2Obj(String jsonStr, Class<T> beanType) { |
||||
|
T obj = null; |
||||
|
try { |
||||
|
obj = MAPPER.readValue(jsonStr, beanType); |
||||
|
} catch (Exception e) { |
||||
|
log.error("json格式化异常", e); |
||||
|
} |
||||
|
return obj; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将JSON数据转换为对象列表 |
||||
|
* |
||||
|
* @param jsonData 要转换的JSON数据 |
||||
|
* @param beanType 目标对象的类型 |
||||
|
* @return 转换后的对象列表 |
||||
|
*/ |
||||
|
public static <T> List<T> json2List(String jsonData, Class<T> beanType) { |
||||
|
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType); |
||||
|
try { |
||||
|
// 使用ObjectMapper将JSON数据转换为对象列表
|
||||
|
return MAPPER.readValue(jsonData, javaType); |
||||
|
} catch (Exception e) { |
||||
|
log.error("json格式化异常", e); |
||||
|
} |
||||
|
// 返回空列表
|
||||
|
return new ArrayList<>(0); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,121 @@ |
|||||
|
package org.ycloud.aipan.util; |
||||
|
|
||||
|
import io.jsonwebtoken.Claims; |
||||
|
import io.jsonwebtoken.Jwts; |
||||
|
import io.jsonwebtoken.security.Keys; |
||||
|
import io.jsonwebtoken.security.SecureDigestAlgorithm; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.ycloud.aipan.dto.AccountDTO; |
||||
|
|
||||
|
import javax.crypto.SecretKey; |
||||
|
import java.util.Date; |
||||
|
|
||||
|
|
||||
|
@Slf4j |
||||
|
public class JwtUtil { |
||||
|
|
||||
|
// JWT的主题
|
||||
|
private static final String LOGIN_SUBJECT = "YUAN"; |
||||
|
|
||||
|
|
||||
|
//注意这个密钥长度需要足够长, 推荐:JWT的密钥,从环境变量中获取
|
||||
|
private final static String SECRET_KEY = "yuan.yuannet.netyuan.password.3421.sdfnet.com.efszyuaszgh"; |
||||
|
// 签名算法
|
||||
|
private final static SecureDigestAlgorithm<SecretKey, SecretKey> ALGORITHM = Jwts.SIG.HS256; |
||||
|
// 使用密钥
|
||||
|
private final static SecretKey KEY = Keys.hmacShaKeyFor(SECRET_KEY.getBytes()); |
||||
|
// token过期时间,30天
|
||||
|
private static final long EXPIRED = 1000 * 60 * 60 * 24 * 7; |
||||
|
|
||||
|
/** |
||||
|
* 生成JWT |
||||
|
* |
||||
|
* @param accountDTO 登录账户信息 |
||||
|
* @return 生成的JWT字符串 |
||||
|
* @throws NullPointerException 如果传入的accountDTO为空 |
||||
|
*/ |
||||
|
public static String geneLoginJWT(AccountDTO accountDTO) { |
||||
|
if (accountDTO == null) { |
||||
|
throw new NullPointerException("对象为空"); |
||||
|
} |
||||
|
|
||||
|
// 创建 JWT token
|
||||
|
String token = Jwts.builder() |
||||
|
.subject(LOGIN_SUBJECT) |
||||
|
.claim("accountId", accountDTO.getId()) |
||||
|
.claim("username", accountDTO.getUsername()) |
||||
|
.issuedAt(new Date()) |
||||
|
.expiration(new Date(System.currentTimeMillis() + EXPIRED)) |
||||
|
.signWith(KEY, ALGORITHM) // 直接使用KEY即可
|
||||
|
.compact(); |
||||
|
|
||||
|
// 添加自定义前缀
|
||||
|
return addPrefix(token, LOGIN_SUBJECT); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 校验JWT |
||||
|
* |
||||
|
* @param token JWT字符串 |
||||
|
* @return JWT的Claims部分 |
||||
|
* @throws IllegalArgumentException 如果传入的token为空或只包含空白字符 |
||||
|
* @throws RuntimeException 如果JWT签名验证失败、JWT已过期或JWT解密失败 |
||||
|
*/ |
||||
|
public static Claims checkLoginJWT(String token) { |
||||
|
try { |
||||
|
log.debug("开始校验 JWT: {}", token); |
||||
|
// 校验 Token 是否为空
|
||||
|
if (token == null || token.trim().isEmpty()) { |
||||
|
log.error("Token 不能为空"); |
||||
|
throw new IllegalArgumentException("Token 不能为空"); |
||||
|
} |
||||
|
token = token.trim(); |
||||
|
// 移除前缀
|
||||
|
token = removePrefix(token, LOGIN_SUBJECT); |
||||
|
log.debug("移除前缀后的 Token: {}", token); |
||||
|
// 解析 JWT
|
||||
|
Claims payload = Jwts.parser() |
||||
|
.verifyWith(KEY) //设置签名的密钥, 使用相同的 KEY
|
||||
|
.build() |
||||
|
.parseSignedClaims(token).getPayload(); |
||||
|
|
||||
|
log.info("JWT 解密成功,Claims: {}", payload); |
||||
|
return payload; |
||||
|
} catch (IllegalArgumentException e) { |
||||
|
log.error("JWT 校验失败: {}", e.getMessage(), e); |
||||
|
throw e; |
||||
|
} catch (io.jsonwebtoken.security.SignatureException e) { |
||||
|
log.error("JWT 签名验证失败: {}", e.getMessage(), e); |
||||
|
throw new RuntimeException("JWT 签名验证失败", e); |
||||
|
} catch (io.jsonwebtoken.ExpiredJwtException e) { |
||||
|
log.error("JWT 已过期: {}", e.getMessage(), e); |
||||
|
throw new RuntimeException("JWT 已过期", e); |
||||
|
} catch (Exception e) { |
||||
|
log.error("JWT 解密失败: {}", e.getMessage(), e); |
||||
|
throw new RuntimeException("JWT 解密失败", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 给token添加前缀 |
||||
|
* |
||||
|
* @param token 原始token字符串 |
||||
|
* @return 添加前缀后的token字符串 |
||||
|
*/ |
||||
|
private static String addPrefix(String token, String prefix) { |
||||
|
return prefix + token; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 移除token的前缀 |
||||
|
* |
||||
|
* @param token 带前缀的token字符串 |
||||
|
* @return 移除前缀后的token字符串 |
||||
|
*/ |
||||
|
private static String removePrefix(String token, String prefix) { |
||||
|
if (token.startsWith(prefix)) { |
||||
|
return token.replace(prefix, "").trim(); |
||||
|
} |
||||
|
return token; |
||||
|
} |
||||
|
} |
@ -0,0 +1,59 @@ |
|||||
|
package org.ycloud.aipan.util; |
||||
|
|
||||
|
import org.springframework.beans.BeanUtils; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* SpringBeanUtil 工具类,提供了对象属性复制的功能。 |
||||
|
*/ |
||||
|
public class SpringBeanUtil { |
||||
|
|
||||
|
/** |
||||
|
* 复制属性 |
||||
|
* |
||||
|
* @param <T> 目标对象类型 |
||||
|
* @param source 源对象 |
||||
|
* @param target 目标对象类型 |
||||
|
* @return 复制后的目标对象 |
||||
|
*/ |
||||
|
public static <T> T copyProperties(Object source, Class<T> target) { |
||||
|
try { |
||||
|
T t = target.getConstructor().newInstance(); |
||||
|
BeanUtils.copyProperties(source, t); |
||||
|
return t; |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 复制一份具有相同属性的列表 |
||||
|
* |
||||
|
* @param sourceList 源列表 |
||||
|
* @param target 目标对象的类型 |
||||
|
* @param <T> 目标对象的类型 |
||||
|
* @return 复制后的目标列表 |
||||
|
*/ |
||||
|
public static <T> List<T> copyProperties(List<?> sourceList, Class<T> target) { |
||||
|
ArrayList<T> targetList = new ArrayList<>(); |
||||
|
sourceList.forEach(source -> { |
||||
|
T t = copyProperties(source, target); |
||||
|
targetList.add(t); |
||||
|
}); |
||||
|
return targetList; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 复制属性 |
||||
|
* |
||||
|
* @param source 源对象 |
||||
|
* @param target 目标对象 |
||||
|
*/ |
||||
|
public static void copyProperties(Object source, Object target){ |
||||
|
BeanUtils.copyProperties(source,target); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
server: |
||||
|
port: 8081 |
||||
|
|
||||
|
spring: |
||||
|
application: |
||||
|
name: ycloud-aipan |
||||
|
|
||||
|
datasource: |
||||
|
driver-class-name: com.mysql.cj.jdbc.Driver |
||||
|
url: jdbc:mysql://47.98.137.138:3306/ycloud-aipan?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true |
||||
|
username: root |
||||
|
password: Yuan625621105 |
||||
|
|
||||
|
data: |
||||
|
redis: |
||||
|
host: 47.98.137.138 |
||||
|
port: 6379 |
||||
|
password: yuan625621105 |
||||
|
|
||||
|
|
||||
|
# MyBatis-Plus配置 |
||||
|
mybatis-plus: |
||||
|
# 全局配置 |
||||
|
global-config: |
||||
|
# 数据库配置 |
||||
|
db-config: |
||||
|
# 逻辑删除字段 |
||||
|
logic-delete-field: del |
||||
|
# 逻辑删除值 |
||||
|
logic-delete-value: 1 |
||||
|
# 逻辑未删除值 |
||||
|
logic-not-delete-value: 0 |
||||
|
# 配置 |
||||
|
configuration: |
||||
|
# 日志实现 |
||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl |
||||
|
# 驼峰命名转换 |
||||
|
map-underscore-to-camel-case: true |
||||
|
|
||||
|
|
||||
|
# minio配置 |
||||
|
minio: |
||||
|
endpoint: http://192.168.60.124:9000 |
||||
|
access-key: minio_root |
||||
|
access-secret: minio_123456 |
||||
|
bucket-name: ai-pan |
||||
|
avatar-bucket-name: avatar |
@ -0,0 +1,27 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="org.ycloud.aipan.mapper.AccountFileMapper"> |
||||
|
|
||||
|
<!-- 通用查询映射结果 --> |
||||
|
<resultMap id="BaseResultMap" type="org.ycloud.aipan.model.AccountFileDO"> |
||||
|
<id column="id" property="id" /> |
||||
|
<result column="account_id" property="accountId" /> |
||||
|
<result column="is_dir" property="isDir" /> |
||||
|
<result column="parent_id" property="parentId" /> |
||||
|
<result column="file_id" property="fileId" /> |
||||
|
<result column="file_name" property="fileName" /> |
||||
|
<result column="file_type" property="fileType" /> |
||||
|
<result column="file_suffix" property="fileSuffix" /> |
||||
|
<result column="file_size" property="fileSize" /> |
||||
|
<result column="del" property="del" /> |
||||
|
<result column="del_time" property="delTime" /> |
||||
|
<result column="gmt_modified" property="gmtModified" /> |
||||
|
<result column="gmt_create" property="gmtCreate" /> |
||||
|
</resultMap> |
||||
|
|
||||
|
<!-- 通用查询结果列 --> |
||||
|
<sql id="Base_Column_List"> |
||||
|
id, account_id, is_dir, parent_id, file_id, file_name, file_type, file_suffix, file_size, del, del_time, gmt_modified, gmt_create |
||||
|
</sql> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,23 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="org.ycloud.aipan.mapper.AccountMapper"> |
||||
|
|
||||
|
<!-- 通用查询映射结果 --> |
||||
|
<resultMap id="BaseResultMap" type="org.ycloud.aipan.model.AccountDO"> |
||||
|
<id column="id" property="id" /> |
||||
|
<result column="username" property="username" /> |
||||
|
<result column="password" property="password" /> |
||||
|
<result column="avatar_url" property="avatarUrl" /> |
||||
|
<result column="phone" property="phone" /> |
||||
|
<result column="role" property="role" /> |
||||
|
<result column="del" property="del" /> |
||||
|
<result column="gmt_create" property="gmtCreate" /> |
||||
|
<result column="gmt_modified" property="gmtModified" /> |
||||
|
</resultMap> |
||||
|
|
||||
|
<!-- 通用查询结果列 --> |
||||
|
<sql id="Base_Column_List"> |
||||
|
id, username, password, avatar_url, phone, role, del, gmt_create, gmt_modified |
||||
|
</sql> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,26 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="org.ycloud.aipan.mapper.FileChunkMapper"> |
||||
|
|
||||
|
<!-- 通用查询映射结果 --> |
||||
|
<resultMap id="BaseResultMap" type="org.ycloud.aipan.model.FileChunkDO"> |
||||
|
<id column="id" property="id" /> |
||||
|
<result column="identifier" property="identifier" /> |
||||
|
<result column="upload_id" property="uploadId" /> |
||||
|
<result column="file_name" property="fileName" /> |
||||
|
<result column="bucket_name" property="bucketName" /> |
||||
|
<result column="object_key" property="objectKey" /> |
||||
|
<result column="total_size" property="totalSize" /> |
||||
|
<result column="chunk_size" property="chunkSize" /> |
||||
|
<result column="chunk_num" property="chunkNum" /> |
||||
|
<result column="account_id" property="accountId" /> |
||||
|
<result column="gmt_create" property="gmtCreate" /> |
||||
|
<result column="gmt_modified" property="gmtModified" /> |
||||
|
</resultMap> |
||||
|
|
||||
|
<!-- 通用查询结果列 --> |
||||
|
<sql id="Base_Column_List"> |
||||
|
id, identifier, upload_id, file_name, bucket_name, object_key, total_size, chunk_size, chunk_num, account_id, gmt_create, gmt_modified |
||||
|
</sql> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,24 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="org.ycloud.aipan.mapper.FileMapper"> |
||||
|
|
||||
|
<!-- 通用查询映射结果 --> |
||||
|
<resultMap id="BaseResultMap" type="org.ycloud.aipan.model.FileDO"> |
||||
|
<id column="id" property="id" /> |
||||
|
<result column="account_id" property="accountId" /> |
||||
|
<result column="file_name" property="fileName" /> |
||||
|
<result column="file_suffix" property="fileSuffix" /> |
||||
|
<result column="file_size" property="fileSize" /> |
||||
|
<result column="object_key" property="objectKey" /> |
||||
|
<result column="identifier" property="identifier" /> |
||||
|
<result column="del" property="del" /> |
||||
|
<result column="gmt_modified" property="gmtModified" /> |
||||
|
<result column="gmt_create" property="gmtCreate" /> |
||||
|
</resultMap> |
||||
|
|
||||
|
<!-- 通用查询结果列 --> |
||||
|
<sql id="Base_Column_List"> |
||||
|
id, account_id, file_name, file_suffix, file_size, object_key, identifier, del, gmt_modified, gmt_create |
||||
|
</sql> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,17 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="org.ycloud.aipan.mapper.FileSuffixMapper"> |
||||
|
|
||||
|
<!-- 通用查询映射结果 --> |
||||
|
<resultMap id="BaseResultMap" type="org.ycloud.aipan.model.FileSuffixDO"> |
||||
|
<id column="id" property="id" /> |
||||
|
<result column="file_suffix" property="fileSuffix" /> |
||||
|
<result column="file_type_id" property="fileTypeId" /> |
||||
|
</resultMap> |
||||
|
|
||||
|
<!-- 通用查询结果列 --> |
||||
|
<sql id="Base_Column_List"> |
||||
|
id, file_suffix, file_type_id |
||||
|
</sql> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,16 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="org.ycloud.aipan.mapper.FileTypeMapper"> |
||||
|
|
||||
|
<!-- 通用查询映射结果 --> |
||||
|
<resultMap id="BaseResultMap" type="org.ycloud.aipan.model.FileTypeDO"> |
||||
|
<id column="id" property="id" /> |
||||
|
<result column="file_type_name" property="fileTypeName" /> |
||||
|
</resultMap> |
||||
|
|
||||
|
<!-- 通用查询结果列 --> |
||||
|
<sql id="Base_Column_List"> |
||||
|
id, file_type_name |
||||
|
</sql> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="org.ycloud.aipan.mapper.ShareFileMapper"> |
||||
|
|
||||
|
<!-- 通用查询映射结果 --> |
||||
|
<resultMap id="BaseResultMap" type="org.ycloud.aipan.model.ShareFileDO"> |
||||
|
<id column="id" property="id" /> |
||||
|
<result column="share_id" property="shareId" /> |
||||
|
<result column="account_file_id" property="accountFileId" /> |
||||
|
<result column="account_id" property="accountId" /> |
||||
|
<result column="gmt_create" property="gmtCreate" /> |
||||
|
<result column="gmt_modified" property="gmtModified" /> |
||||
|
</resultMap> |
||||
|
|
||||
|
<!-- 通用查询结果列 --> |
||||
|
<sql id="Base_Column_List"> |
||||
|
id, share_id, account_file_id, account_id, gmt_create, gmt_modified |
||||
|
</sql> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,26 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="org.ycloud.aipan.mapper.ShareMapper"> |
||||
|
|
||||
|
<!-- 通用查询映射结果 --> |
||||
|
<resultMap id="BaseResultMap" type="org.ycloud.aipan.model.ShareDO"> |
||||
|
<id column="id" property="id" /> |
||||
|
<result column="share_name" property="shareName" /> |
||||
|
<result column="share_type" property="shareType" /> |
||||
|
<result column="share_day_type" property="shareDayType" /> |
||||
|
<result column="share_day" property="shareDay" /> |
||||
|
<result column="share_end_time" property="shareEndTime" /> |
||||
|
<result column="share_url" property="shareUrl" /> |
||||
|
<result column="share_code" property="shareCode" /> |
||||
|
<result column="share_status" property="shareStatus" /> |
||||
|
<result column="account_id" property="accountId" /> |
||||
|
<result column="gmt_create" property="gmtCreate" /> |
||||
|
<result column="gmt_modified" property="gmtModified" /> |
||||
|
</resultMap> |
||||
|
|
||||
|
<!-- 通用查询结果列 --> |
||||
|
<sql id="Base_Column_List"> |
||||
|
id, share_name, share_type, share_day_type, share_day, share_end_time, share_url, share_code, share_status, account_id, gmt_create, gmt_modified |
||||
|
</sql> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
|
<mapper namespace="org.ycloud.aipan.mapper.StorageMapper"> |
||||
|
|
||||
|
<!-- 通用查询映射结果 --> |
||||
|
<resultMap id="BaseResultMap" type="org.ycloud.aipan.model.StorageDO"> |
||||
|
<id column="id" property="id" /> |
||||
|
<result column="account_id" property="accountId" /> |
||||
|
<result column="used_size" property="usedSize" /> |
||||
|
<result column="total_size" property="totalSize" /> |
||||
|
<result column="gmt_create" property="gmtCreate" /> |
||||
|
<result column="gmt_modified" property="gmtModified" /> |
||||
|
</resultMap> |
||||
|
|
||||
|
<!-- 通用查询结果列 --> |
||||
|
<sql id="Base_Column_List"> |
||||
|
id, account_id, used_size, total_size, gmt_create, gmt_modified |
||||
|
</sql> |
||||
|
|
||||
|
</mapper> |
@ -0,0 +1,37 @@ |
|||||
|
package org.ycloud.aipan; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.junit.jupiter.api.Test; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.boot.test.context.SpringBootTest; |
||||
|
import org.ycloud.aipan.controller.req.AccountLoginReq; |
||||
|
import org.ycloud.aipan.controller.req.AccountRegisterReq; |
||||
|
import org.ycloud.aipan.dto.AccountDTO; |
||||
|
import org.ycloud.aipan.service.AccountService; |
||||
|
import org.ycloud.aipan.util.JwtUtil; |
||||
|
|
||||
|
@SpringBootTest |
||||
|
@Slf4j |
||||
|
public class AccountTest { |
||||
|
|
||||
|
@Autowired |
||||
|
private AccountService accountService; |
||||
|
|
||||
|
/** |
||||
|
* 注册方法测试 |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testRegister() { |
||||
|
accountService.register(AccountRegisterReq.builder().phone("15600000000").password("123456").username("yuan").avatarUrl("https://yn-blog.oss-cn-chengdu.aliyuncs.com/index/logo.png").build()); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 登录方法测试 |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testLogin() { |
||||
|
AccountDTO accountDTO = accountService.login(AccountLoginReq.builder().phone("15600000000").password("123456").build()); |
||||
|
String token = JwtUtil.geneLoginJWT(accountDTO); |
||||
|
log.info("token: {}", token); |
||||
|
} |
||||
|
} |
@ -0,0 +1,183 @@ |
|||||
|
package org.ycloud.aipan; |
||||
|
|
||||
|
import cn.hutool.core.date.DateUtil; |
||||
|
import com.amazonaws.HttpMethod; |
||||
|
import com.amazonaws.services.s3.AmazonS3Client; |
||||
|
import com.amazonaws.services.s3.model.*; |
||||
|
import com.amazonaws.util.IOUtils; |
||||
|
import lombok.SneakyThrows; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.junit.jupiter.api.Test; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.boot.test.context.SpringBootTest; |
||||
|
|
||||
|
import java.io.ByteArrayInputStream; |
||||
|
import java.io.File; |
||||
|
import java.io.FileInputStream; |
||||
|
import java.io.FileOutputStream; |
||||
|
import java.net.URL; |
||||
|
import java.util.Date; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
@SpringBootTest |
||||
|
@Slf4j |
||||
|
class AmazonS3ClientTests { |
||||
|
|
||||
|
@Autowired |
||||
|
private AmazonS3Client amazonS3Client; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 判断bucket是否存在 |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testBucketExists() { |
||||
|
boolean bucketExist = amazonS3Client.doesBucketExist("ai-pan1"); |
||||
|
log.info("bucket是否存在:{}", bucketExist); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建bucket |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testCreateBucket() { |
||||
|
String bucketName = "avatar"; |
||||
|
Bucket bucket = amazonS3Client.createBucket(bucketName); |
||||
|
log.info("bucket:{}", bucket); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除bucket |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testDeleteBucket() { |
||||
|
String bucketName = "ai-pan1"; |
||||
|
amazonS3Client.deleteBucket(bucketName); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取全部bucket |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testListBuckets() { |
||||
|
for (Bucket bucket : amazonS3Client.listBuckets()) { |
||||
|
log.info("bucket:{}", bucket.getName()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据bucket名称获取bucket详情 |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testGetBucket() { |
||||
|
String bucketName = "ai-pan1"; |
||||
|
Optional<Bucket> optionalBucket = amazonS3Client.listBuckets().stream().filter(bucket -> bucketName.equals(bucket.getName())).findFirst(); |
||||
|
if (optionalBucket.isPresent()) { |
||||
|
log.info("bucket:{}", optionalBucket.get()); |
||||
|
} else { |
||||
|
log.info("bucket不存在"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传单个文件,直接写入文本 |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testUploadFile() { |
||||
|
PutObjectResult putObject = amazonS3Client.putObject("ai-pan", "test1.txt", "hello world11"); |
||||
|
log.info("putObject:{}", putObject); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传单个文件,直接写入文本 |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testUploadFile2() { |
||||
|
amazonS3Client.putObject("ai-pan", "test2.txt", new File("/Users/xdclass/Desktop/dpan.sql")); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传文件 包括文件夹路径 不带斜杠 都一样 |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testUploadFileWithDir1() { |
||||
|
amazonS3Client.putObject("ai-pan", "aa/bb/test3.txt", new File("/Users/xdclass/Desktop/dpan.sql")); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传文件 包括文件夹路径 带斜杠 都一样 |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testUploadFileWithDir2() { |
||||
|
amazonS3Client.putObject("ai-pan", "/a/b/test4.txt", new File("/Users/xdclass/Desktop/dpan.sql")); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传文件,输入流的方式 带上文件元数据 |
||||
|
*/ |
||||
|
@Test |
||||
|
@SneakyThrows |
||||
|
public void testUploadFileWithMetadata() { |
||||
|
try (FileInputStream fileInputStream = new FileInputStream("/Users/xdclass/Desktop/dpan.sql");) { |
||||
|
ObjectMetadata objectMetadata = new ObjectMetadata(); |
||||
|
objectMetadata.setContentType("text/plain"); |
||||
|
amazonS3Client.putObject("ai-pan", "/meta/test5.txt", fileInputStream, objectMetadata); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传文件,输入流的方式 带上文件元数据 |
||||
|
*/ |
||||
|
@Test |
||||
|
@SneakyThrows |
||||
|
public void testUploadFileWithMetadata2() { |
||||
|
try (FileInputStream stream = new FileInputStream("/Users/xdclass/Desktop/dpan.sql");) { |
||||
|
byte[] bytes = IOUtils.toByteArray(stream); |
||||
|
ObjectMetadata objectMetadata = new ObjectMetadata(); |
||||
|
objectMetadata.setContentType("text/plain"); |
||||
|
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); |
||||
|
// 上传
|
||||
|
amazonS3Client.putObject("ai-pan", "/meta/testIO.txt", byteArrayInputStream, objectMetadata); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取文件 |
||||
|
*/ |
||||
|
@Test |
||||
|
@SneakyThrows |
||||
|
public void testGetObject() { |
||||
|
try (FileOutputStream fileOutputStream = new FileOutputStream(new File("/Users/xdclass/Desktop/test5.txt"));) { |
||||
|
S3Object s3Object = amazonS3Client.getObject("ai-pan", "/meta/test5.txt"); |
||||
|
s3Object.getObjectContent().transferTo(fileOutputStream); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除文件 |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testDeleteObject() { |
||||
|
amazonS3Client.deleteObject("ai-pan", "/meta/test5.txt"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成文件访问地址 |
||||
|
*/ |
||||
|
@Test |
||||
|
public void testGeneratePresignedUrl() { |
||||
|
// 预签名url过期时间(ms)
|
||||
|
long PRE_SIGN_URL_EXPIRE = 60 * 10 * 1000L; |
||||
|
// 计算预签名url的过期日期
|
||||
|
Date expireDate = DateUtil.offsetMillisecond(new Date(), (int) PRE_SIGN_URL_EXPIRE); |
||||
|
// 创建生成预签名url的请求,并设置过期时间和HTTP方法, withMethod是生成的URL访问方式
|
||||
|
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest("avatar", "/2025/3/22/999ae223-cf98-4891-a7da-e3ab1d618719.jpg") |
||||
|
.withExpiration(expireDate).withMethod(HttpMethod.GET); |
||||
|
// 生成预签名url
|
||||
|
URL preSignedUrl = amazonS3Client.generatePresignedUrl(request); |
||||
|
// 输出预签名url
|
||||
|
System.out.println(preSignedUrl.toString()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,75 @@ |
|||||
|
package org.ycloud.aipan.db; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.IdType; |
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import com.baomidou.mybatisplus.generator.FastAutoGenerator; |
||||
|
import com.baomidou.mybatisplus.generator.config.OutputFile; |
||||
|
import com.baomidou.mybatisplus.generator.config.TemplateType; |
||||
|
import com.baomidou.mybatisplus.generator.config.rules.DateType; |
||||
|
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType; |
||||
|
import org.apache.ibatis.type.JdbcType; |
||||
|
|
||||
|
import java.util.Collections; |
||||
|
|
||||
|
|
||||
|
public class MyBatisPlusGenerator { |
||||
|
public static void main(String[] args) { |
||||
|
String userName = "root"; |
||||
|
String password = "Yuan625621105"; |
||||
|
String serverInfo = "47.98.137.138:3306"; |
||||
|
String targetModuleNamePath = "/"; |
||||
|
String dbName = "ycloud-aipan"; |
||||
|
|
||||
|
String[] tables = { |
||||
|
"account", "file","account_file","file_chunk", "file_suffix","file_type", "share", "share_file", "storage" |
||||
|
}; |
||||
|
// 使用 FastAutoGenerator 快速配置代码生成器
|
||||
|
FastAutoGenerator.create("jdbc:mysql://"+serverInfo+"/"+dbName+"?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&tinyInt1isBit=true", userName, password) |
||||
|
.globalConfig(builder -> { |
||||
|
builder.author("everyone") // 设置作者
|
||||
|
.commentDate("yyyy-MM-dd") |
||||
|
.enableSpringdoc() |
||||
|
.disableOpenDir() //禁止打开输出目录
|
||||
|
.dateType(DateType.ONLY_DATE) //定义生成的实体类中日期类型 DateType.ONLY_DATE 默认值: DateType.TIME_PACK
|
||||
|
.outputDir(System.getProperty("user.dir") + targetModuleNamePath + "/src/main/java"); // 指定输出目录
|
||||
|
}) |
||||
|
.packageConfig(builder -> { |
||||
|
builder.parent("org.ycloud.aipan") // 父包模块名
|
||||
|
.entity("model") //Entity 包名 默认值:entity
|
||||
|
.mapper("mapper") //Mapper 包名 默认值:mapper
|
||||
|
.pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + targetModuleNamePath + "/src/main/resources/mapper")); // 设置mapperXml生成路,默认存放在mapper的xml下
|
||||
|
}) |
||||
|
.dataSourceConfig(builder -> {//Mysql下tinyint字段转换
|
||||
|
builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> { |
||||
|
if (JdbcType.TINYINT == metaInfo.getJdbcType()) { |
||||
|
return DbColumnType.BOOLEAN; |
||||
|
} |
||||
|
return typeRegistry.getColumnType(metaInfo); |
||||
|
}); |
||||
|
}) |
||||
|
.strategyConfig(builder -> { |
||||
|
builder.addInclude(tables) // 设置需要生成的表名 可变参数
|
||||
|
.entityBuilder()// Entity策略配置
|
||||
|
.enableFileOverride() // 开启生成Entity层文件覆盖
|
||||
|
.idType(IdType.ASSIGN_ID)//主键策略 雪花算法自动生成的id
|
||||
|
.enableLombok() //开启lombok
|
||||
|
.logicDeleteColumnName("del")// 说明逻辑删除是哪个字段
|
||||
|
.enableTableFieldAnnotation()// 属性加上注解说明
|
||||
|
.formatFileName("%sDO") //格式化生成的文件名称
|
||||
|
.controllerBuilder().disable()// Controller策略配置,这里不生成Controller层
|
||||
|
.serviceBuilder().disable()// Service策略配置,这里不生成Service层
|
||||
|
.mapperBuilder()// Mapper策略配置
|
||||
|
.enableFileOverride() // 开启生成Mapper层文件覆盖
|
||||
|
.formatMapperFileName("%sMapper")// 格式化Mapper文件名称
|
||||
|
.superClass(BaseMapper.class) //继承的父类
|
||||
|
.enableBaseResultMap() // 开启生成resultMap,
|
||||
|
.enableBaseColumnList() // 开启生成Sql片段
|
||||
|
.formatXmlFileName("%sMapper"); // 格式化xml文件名称
|
||||
|
}) |
||||
|
.templateConfig(builder -> { |
||||
|
// 不生成Controller
|
||||
|
builder.disable(TemplateType.CONTROLLER,TemplateType.SERVICE,TemplateType.SERVICE_IMPL); |
||||
|
}) |
||||
|
.execute(); // 执行生成
|
||||
|
} |
||||
|
} |
Loading…
Reference in new issue