feat: 实现 Auth 全局状态机与 401 统一处理机制

- 新增 AuthSessionInvalidated 事件处理 token 失效场景
- ApiInterceptor 新增 authFailureCallback 单飞机制
- AuthBloc 区分 manual logout 与 auto expiry 语义
- 新增 startup recovery fallback 防止启动卡死

feat: 重构 Calendar DayWeek 视图事件布局引擎

- 新增 DayEventLayoutEngine 解耦事件计算与渲染
- 新增 DayTimelineMetrics 统一时间轴常量
- 新增 DayViewScale 支持捏合缩放

feat: 新增 Settings 页面共享 UI 组件

- 新增 BackTitlePageHeader 统一页面 header
- 新增 DetailHeaderActionMenu 统一操作菜单
- 新增 DestructiveActionSheet 统一删除确认
- 新增 AppToggleSwitch 统一开关组件

feat: Chat UI Schema 支持导航操作

- 支持 navigation 类型 action 触发内部路由跳转
- 新增路径验证与参数处理

chore: 更新相关测试覆盖 auth 失效路径
This commit is contained in:
qzl
2026-03-18 13:35:25 +08:00
parent 19981964fb
commit b34697660d
56 changed files with 2602 additions and 784 deletions
@@ -125,7 +125,7 @@ def upgrade() -> None:
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
SET search_path = ''
AS $$
BEGIN
INSERT INTO public.profiles (id, username, avatar_url, bio, settings, created_at, updated_at)
@@ -52,8 +52,8 @@ def upgrade() -> None:
op.execute(
"""
CREATE OR REPLACE FUNCTION public.create_profile_for_new_user()
RETURNS trigger
CREATE OR REPLACE FUNCTION public.generate_invite_code()
RETURNS TEXT
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
@@ -78,7 +78,7 @@ def upgrade() -> None:
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
SET search_path = ''
AS $$
DECLARE
invite_code_value TEXT;
@@ -69,10 +69,15 @@ def upgrade() -> None:
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
SET search_path = ''
AS $$
DECLARE
invite_code_value TEXT;
referrer_id UUID;
new_code TEXT;
attempts INT := 0;
BEGIN
INSERT INTO public.profiles (id, username, avatar_url, bio, settings, created_at, updated_at)
INSERT INTO public.profiles (id, username, avatar_url, bio, settings, referred_by, created_at, updated_at)
VALUES (
NEW.id,
COALESCE(
@@ -82,11 +87,54 @@ def upgrade() -> None:
),
NULL,
NULL,
'{"agent_prompts": {}}'::jsonb,
'{}'::jsonb,
NULL,
now(),
now()
)
ON CONFLICT (id) DO NOTHING;
LOOP
BEGIN
new_code := public.generate_invite_code();
INSERT INTO public.invite_codes (code, owner_id, status, used_count, max_uses, expires_at, reward_config)
VALUES (
new_code,
NEW.id,
'active',
0,
NULL,
NULL,
'{}'::jsonb
);
EXIT;
EXCEPTION WHEN unique_violation THEN
attempts := attempts + 1;
IF attempts >= 100 THEN
RAISE EXCEPTION 'Failed to generate unique invite code after 100 attempts';
END IF;
END;
END LOOP;
invite_code_value := NEW.raw_user_meta_data ->> 'invite_code';
IF invite_code_value IS NOT NULL AND length(invite_code_value) = 4 THEN
invite_code_value := upper(invite_code_value);
IF invite_code_value ~ '^[ABCDEFGHJKMNPQRSTUVWXYZ23456789]{4}$' THEN
UPDATE public.invite_codes
SET used_count = used_count + 1
WHERE code = invite_code_value
AND status = 'active'
AND (max_uses IS NULL OR used_count < max_uses)
AND (expires_at IS NULL OR expires_at > NOW())
RETURNING owner_id INTO referrer_id;
IF referrer_id IS NOT NULL THEN
UPDATE public.profiles
SET referred_by = referrer_id
WHERE id = NEW.id;
END IF;
END IF;
END IF;
RETURN NEW;
END;
@@ -121,7 +169,7 @@ def downgrade() -> None:
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
SET search_path = ''
AS $$
BEGIN
INSERT INTO public.profiles (id, username, avatar_url, bio, settings, created_at, updated_at)
@@ -71,7 +71,7 @@ def upgrade() -> None:
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
SET search_path = ''
AS $$
BEGIN
INSERT INTO public.profiles (id, username, avatar_url, bio, settings, created_at, updated_at)
@@ -114,7 +114,7 @@ def downgrade() -> None:
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
SET search_path = ''
AS $$
BEGIN
INSERT INTO public.profiles (id, username, avatar_url, bio, settings, created_at, updated_at)