a4a97c03c2
资料页头部三项(展示名 / 头像 / 数字)自此全部来自服务端,176 行硬编码 demo(「萌宠新手(豆豆家长)」/ 24 / 1.8k / 2)退役。 - `PatchField<T>` 承载契约 v1.4.0 的 PATCH 三态(absent 不落键 / clear 落 显式 null / value 落值)。三态必须由类型承载而非 `T?` 加约定:把「不改」 也编码成 null,用户只改昵称就会连头像一起被服务端清掉。 - `UserProfile` 补 nickname / avatarUrl,展示回退 `nickname ?? username` **做在客户端展示层**——服务端 /me 刻意返回 DB 原值,编辑页因此只用原值 预填,避免把展示约定固化成真实昵称。 - `ProfileController`:`/me` 主链路四态 + 两块统计独立三态(统计失败只降级 这一块,不为两个数字丢掉整页);登出 reset 防跨账号泄漏。 - 编辑页维护三态意图而非「当前值整体提交」:昵称 / 头像各有显式清除入口, 未改字段的键根本不进 JSON;空 patch 直接短路(服务端对空 patch 答 400)。 - 昵称校验按**码点**计 1~32 且先 btrim,与 `ck_users_nickname` 对齐; 纯空白是校验失败而非隐式清空。 - `MediaUploader` 的 purpose 参数化 + 复用其六态编排的 `AvatarUploadSheet` (单图、预览确认后才交付 ready assetId,孤儿防护不变)。 - 修复 `/api/v1/me` 端口线路:该端点由 user 服务(:8082)提供,`ApiAuthRepository` 此前只挂 auth(:8081)会得到 404。此前无人消费 me(),故这条错线一直没被 触发;本单是第一个真实消费者,桌面实测即暴露。 测试 526 → 588(+62)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
48 lines
2.5 KiB
Dart
48 lines
2.5 KiB
Dart
import 'package:patbond_flutter/core/network/api_exception.dart';
|
||
|
||
/// 资料页/编辑页的错误文案映射(沿 `petLoadErrorMessage` 先例:
|
||
/// 服务端原始 message 一律不上屏)。
|
||
String profileLoadErrorMessage(ApiException? error) => switch (error) {
|
||
ApiNetworkException _ => '网络异常,请检查网络后重试',
|
||
ApiRateLimitException _ => '请求过于频繁,请稍后再试',
|
||
_ => '加载失败,请稍后重试',
|
||
};
|
||
|
||
/// 保存资料的失败文案,按契约 v1.4.0 的 `PATCH /api/v1/me` 错误谱分层:
|
||
///
|
||
/// | 码 | 成因 | 文案取向 |
|
||
/// | --- | --- | --- |
|
||
/// | 40000 | 昵称长度/纯空白、空 patch、非法 UUID | 指向输入本身可改 |
|
||
/// | 40405 | 头像 asset 不存在/非本人/已删/用途不符 | 引导重新上传 |
|
||
/// | 42203 | 本人头像 asset 仍在 uploading/failed | 明说「还没传完」,可重试 |
|
||
///
|
||
/// 40000 的三种成因在客户端已各自前置拦截(长度校验 / 空 patch 短路 /
|
||
/// assetId 来自服务端),真收到 40000 说明校验与服务端有偏差,
|
||
/// 故文案不写死「昵称」,只指向「填写内容」。
|
||
String profileSaveErrorMessage(ApiException? error) => switch (error) {
|
||
ApiBusinessException(code: ApiCodes.paramError) => '填写内容不符合要求,请检查后重试',
|
||
ApiBusinessException(code: ApiCodes.mediaNotFound) => '头像已失效,请重新上传',
|
||
ApiBusinessException(code: ApiCodes.mediaNotReady) => '头像还没上传完,请稍后重试',
|
||
ApiNetworkException _ => '网络异常,请检查网络后重试',
|
||
ApiRateLimitException _ => '请求过于频繁,请稍后再试',
|
||
_ => '保存失败,请稍后重试',
|
||
};
|
||
|
||
/// 昵称的客户端校验,与后端 `ck_users_nickname`(btrim + 1~32)对齐。
|
||
///
|
||
/// **长度按码点计**(`String.characters` 的等价物 `runes`):数据库
|
||
/// `char_length` 数的是码点,若按 UTF-16 `String.length` 校验,32 个 emoji
|
||
/// 的合法昵称(UTF-16 长度 64)会被客户端误拒。
|
||
///
|
||
/// 返回 null 即通过;非空即字段级 errorText。
|
||
String? validateNickname(String raw) {
|
||
// btrim 对齐:服务端先 btrim 再判长度,故校验也先 trim。
|
||
final trimmed = raw.trim();
|
||
if (trimmed.isEmpty) {
|
||
// 纯空白是 400/40000 而非隐式清空——清空走「清除昵称」显式入口。
|
||
return '昵称不能为空,如需清除请用下方「清除昵称」';
|
||
}
|
||
if (trimmed.runes.length > 32) return '昵称最多 32 个字';
|
||
return null;
|
||
}
|