Skip to content

Commit 9f8b00a

Browse files
committed
feat(system): 系统支持连接对象存储(阿里云OSS腾讯云COS等)
系统支持连接对象存储(阿里云OSS腾讯云COS等)
1 parent 1a044c8 commit 9f8b00a

28 files changed

Lines changed: 1633 additions & 90 deletions

backend/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@
140140
<artifactId>aspectjweaver</artifactId>
141141
</dependency>
142142

143+
<!-- S3 -->
144+
<dependency>
145+
<groupId>software.amazon.awssdk</groupId>
146+
<artifactId>s3</artifactId>
147+
<version>2.29.0</version>
148+
</dependency>
149+
143150
</dependencies>
144151

145152

backend/src/main/java/xyz/lingview/dimstack/controller/FileAccessController.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import lombok.extern.slf4j.Slf4j;
44
import org.springframework.beans.factory.annotation.Autowired;
55
import org.springframework.http.HttpHeaders;
6+
import org.springframework.http.HttpStatus;
67
import org.springframework.http.MediaType;
78
import org.springframework.http.ResponseEntity;
89
import org.springframework.web.bind.annotation.GetMapping;
@@ -12,6 +13,8 @@
1213
import org.springframework.web.bind.annotation.RestController;
1314
import xyz.lingview.dimstack.service.FileAccessService;
1415

16+
import java.net.URI;
17+
1518
/**
1619
* @Author: lingview
1720
* @Date: 2025/11/12 09:03:20
@@ -37,6 +40,15 @@ public ResponseEntity<org.springframework.core.io.Resource> getFile(
3740
return ResponseEntity.notFound().build();
3841
}
3942

43+
// 外部存储:302 重定向
44+
if (result.redirectUrl() != null) {
45+
return ResponseEntity.status(HttpStatus.FOUND)
46+
.location(URI.create(result.redirectUrl()))
47+
.header(HttpHeaders.CONTENT_DISPOSITION, result.filename())
48+
.build();
49+
}
50+
51+
// 本地存储:直接返回文件内容
4052
return ResponseEntity.ok()
4153
.contentType(MediaType.parseMediaType(result.contentType()))
4254
.header(HttpHeaders.CONTENT_DISPOSITION, result.filename())
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package xyz.lingview.dimstack.controller;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.PostMapping;
7+
import org.springframework.web.bind.annotation.RequestBody;
8+
import org.springframework.web.bind.annotation.RequestMapping;
9+
import org.springframework.web.bind.annotation.RestController;
10+
import xyz.lingview.dimstack.annotation.RequiresPermission;
11+
import xyz.lingview.dimstack.common.ApiResponse;
12+
import xyz.lingview.dimstack.domain.StorageMethod;
13+
import xyz.lingview.dimstack.service.CurrentUserService;
14+
import xyz.lingview.dimstack.service.StorageMethodService;
15+
16+
import java.util.List;
17+
import java.util.Map;
18+
19+
/**
20+
* @Author: lingview
21+
* @Date: 2026/07/17 17:23:48
22+
* @Description: 存储方式管理控制器
23+
* @Version: 1.0
24+
*/
25+
@Slf4j
26+
@RestController
27+
@RequestMapping("/api/storage")
28+
public class StorageMethodController {
29+
30+
@Autowired
31+
private StorageMethodService storageMethodService;
32+
33+
@Autowired
34+
private CurrentUserService currentUserService;
35+
36+
@GetMapping("/list")
37+
@RequiresPermission("system:config:management")
38+
public ApiResponse<List<StorageMethod>> list() {
39+
return ApiResponse.success(storageMethodService.list());
40+
}
41+
42+
@PostMapping("/add")
43+
@RequiresPermission("system:config:management")
44+
public ApiResponse<Map<String, String>> add(@RequestBody StorageMethod storageMethod) {
45+
String userUuid = currentUserService.getCurrentUserUuid();
46+
Map<String, String> result = storageMethodService.add(storageMethod, userUuid);
47+
if (result.containsKey("error")) {
48+
return ApiResponse.error(400, result.get("error"));
49+
}
50+
return ApiResponse.success(result);
51+
}
52+
53+
@PostMapping("/edit")
54+
@RequiresPermission("system:config:management")
55+
public ApiResponse<Map<String, String>> edit(@RequestBody StorageMethod storageMethod) {
56+
Map<String, String> result = storageMethodService.edit(storageMethod);
57+
if (result.containsKey("error")) {
58+
return ApiResponse.error(400, result.get("error"));
59+
}
60+
return ApiResponse.success(result);
61+
}
62+
63+
@PostMapping("/disable")
64+
@RequiresPermission("system:config:management")
65+
public ApiResponse<Map<String, String>> disable(@RequestBody Map<String, String> payload) {
66+
String uuid = payload.get("uuid");
67+
Map<String, String> result = storageMethodService.disable(uuid);
68+
if (result.containsKey("error")) {
69+
return ApiResponse.error(400, result.get("error"));
70+
}
71+
return ApiResponse.success(result);
72+
}
73+
74+
@PostMapping("/enable")
75+
@RequiresPermission("system:config:management")
76+
public ApiResponse<Map<String, String>> enable(@RequestBody Map<String, String> payload) {
77+
String uuid = payload.get("uuid");
78+
Map<String, String> result = storageMethodService.enable(uuid);
79+
if (result.containsKey("error")) {
80+
return ApiResponse.error(400, result.get("error"));
81+
}
82+
return ApiResponse.success(result);
83+
}
84+
85+
@PostMapping("/delete")
86+
@RequiresPermission("system:config:management")
87+
public ApiResponse<Map<String, String>> delete(@RequestBody Map<String, String> payload) {
88+
String uuid = payload.get("uuid");
89+
Map<String, String> result = storageMethodService.deletePhysical(uuid);
90+
if (result.containsKey("error")) {
91+
return ApiResponse.error(400, result.get("error"));
92+
}
93+
return ApiResponse.success(result);
94+
}
95+
96+
@PostMapping("/set-default")
97+
@RequiresPermission("system:config:management")
98+
public ApiResponse<Map<String, String>> setDefault(@RequestBody Map<String, String> payload) {
99+
String uuid = payload.get("uuid");
100+
Map<String, String> result = storageMethodService.setDefault(uuid);
101+
if (result.containsKey("error")) {
102+
return ApiResponse.error(400, result.get("error"));
103+
}
104+
return ApiResponse.success(result);
105+
}
106+
}

backend/src/main/java/xyz/lingview/dimstack/domain/AttachmentManagement.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class AttachmentManagement {
1717
private String original_filename;
1818
private String attachment_path;
1919
private String access_key;
20+
private String storage_id;
2021
private LocalDateTime create_time;
2122
private LocalDateTime deleted_time;
2223
private int status;

backend/src/main/java/xyz/lingview/dimstack/domain/SiteConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,5 @@ public class SiteConfig {
4444
private Integer comment_status;
4545
private Integer enable_llm_comment_review;
4646
private Integer admin_comment_no_review;
47+
private String default_storage;
4748
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package xyz.lingview.dimstack.domain;
2+
3+
import lombok.Data;
4+
5+
import java.time.LocalDateTime;
6+
7+
/**
8+
* @Author: lingview
9+
* @Date: 2026/07/17 20:00:00
10+
* @Description: 存储方式实体
11+
* @Version: 1.0
12+
*/
13+
@Data
14+
public class StorageMethod {
15+
private String uuid;
16+
private String user_uuid;
17+
private String name;
18+
private String type;
19+
private String config;
20+
private int status;
21+
private LocalDateTime created_at;
22+
private LocalDateTime updated_at;
23+
}

backend/src/main/java/xyz/lingview/dimstack/domain/UploadAttachment.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ public class UploadAttachment {
99
private String original_filename;
1010
private String attachment_path;
1111
private String access_key;
12+
private String storage_id;
1213
private int status;
1314
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package xyz.lingview.dimstack.mapper;
2+
3+
import org.apache.ibatis.annotations.Mapper;
4+
import org.springframework.stereotype.Repository;
5+
import xyz.lingview.dimstack.domain.StorageMethod;
6+
7+
import java.util.List;
8+
9+
@Mapper
10+
@Repository
11+
public interface StorageMethodMapper {
12+
StorageMethod selectByUuid(String uuid);
13+
14+
StorageMethod selectByName(String name);
15+
16+
StorageMethod selectByType(String type);
17+
18+
List<StorageMethod> selectAll();
19+
20+
int insert(StorageMethod storageMethod);
21+
22+
int update(StorageMethod storageMethod);
23+
24+
int disable(String uuid);
25+
26+
int enable(String uuid);
27+
28+
int deletePhysical(String uuid);
29+
}

backend/src/main/java/xyz/lingview/dimstack/service/FileAccessService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ record FileAccessResult(
1919
Resource resource,
2020
String contentType,
2121
String filename,
22-
boolean found
22+
boolean found,
23+
String redirectUrl
2324
) {}
2425
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package xyz.lingview.dimstack.service;
2+
3+
import java.io.InputStream;
4+
5+
/**
6+
* @Author: lingview
7+
* @Date: 2026/07/17 21:23:12
8+
* @Description: 文件存储抽象接口,定义所有存储后端统一操作
9+
* @Version: 1.0
10+
*/
11+
public interface FileStorage {
12+
13+
void store(String objectKey, InputStream data, long contentLength, String contentType);
14+
15+
InputStream retrieve(String objectKey);
16+
17+
void delete(String objectKey);
18+
19+
boolean exists(String objectKey);
20+
21+
void copy(String sourceKey, String destKey);
22+
23+
String getType();
24+
}

0 commit comments

Comments
 (0)