All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m33s
主要改动: - 新增交互式环境配置脚本 (scripts/setup-env.sh) - 新增本地启动快捷脚本 (scripts/run-local.sh) - 新增环境变量模板文件 (.env.example) - 部署模式改版:使用嵌入式配置 + 环境变量覆盖 - 添加对象存储功能支持 - 改进 IoT 卡片导入任务 - 优化 OpenAPI 文档生成 - 删除旧的配置文件,改用嵌入式默认配置
6.3 KiB
6.3 KiB
object-storage Specification
Purpose
TBD - created by archiving change add-object-storage. Update Purpose after archive.
Requirements
Requirement: Provider 接口
系统 SHALL 提供统一的对象存储 Provider 接口,支持 S3 兼容的对象存储服务。
接口定义:
type Provider interface {
Upload(ctx context.Context, key string, reader io.Reader, contentType string) error
Download(ctx context.Context, key string, writer io.Writer) error
DownloadToTemp(ctx context.Context, key string) (localPath string, cleanup func(), err error)
Delete(ctx context.Context, key string) error
Exists(ctx context.Context, key string) (bool, error)
GetUploadURL(ctx context.Context, key string, contentType string, expires time.Duration) (string, error)
GetDownloadURL(ctx context.Context, key string, expires time.Duration) (string, error)
}
Scenario: 创建 S3 Provider
- WHEN 系统启动时读取 storage 配置
- THEN 系统 SHALL 创建 S3Provider 实例并验证连接
Scenario: 配置缺失
- WHEN storage 配置未设置或不完整
- THEN 系统 SHALL 记录警告日志并跳过初始化(不影响启动)
Requirement: 文件上传
系统 SHALL 支持通过 Provider 接口上传文件到对象存储。
Scenario: 上传成功
- WHEN 调用
Upload(ctx, "imports/test.csv", reader, "text/csv") - THEN 文件 SHALL 被上传到配置的 Bucket 中指定路径
- THEN 方法 SHALL 返回 nil
Scenario: 上传失败
- WHEN 对象存储服务不可用
- THEN 方法 SHALL 返回包含错误详情的 error
Requirement: 文件下载
系统 SHALL 支持从对象存储下载文件。
Scenario: 下载到 Writer
- WHEN 调用
Download(ctx, "imports/test.csv", writer) - THEN 文件内容 SHALL 被写入到提供的 writer
Scenario: 下载到临时文件
- WHEN 调用
DownloadToTemp(ctx, "imports/test.csv") - THEN 系统 SHALL 下载文件到临时目录
- THEN 方法 SHALL 返回本地文件路径和 cleanup 函数
- THEN 调用 cleanup() 后临时文件 SHALL 被删除
Scenario: 文件不存在
- WHEN 下载的文件在对象存储中不存在
- THEN 方法 SHALL 返回 "文件不存在" 错误
Requirement: 文件删除
系统 SHALL 支持从对象存储删除文件。
Scenario: 删除成功
- WHEN 调用
Delete(ctx, "imports/test.csv") - THEN 文件 SHALL 从对象存储中删除
- THEN 方法 SHALL 返回 nil
Scenario: 删除不存在的文件
- WHEN 删除的文件不存在
- THEN 方法 SHALL 返回 nil(幂等操作)
Requirement: 文件存在性检查
系统 SHALL 支持检查文件是否存在于对象存储。
Scenario: 文件存在
- WHEN 调用
Exists(ctx, "imports/test.csv")且文件存在 - THEN 方法 SHALL 返回 (true, nil)
Scenario: 文件不存在
- WHEN 调用
Exists(ctx, "imports/test.csv")且文件不存在 - THEN 方法 SHALL 返回 (false, nil)
Requirement: 预签名上传 URL
系统 SHALL 支持生成预签名上传 URL,允许前端直接上传文件到对象存储。
Scenario: 生成上传 URL
- WHEN 调用
GetUploadURL(ctx, "imports/test.csv", "text/csv", 15*time.Minute) - THEN 方法 SHALL 返回有效的预签名 URL
- THEN URL SHALL 在指定时间(15分钟)后过期
- THEN 使用该 URL 的 PUT 请求 SHALL 能成功上传文件
Scenario: URL 过期后
- WHEN 使用过期的预签名 URL 上传
- THEN 对象存储 SHALL 返回 403 Forbidden
Requirement: 预签名下载 URL
系统 SHALL 支持生成预签名下载 URL,允许用户直接从对象存储下载文件。
Scenario: 生成下载 URL
- WHEN 调用
GetDownloadURL(ctx, "exports/report.xlsx", 24*time.Hour) - THEN 方法 SHALL 返回有效的预签名 URL
- THEN URL SHALL 在指定时间(24小时)后过期
- THEN 使用该 URL 的 GET 请求 SHALL 能下载文件
Requirement: 获取上传 URL API
系统 SHALL 提供 API 接口供前端获取预签名上传 URL。
接口定义:
POST /api/admin/storage/upload-url
Authorization: Bearer <token>
Content-Type: application/json
Request:
{
"file_name": "cards.csv",
"content_type": "text/csv",
"purpose": "iot_import"
}
Response:
{
"code": 0,
"message": "success",
"data": {
"upload_url": "http://obs-helf.cucloud.cn/cmp/imports/2025/01/24/abc123.csv?X-Amz-...",
"file_key": "imports/2025/01/24/abc123.csv",
"expires_in": 900
}
}
Scenario: 获取上传 URL 成功
- WHEN 已认证用户调用 POST /api/admin/storage/upload-url
- AND 请求包含有效的 file_name、content_type、purpose
- THEN 系统 SHALL 返回预签名上传 URL 和 file_key
- THEN file_key 格式 SHALL 为
{purpose}/{year}/{month}/{day}/{uuid}.{ext}
Scenario: 参数缺失
- WHEN 请求缺少必填参数
- THEN 系统 SHALL 返回 400 错误
Scenario: 未认证
- WHEN 请求未携带有效 Token
- THEN 系统 SHALL 返回 401 错误
Requirement: 文件路径规范
系统 SHALL 按照规范生成文件路径。
路径格式:{purpose}/{year}/{month}/{day}/{uuid}.{ext}
支持的 purpose 值:
iot_import→imports/export→exports/attachment→attachments/
Scenario: 生成导入文件路径
- WHEN purpose 为 "iot_import",file_name 为 "cards.csv"
- THEN 生成的 file_key SHALL 匹配
imports/\d{4}/\d{2}/\d{2}/[a-f0-9-]+\.csv
Scenario: 未知 purpose
- WHEN purpose 值不在支持列表中
- THEN 系统 SHALL 返回错误 "不支持的文件用途"
Requirement: 配置结构
系统 SHALL 支持通过配置文件配置对象存储参数。
storage:
provider: "s3"
s3:
endpoint: "http://obs-helf.cucloud.cn"
region: "cn-langfang-2"
bucket: "cmp"
access_key_id: "${OSS_ACCESS_KEY_ID}"
secret_access_key: "${OSS_SECRET_ACCESS_KEY}"
use_ssl: false
path_style: true
presign:
upload_expires: "15m"
download_expires: "24h"
temp_dir: "/tmp/junhong-storage"
Scenario: 环境变量替换
- WHEN 配置值为
${ENV_VAR}格式 - THEN 系统 SHALL 从环境变量读取实际值
Scenario: 临时目录不存在
- WHEN temp_dir 目录不存在
- THEN 系统 SHALL 自动创建该目录