feat(software-version): 安装包改浏览器直传 MinIO,支持批量删除

- 后端新增 /api/admin/version/presign、confirm、delete 端点;直传后服务端校验对象并写 web_config
- OSS 层新增软件版本对象 presign PUT/大小校验/删除与 client 桶 URL 识别
- 后台前端上传改 presign→PUT→confirm 三段式直传,带进度条;成功提示精简为"发布成功"
- 版本列表加勾选/表头全选、批量删除与单行删除;操作按钮样式对齐其他子菜单
This commit is contained in:
2026-09-08 23:51:18 +08:00
parent 5fad0ae027
commit 45fdd8a294
9 changed files with 440 additions and 30 deletions
@@ -1,6 +1,8 @@
package com.nanri.aiimage.modules.file.service.oss;
import com.nanri.aiimage.config.OssProperties;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -8,6 +10,11 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class OssStorageServiceTest {
@@ -124,6 +131,34 @@ class OssStorageServiceTest {
storageService.generateFreshDownloadUrl("result/similar_asin/1/re.xlsx"));
}
@Test
void presignSoftwareVersionUploadDelegatesToClientPut() throws Exception {
// 自定义 endpoint 下 getPresignedObjectUrl 会先联网探测 region,单测用 mock 客户端隔离
MinioClient client = mock(MinioClient.class);
when(client.getPresignedObjectUrl(any(GetPresignedObjectUrlArgs.class)))
.thenReturn("https://oss.aishufu.top/client/nanri-image/versions/3.0.67.zip?X-Amz-Signature=test-sig");
OssStorageService oss = new OssStorageService(properties, client);
assertEquals("https://oss.aishufu.top/client/nanri-image/versions/3.0.67.zip?X-Amz-Signature=test-sig",
oss.presignSoftwareVersionUpload("nanri-image/versions/3.0.67.zip", 180));
verify(client).getPresignedObjectUrl(any(GetPresignedObjectUrlArgs.class));
}
@Test
void presignSoftwareVersionUploadRejectsForeignKey() {
assertThrows(IllegalArgumentException.class,
() -> storageService.presignSoftwareVersionUpload("other/prefix.zip", 180));
}
@Test
void clientBucketSoftwareVersionUrlResolution() {
assertEquals("https://oss.aishufu.top/client/nanri-image/versions/3.0.66.zip",
storageService.getSoftwareVersionDownloadUrl("nanri-image/versions/3.0.66.zip"));
// client 桶已并入 configuredBuckets,其 URL 可被 normalize 正确识别保留而不误判私有桶
assertEquals("https://oss.aishufu.top/client/nanri-image/versions/3.0.66.zip",
storageService.normalizeManagedPublicUrl(
"http://47.110.241.161:9000/client/nanri-image/versions/3.0.66.zip"));
}
private OssProperties properties() {
return properties;
}