新增:媒体上传客户端——MediaUploader 编排状态机 + 预签名直传 + 进度组件,修正 media 端点误挂 community 服务(T3-13)
CI / flutter-gates (push) Successful in 2m30s
CI / flutter-gates (push) Successful in 2m30s
- MediaUploader:选图→压缩(80→60 降质阶梯,10 MiB 超限拒绝)→createUpload →预签名 PUT 直传(进度回调)→confirm→ready assetId 交付;并发槽位 2、 顺序即 position、单图失败不拖垮整批;凭据过期预检与存储侧 403 各自动 换新凭据一次;失败重试复用压缩产物、从 createUpload 全新开始 - 孤儿防护:未 confirm 的 assetId 只以管线局部变量存在,快照 assetId 与 ready 态断言绑定,buildAttachRequests 非全员 ready 即抛 StateError - 线路修正:media 两步上传端点由 user 服务(:8082 MediaController)提供, T3-12 误挂 community 客户端(:8084 无 media 路由),补 mediaApi 分端口 直连,compose 真链路实测证实 - UploadProgressOverlay 四态进度层(05 号规范 §3.3);直传层本地 HttpServer 三线路测试(200/403/断连);compose 冒烟测试 (PATBOND_MEDIA_SMOKE=1 启用,默认跳过) - 新依赖:image_picker、flutter_image_compress(03 号评估选型) - flutter test 379 全绿(基线 347,+32)、analyze 0、format 无 diff Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -62,10 +62,21 @@ abstract class CommunityRepository {
|
||||
/// `Idempotency-Key`(1~128 字符;每次逻辑提交换新键;token 刷新后的
|
||||
/// 自动重放沿用同一个键——键在本层每次调用生成一次,重放走同一 headers)。
|
||||
/// 点赞/收藏/关注为 PUT/DELETE 语义幂等,无需幂等键。
|
||||
///
|
||||
/// 端口线路:community 域端点走 community 服务客户端;media 两步上传
|
||||
/// 端点由 **user 服务**提供(13 号报告 §2,MediaController 在
|
||||
/// patbond-user),经 [mediaApi] 直连——T3-13 真链路实测修正,
|
||||
/// 未提供时回落主客户端(既有测试桩场景)。
|
||||
class ApiCommunityRepository implements CommunityRepository {
|
||||
ApiCommunityRepository({required this._api, this._uuid = const Uuid()});
|
||||
ApiCommunityRepository({
|
||||
required ApiClient api,
|
||||
ApiClient? mediaApi,
|
||||
this._uuid = const Uuid(),
|
||||
}) : _api = api,
|
||||
_mediaApi = mediaApi ?? api;
|
||||
|
||||
final ApiClient _api;
|
||||
final ApiClient _mediaApi;
|
||||
final Uuid _uuid;
|
||||
|
||||
Future<Object?> _request(
|
||||
@@ -74,9 +85,10 @@ class ApiCommunityRepository implements CommunityRepository {
|
||||
Object? body,
|
||||
Map<String, Object?>? query,
|
||||
bool idempotent = false,
|
||||
bool media = false,
|
||||
}) async {
|
||||
try {
|
||||
return await _api.request(
|
||||
return await (media ? _mediaApi : _api).request(
|
||||
path,
|
||||
method: method,
|
||||
body: body,
|
||||
@@ -101,6 +113,7 @@ class ApiCommunityRepository implements CommunityRepository {
|
||||
'/api/v1/media/uploads',
|
||||
method: 'POST',
|
||||
body: request.toJson(),
|
||||
media: true,
|
||||
);
|
||||
return MediaUploadCredentials.fromJson(_asMap(data));
|
||||
}
|
||||
@@ -111,6 +124,7 @@ class ApiCommunityRepository implements CommunityRepository {
|
||||
final data = await _request(
|
||||
'/api/v1/media/uploads/$assetId/complete',
|
||||
method: 'POST',
|
||||
media: true,
|
||||
);
|
||||
return MediaAsset.fromJson(_asMap(data));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user