feat(client): C 端 1-2 账号链路(邮箱登录/注册 + 令牌与 401 + 匿名/登录分叉)

为什么:客户端此前完全没有账号概念(mine 页的"登录"只是本地 demo 开关),
而受限动作(收藏/评论)要对到具体账号。本批把账号链路接上,为后续互动、进度同步、下载打底。

- AuthApi + AuthController + AuthSessionStore:注册/登录/登出/刷新,令牌与设备号存 shared_preferences
  (用 SharedPreferencesAsync,与 engagement 仓库同一套 API)
- ApiClient:统一带 Authorization 与 X-Device-Id;401 自动刷新一次并重试,刷新失败清登录态走 onUnauthorized
- 登录/注册页(pages/auth,沿用 binding/logic/view 与现有视觉):邮箱格式、密码 8-64 位含字母数字本地先校验,
  错误提示走 l10n;登录/注册一键切换
- 匿名/登录分叉:首页、发现、详情保持匿名可看;收藏/点赞等受限动作走 ensureSignedIn() 弹底部提示引导登录
- mine 页展示真实账号(昵称 + uid),拿不到才回落到演示文案
- 测试:新增 auth 控制器(注册落盘/401 刷新重试/刷新失败清会话/登出)与登录页(校验/切换)用例;
  widget_test 改为预置本地会话(账号不再是本地开关);修掉 playback 用例对 host 大小写的环境依赖断言

门禁:flutter analyze 0 问题;flutter test 148 全过。
This commit is contained in:
Vicky committed 2026-10-15 17:53:35 +08:00
1 parent 77d5748dbb
commit e1b98df509
24 files changed
+2871 -1016

No files matched your search

+150
View File
@@ -289,6 +289,12 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get catalogError => '剧目列表加载失败。';
@override
String get detailError => '剧集信息加载失败。';
@override
String get noMediaNotice => '该剧集还没有可播放的片源。';
@override
String get noStories => '暂无剧目。';
@@ -393,4 +399,148 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get shareFooter => 'Nontonki 演示';
@override
String get shortsTab => '短剧';
@override
String get myListTab => '片单';
@override
String get watchFull => '看全集';
@override
String get swipeForNext => '上滑看下一部';
@override
String get clearScreen => '清屏';
@override
String get exitClearScreen => '退出清屏';
@override
String get logIn => '登录';
@override
String get logInHint => '登录后保存金币和片单';
@override
String get rewards => '奖励';
@override
String get forYou => '为你推荐';
@override
String get hot => '热门';
@override
String get newest => '最新';
@override
String get favorite => '收藏';
@override
String get myAccount => '我的账户';
@override
String get coins => '金币';
@override
String get topUpWallet => '充值';
@override
String get vipMembership => '会员';
@override
String get notActive => '未开通';
@override
String get joinVip => '开通会员';
@override
String get earnRewards => '赚奖励';
@override
String get freeBonus => '领免费奖励';
@override
String get watchHistory => '观看历史';
@override
String get settings => '设置';
@override
String get signOut => '退出登录';
@override
String get mockUserName => 'Vicky sheng';
@override
String get mockUserId => 'ID: M1CZWVYE';
@override
String get mockNotice => '当前是示例数据。';
@override
String get topBadge => 'TOP 1';
@override
String get signIn => '登录';
@override
String get signUp => '注册';
@override
String get authSignInTitle => '欢迎回来';
@override
String get authSignUpTitle => '创建账号';
@override
String get authSubtitle => '用邮箱和密码登录。';
@override
String get authEmailLabel => '邮箱';
@override
String get authPasswordLabel => '密码';
@override
String get authPasswordHint => '至少 8 位,需包含字母和数字';
@override
String get authEmailRequired => '请输入邮箱';
@override
String get authEmailInvalid => '邮箱格式不正确';
@override
String get authPasswordRequired => '请输入密码';
@override
String get authPasswordLength => '密码长度需在 8-64 位之间';
@override
String get authPasswordWeak => '密码需同时包含字母和数字';
@override
String get authNetworkError => '网络异常,请稍后重试';
@override
String get authSwitchToSignUp => '还没有账号?去注册';
@override
String get authSwitchToSignIn => '已有账号?去登录';
@override
String get authGateTitle => '登录后可继续';
@override
String get authGateMessage => '收藏、评论等操作需要登录。';
@override
String get authGateAction => '去登录';
@override
String get cancel => '暂不';
}