---
url: /docs/api-docs/v1/admin/storage.md
---
# storage — 存储管理（STS 直传链路）（7 action）

> 前缀：`/api/v1/admin/storage/`
> 权限：读 = `STORAGE_READ`（**super + operator + it**）；删 = `STORAGE_DELETE`（**super + operator**，it 删除一律 403）
> 代码：`src/app/admin/interface/api/storage_mgmt.py` + `application/usecase/storage_mgmt.py`
> 规划/设计：`.claude/plan/admin-storage-management{,-design}.md`
> ⚠️ App 端四端点（`/api/v1/storage/{sts,complete,download-url,delete}`）契约零变化 — 本域为 admin 跨用户视角

## 1. records —— 上传记录列表（时间流）

`GET /api/v1/admin/storage/records` · 权限 `STORAGE_READ`

**Query 参数**：

| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| `page` | int | ⬜ | 1 | ≥1 |
| `page_size` | int | ⬜ | 20 | 1~100 |
| `user_id` | string | ⬜ | — | 用户 ID（**精确匹配**） |
| `type` | string | ⬜ | — | 数据包类型（如 `xiaomi_zip` / `coros_fit`） |
| `status` | string | ⬜ | — | `pending` / `parsing` / `parsed` / `failed` |
| `date_from` / `date_to` | string | ⬜ | — | ISO 日期（含当日 00:00:00 / 23:59:59） |

**响应 data**：`{ total, items: [UploadRecord] }`（`created_at` 倒序；筛选 AND 语义）
UploadRecord 字段：`id`（`up-N`）/ `user_id` / `type` / `cos_url` / `object_key` / `mime` / `size` / `status` / `password` / `parsed_result` / `error_msg` / `created_at`

## 2. records/{id} —— 记录详情

`GET /api/v1/admin/storage/records/{record_id}` · 权限 `STORAGE_READ`

**响应 data**：`{ record: UploadRecord }`（全字段 — 抽屉渲染用）

**错误码**：`40201004` 记录不存在（**HTTP 404** — 与权限码同值别名，靠 HTTP 状态区分）。

## 3. records/{id} —— 删除（对象 + 记录双删）

`DELETE /api/v1/admin/storage/records/{record_id}` · 权限 `STORAGE_DELETE`

语义：查记录 → COS 对象删除（幂等）→ 记录删除 → `admin_audit` 留痕（`action=STORAGE_RECORD_DELETE`）。
⚠️ 与 App 端属主删除不同：admin 跨用户，无属主校验（RBAC 已收口）。

**响应 data**：`{ ok, object_deleted, record_deleted, object_key }`

**错误码**：`40201004` 记录不存在（**HTTP 404**；已删过再删同样 404，前端提示「记录不存在」）。

## 4. stats —— 统计看板

`GET /api/v1/admin/storage/stats` · 权限 `STORAGE_READ`

**响应 data**：

```json
{
  "total_count": 12480,
  "total_size": 92644487168,
  "type_dist": {"xiaomi_zip": 7740, "coros_fit": 3870},
  "status_dist": {"parsed": 12410, "pending": 52, "parsing": 18, "failed": 37},
  "trend_30d": [{"date": "2026-09-11", "count": 12}, "…共 30 项逐日补零升序"]
}
```

实时 SQL 聚合（`generate_series` 逐日补零）——前端免补点。

## 5. config-health —— 配置健康 + 自检

`GET /api/v1/admin/storage/config-health[?self_check=true]` · 权限 `STORAGE_READ`

| 档位 | 行为 |
|---|---|
| 默认（轻档） | env 五键脱敏现状 + 规则常量 + `checks: []` / `all_ok: null` |
| `self_check=true`（重档） | 真发 STS AssumeRole 冒烟（探针身份 `__health_probe__`）+ 桶列举探测，逐项 `{name, ok, detail, elapsed_ms}` |

**响应 data**：`{ env, env_missing, rules, checks, self_check, all_ok }`
⚠️ `env.STS_SECRET_KEY` 恒 `"******"`（密钥永不回显）；`rules` 为代码常量只读快照（max\_size/allowed\_mime/allowed\_exts/rate\_limit/sts\_duration/download\_ttl）。

## 6. orphan-scan —— 孤儿对账（桶 ↔ 记录双向）

`POST /api/v1/admin/storage/orphan-scan` · 权限 `STORAGE_READ`

同步扫描（最长约 30s — 前端需 60s timeout）：COS `uploads/` 前缀列举（上限 **1000** 对象）↔ upload\_record 全量 object\_key。

⚠️ **竞态防护**：桶侧**排除最近修改 1 小时内**的对象（「已 PUT 未 complete」的上传中文件会被误判为孤儿，直接删会打断真实用户上传）= `recent_excluded` 计数。
⚠️ `truncated=true`（达 1000 上限）时**不返回 record\_orphans**（桶 key 集不完整，记录侧差集不可信）。

**响应 data**：

```json
{
  "bucket_orphans": [{"object_key": "uploads/…", "size": 812, "last_modified": "…"}],
  "record_orphans": [{"record_id": "up-1239", "object_key": "uploads/…", "type": "…", "created_at": "…"}],
  "scanned_objects": 342,
  "truncated": false,
  "recent_excluded": 2,
  "elapsed_ms": 8210
}
```

## 7. orphan-delete —— 批量删桶孤儿

`POST /api/v1/admin/storage/orphan-delete` · 权限 `STORAGE_DELETE`

**Body**：`{ "object_keys": ["uploads/…", "…"] }`（≤ **50**/次）
语义：逐个 `cz_delete_object`（幂等）；单项失败不中断，逐项回报；聚合 `admin_audit` 留痕（`action=STORAGE_ORPHAN_DELETE`）。

**响应 data**：`{ requested, deleted: [{object_key, ok}], failed: [{object_key, error}] }`

**错误码**：`40201001` object\_keys 非数组/为空/超 50 上限（HTTP 400）。

## 错误码一览

| 码 | 场景 | HTTP |
|---|---|---|
| `40201002` | 未登录 / token 无效 | 401 |
| `40201004` | RBAC 权限不足（无 storage:read / storage:delete） | 403 |
| `40201001` | 参数非法（object\_keys 形态/上限） | 400 |
| `40201004` | 记录不存在（详情/删除 miss — 与权限码同值别名） | 404 |

## 审计动作登记

| action | 触发 | target |
|---|---|---|
| `STORAGE_RECORD_DELETE` | 端点 3 | `upload_record` / record\_id（before 含 object\_key+user\_id） |
| `STORAGE_ORPHAN_DELETE` | 端点 7 | `cos_object` / `batch:Nok/Mfail`（before 含 object\_keys 全量） |
