chore: record session journal - 握力环训练趣味化
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# 前端直传腾讯云 COS(视频大文件)
|
||||
|
||||
## Goal
|
||||
|
||||
让管理后台的视频上传绕开 PHP 服务器的中转通道,由浏览器直接把视频文件分片上传到腾讯云 COS。
|
||||
解决两类痛点:(1) 服务器双倍带宽消耗(client→server→COS);(2) PHP `upload_max_filesize` / `post_max_size` 与 fpm 进程内存对大视频不友好。
|
||||
首发场景:诊单 `CallRecordPanel.vue` 上传通话回放视频。
|
||||
|
||||
## What I already know
|
||||
|
||||
- 现有上传链路:`admin/src/components/upload/index.vue` → `POST /upload/video`(`UploadController::video`)→ `UploadService::video()` → `storage/Driver` → `engine/Qcloud::upload()` 用 `putObject`。
|
||||
- 服务端入参依赖 `$_FILES`,受 PHP 上传上限约束;返回 `{id, cid, type, name, uri, url}`,`uri` 经 `FileService::getFileUrl()` 拼接 OSS/CDN 域名。
|
||||
- 存储驱动配置走 `dev_config.storage`(管理员可在后台「存储配置」切换 local/qiniu/aliyun/qcloud;本任务只对 qcloud 启用直传)。
|
||||
- composer 已有 `qcloud/cos-sdk-v5 ^2.5`,但 STS 临时凭证需要再装 `qcloud/cos-sts-sdk`。
|
||||
- 未提交代码:`CallRecordPanel.vue` 已加「上传视频」按钮,调用 `createManualCallRecord` + `attachLocalCallRecording`,依赖上传组件吐出的 `response.data.uri/url`。AuthMiddleware 已为 `tcm.diagnosis/uploadcallrecording` 等加白名单 — 直传新接口与之解耦,不需要白名单。
|
||||
|
||||
## Assumptions (temporary)
|
||||
|
||||
- 生产环境默认存储引擎已经是 `qcloud`;切到 `local/aliyun/qiniu` 时 direct 模式需要降级回老链路。
|
||||
- COS Bucket 已开启跨域 CORS(直传必须)。如未开启,需要运维在 COS 控制台配置 `AllowedOrigin = 后台域名 + 本地 dev`、`AllowedMethod = PUT/POST/HEAD`、`AllowedHeader = *`、`ExposeHeader: ETag`。
|
||||
- 后台域名固定(暂无多租户独立域名)。
|
||||
- 视频文件大小有合理上限(建议 2GB)。
|
||||
|
||||
## Decisions (locked)
|
||||
|
||||
- 视频上限 **2 GB**(写进 STS policy `numeric_less_than_equal cos:content-length` + 前端 `file.size` 预检)。
|
||||
- 保留 `/upload/video` 老接口;`upload/index.vue` 的 `direct` prop 默认 `false`,仅 `CallRecordPanel.vue` 这类显式声明的视频场景启用直传。
|
||||
- STS 凭证有效期 **30 分钟**(1800 秒)。
|
||||
- 直传完成后**必须等 `oss-confirm`** 返回(写完 `file` 表 + HEAD 校验通过)才 emit `success`,保证业务侧拿到的 `uri/url` 一定有 `file_id`。
|
||||
|
||||
## Requirements (evolving)
|
||||
|
||||
- [R1] 后端新增 `POST /adminapi/upload/oss-credentials`,仅当 `storage.default == qcloud` 时返回 `{provider, bucket, region, credentials, expiredTime, key, host, cdn_url_base}`;其它 driver 返回 `{provider: '<engine>', fallback: true}` 让前端自动降级到老链路。
|
||||
- [R2] 后端新增 `POST /adminapi/upload/oss-confirm`,HEAD 一下 `key` 校验确实存在 + size 在阈值内,写入 `file` 表(沿用 `FileService::getFileUrl`),返回与 `UploadService::video()` 完全对齐的 `{id, cid, type, name, uri, url}`。
|
||||
- [R3] 新装 composer 依赖 `qcloud/cos-sts-sdk`;在 `engine/Qcloud.php` 增加 `getStsCredentials($keyPrefix, $maxSize)` 方法,复用现有 `access_key/secret_key/bucket/region`。
|
||||
- [R4] STS policy 限定 action:`name/cos:PostObject`、`name/cos:PutObject`、`name/cos:InitiateMultipartUpload`、`name/cos:UploadPart`、`name/cos:CompleteMultipartUpload`、`name/cos:AbortMultipartUpload`、`name/cos:ListParts`;resource 限定 `qcs::cos:${region}:uid/${appId}:${bucket}/uploads/video/{YYYYMMDD}/*`。
|
||||
- [R5] 前端新增 npm 依赖 `cos-js-sdk-v5`;`upload/index.vue` 增加 `direct: boolean` prop(默认 `false`),`type==='video' && direct===true` 时走 STS + `cos.uploadFile`(含 `SliceSize=5MB`、`onProgress`),仍发出原 `success` 事件 `{ code, data: { uri, url, ... } }` 保持调用侧兼容。
|
||||
- [R6] `CallRecordPanel.vue` 切到 `<upload type="video" direct :show-progress="true">`,并支持上传过程中显示百分比(沿用组件内置 `el-dialog` 进度)。
|
||||
- [R7] 失败/降级路径:STS 接口失败 / response 标记 `fallback=true` / `cos-js-sdk-v5` 上传报错时,自动降级到老的 `POST /upload/video`,提示「直传失败,已切换到普通上传」。
|
||||
- [R8] 安全:保留登录鉴权;STS 凭证 30 分钟有效期;前端拒绝不在 `accept` 列表的扩展名(沿用现有 `.wmv,.avi,...,.mp4,.mkv`)。
|
||||
|
||||
## Acceptance Criteria (evolving)
|
||||
|
||||
- [ ] 在 storage=qcloud 环境,从 `CallRecordPanel.vue` 上传 1.5GB mp4 文件,浏览器 Network 面板能看到流量直接打到 `*.cos.*.myqcloud.com`,`/upload/video` 没有调用。
|
||||
- [ ] 上传过程中能看到分片进度条(5MB 一片,并发 ≥3)。
|
||||
- [ ] 上传完成后,`call_record` 表正确写入 `recording_urls`,前端表格 `recording_urls_list` 渲染出可播放视频。
|
||||
- [ ] `file` 表新增一行,`uri` 形如 `uploads/video/20260508/xxxxx.mp4`,`url` 拼接了 CDN/COS 域名。
|
||||
- [ ] 把 `dev_config.storage.default` 改成 `local`,再次上传,自动降级到老 `/upload/video` 路径,整体功能正常。
|
||||
- [ ] 凭证接口未登录调用返回 401。
|
||||
- [ ] 上传一个被 STS policy resource 拒绝的 key(人为篡改 prefix),COS 返回 403,前端给出明确错误。
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- 单元/集成测试:暂无 PHPUnit 工程,跳过自动化测试;保证手测覆盖 AC 全部场景。
|
||||
- 前端 `npm run type-check` 通过、`npm run lint` 无新增告警。
|
||||
- 后端 `php -l` 通过,新接口在 admin 路由可访问。
|
||||
- 文档:在 `.trellis/spec/`(如已有上传相关 spec)补充直传规范;PRD 同步关闭 Open Questions。
|
||||
- COS CORS / STS 子账号最小权限策略整理为 ops 备忘(写到 prd.md 的 Technical Notes)。
|
||||
|
||||
## Out of Scope (explicit)
|
||||
|
||||
- 不做断点续传(刷新页面不保留 UploadId)。
|
||||
- 不做 PostObject 回调链路。
|
||||
- 不做 aliyun / qiniu 直传。
|
||||
- 不改 `upload/image`、`upload/file` 链路(图片小文件用老链路即可)。
|
||||
- 不做客户端 H5/uniapp 端直传(uniapp 本来就不走 admin)。
|
||||
- 不做并发上传限流 / 全局上传队列。
|
||||
|
||||
## Technical Notes
|
||||
|
||||
- 关键文件:`server/app/common/service/storage/engine/Qcloud.php`、`server/app/common/service/UploadService.php`、`server/app/adminapi/controller/UploadController.php`、`admin/src/components/upload/index.vue`、`admin/src/views/tcm/diagnosis/components/CallRecordPanel.vue`。
|
||||
- composer 新增:`qcloud/cos-sts-sdk: ^3.0`。
|
||||
- npm 新增:`cos-js-sdk-v5`。
|
||||
- COS CORS 配置(运维):`AllowedOrigin: <admin 域名>`、`AllowedMethod: PUT,POST,HEAD,GET`、`AllowedHeader: *`、`ExposeHeader: ETag,x-cos-request-id,x-cos-version-id`。
|
||||
- STS 子账号最小权限:仅赋予对 `${bucket}/uploads/video/*` 的 `cos:Get*`、`cos:Put*`、`cos:Post*`、Multipart 系列动作;STS policy 在此基础上再用 condition + resource 收敛。
|
||||
- Key 命名:`uploads/video/${YYYYMMDD}/${ulid}.${ext}`,与现有 `UploadService::getUploadUrl()` 的 `Ymd` 分日目录保持一致。
|
||||
Reference in New Issue
Block a user