Files
ichni_Official/.omo/plans/online-api-integration.md
Developer ebd5dafa2d update
2026-06-18 10:06:49 +08:00

36 KiB
Raw Blame History

Plan: IchniOnline API Integration

TL;DR

Quick Summary: Create an HTTP API client layer using BestHTTP and implement full auth flows (TapTap third-party login, password login, registration) to connect the Unity game client to the IchniOnline backend server.

Deliverables:

  • BestHTTP-based API client with JSON serialization, JWT injection, error handling
  • Request/Response DTOs matching server contracts
  • Auth orchestration service (TapTap login, password login, register, logout)
  • Extended LoginCacheData with JWT + server user data
  • Updated LoginPage/StartUIPage to use new auth service

Estimated Effort: Medium Parallel Execution: YES — 4 waves Critical Path: asmdef → ApiClient → AuthService → UI Integration


Context

Original Request

对接 IchniOnline 后端的 API 接口,使用 BestHTTP 进行 HTTP 通信,编写详细计划。所有代码写在 Scripts/Online 目录内IchniOnline.asmdef

Interview Summary

Key Discussions:

  • 服务器开发地址: localhost:5433,可配置 Base URL
  • ThirdParty.Unbound: 暂时跳过,当做登录失败处理
  • JWT 存储: 扩展 LoginCacheData加入 JWT + 用户数据,取代纯 TapTap 缓存
  • 计划范围: TapTap 登录 + 密码登录/注册 + 基础 API 框架

Research Findings:

  • BestHTTP 已安装为 embedded package (com.tivadar.best.http)GUID: 9069ac25d95ca17448a247f3bb1c769f,支持 async/await via Task<T>
  • TapTap SDK 登录返回 TapTapAccount.accessTokenkid/tokenType/macKey/macAlgorithm可直接映射到 ThirdPartyLoginRequest
  • 服务端密码加密: XOR with session keyUserService.DecryptPassword
  • 服务端响应格式: GlobalResponse<T> { Code, Message, Data }Code 10000=Ok
  • JWT 验证: 服务端使用 JWT Bearer后续 API 请求需在 Header 注入 Authorization: Bearer {token}

Metis Review

(Skipped per user request — direct exploration used instead)


Work Objectives

Core Objective

为 Unity 客户端创建 IchniOnline 后端 API 对接层实现完整的认证流程TapTap 第三方登录、密码登录、注册),让客户端和服务端互通。

Concrete Deliverables

  • Scripts/Online/IchniOnline.asmdef — 添加 BestHTTP 引用
  • Scripts/Online/Network/ApiClient.cs — BestHTTP 封装单例
  • Scripts/Online/Network/Models/ApiResponse.cs — 响应模型
  • Scripts/Online/Network/Models/AuthDtos.cs — 请求/响应 DTO
  • Scripts/Online/Logic/AuthService.cs — 认证编排服务
  • Scripts/Online/Logic/ThirdPartyServiceManager.cs — 修改:集成 AuthService
  • Scripts/Online/Logic/LoginCacheManager.cs — 修改:扩展 JWT 支持
  • Scripts/Online/Models/LoginCacheData.cs — 修改:添加 JWT 字段

Definition of Done

  • 编译零错误,无警告
  • TapTap 登录后 JWT Token 成功写入本地缓存
  • 密码登录流程完整session key → XOR 加密 → login → JWT
  • 注册流程完整
  • LoginPage 使用新 AuthService 驱动流程
  • StartUIPage 校验会话有效性

Must Have

  • ApiClient 支持 Base URL 配置(运行时可修改)
  • ApiClient 自动注入 JWT Bearer 到 Authorization Header
  • 所有 DTO 映射与服务器 GlobalResponse<T> 完全匹配
  • 密码 XOR 加密算法与服务器端一致
  • AuthService 对外暴露事件OnLoginSuccess/OnLoginFailed/etc.
  • LoginCacheData 向后兼容(旧缓存不报错)

Must NOT Have (Guardrails)

  • 不要引入新的第三方 HTTP 库(只用 BestHTTP
  • 不要修改 LoginCacheEditor.cs(但可扩展)
  • 不要修改 UI/Base/UIPageBase.cs
  • 不要引入 Newtonsoft.Json用 BestHTTP 内置的 LitJson 或 UnityEngine.JsonUtility
  • 不要阻塞主线程(所有 HTTP 请求必须异步)

Verification Strategy

Test Decision

  • Infrastructure exists: YES (Unity Test Framework)
  • Automated tests: None for network layer (needs running server)
  • Agent-Executed QA: ALWAYS — each task verified via Playwright/browser or manual Unity Editor play mode evidence

QA Policy

Every task MUST include agent-executed QA scenarios.

  • C# compilation through Unity Editor domain reload (verify zero compilation errors)
  • Evidence: Unity Editor console logs, screenshots of play mode, runtime log output

Execution Strategy

Parallel Execution Waves

Wave 1 (Foundation — all parallel):
├── Task 1: IchniOnline.asmdef — 添加 BestHTTP 引用 [quick]
├── Task 2: ApiResponse.cs — GlobalResponse<T> 响应模型 [quick]
├── Task 3: AuthDtos.cs — 请求/响应 DTO [quick]
└── Task 4: LoginCacheData.cs — 扩展 JWT 字段 [quick]

Wave 2 (Core Client — depends on Wave 1):
├── Task 5: ApiClient.cs — BestHTTP 封装单例 [unspecified-high]
└── Task 6: LoginCacheManager.cs — 扩展 JWT 存取 [quick]
       Note: Task 5+6 can run in parallel

Wave 3 (Auth Service — depends on Task 5+6):
├── Task 7: AuthService.cs — 认证编排服务 [unspecified-high]
       Note: Depends on ApiClient + LoginCacheManager

Wave 4 (UI Integration — depends on Task 7):
├── Task 8: ThirdPartyServiceManager.cs — 集成 AuthService [quick]
├── Task 9: LoginPage.cs — 使用新 AuthService [visual-engineering]
└── Task 10: StartUIPage.cs — 校验会话有效性 [visual-engineering]

Wave FINAL:
├── F1: Plan Compliance Audit (oracle)
├── F2: Code Quality Review (unspecified-high)
├── F3: Real Manual QA (unspecified-high)
└── F4: Scope Fidelity Check (deep)

Dependency Matrix

  • Task 1-4: - → 5, 6 → Wave 2
  • Task 5, 6: 1-4 → 7 → Wave 3
  • Task 7: 5, 6 → 8-10 → Wave 4
  • Task 8-10: 7 → F1-F4 → Final

TODOs

  • 1. IchniOnline.asmdef — 添加 BestHTTP 引用

    What to do:

    • IchniOnline.asmdefreferences 数组中追加 BestHTTP 的 GUID
    • BestHTTP Runtime asmdef GUID: 9069ac25d95ca17448a247f3bb1c769f
    • 验证 Unity 编译无误IchniOnline 程序集可以 using Best.HTTP;

    Must NOT do:

    • 不要修改其他 asmdef
    • 不要修改 IchniOnline.Editor.asmdef

    Recommended Agent Profile:

    • Category: quick
      • Reason: 单行 asmdef 修改,极简单的任务
    • Skills: None needed

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 2, 3, 4)
    • Blocks: Tasks 5, 6
    • Blocked By: None

    References:

    • Assets/Scripts/Online/IchniOnline.asmdef — 目标文件
    • Packages/com.tivadar.best.http/Runtime/com.Tivadar.Best.HTTP.asmdef.meta:L1-7 — GUID: 9069ac25d95ca17448a247f3bb1c769f

    Acceptance Criteria:

    • IchniOnline.asmdefreferences 数组包含 "GUID:9069ac25d95ca17448a247f3bb1c769f"
    • Unity 编译完成后IchniOnline 程序集内可以 using Best.HTTP; 无报错

    QA Scenarios:

    Scenario: asmdef 引用验证
      Tool: Bash (配合 Unity Editor)
      Preconditions: Unity Editor 已打开IchniOnline.asmdef 已修改
      Steps:
        1. 在 Unity 中等待编译完成AssetDatabase.Refresh 或观察 Console 无红错)
        2. 打开 `Assets/Scripts/Online/IchniOnline.asmdef` 确认 references 包含 BestHTTP GUID
      Expected Result: 编译零错误,无 warning
      Evidence: .omo/evidence/task-1-asmdef-refs.png (Editor 无错误截图)
    

    Commit: YES (group with Tasks 2-4)

    • Message: feat(online): add BestHTTP reference to IchniOnline.asmdef
    • Files: Assets/Scripts/Online/IchniOnline.asmdef
    • Pre-commit: 验证 Unity 编译通过

  • 2. ApiResponse.cs — 创建服务端响应模型

    What to do:

    • 新建文件: Assets/Scripts/Online/Network/Models/ApiResponse.cs
    • 定义 ResponseCode enum匹配服务端 Models/Responses/ResponseCode.cs
    • 定义 GlobalResponse<T> class匹配服务端 GlobalResponse<T>
    • 定义 ApiResult<T> 封装类,包含成功/失败状态 + 错误信息
    • 使用 System.Text.JsonUnityEngine.JsonUtility 做序列化(优先 JsonUtility 避免额外依赖)
    • 命名空间: IchniOnline.Online.Network.Models

    关键映射:

    // 服务端 ResponseCode
    public enum ResponseCode {
        Ok = 10000,
        BadRequest = 10400,
        Unauthorized = 10401,
        Forbidden = 10403,
        NotFound = 10404,
        InternalServerError = 10500
    }
    
    // 服务端 GlobalResponse<T>
    // 注意: Unity JsonUtility 不支持泛型直接反序列化,需要包装或使用封装层
    // 方案: 实现一个非泛型的 ApiResponse 做中间反序列化,再通过 ApiResult<T> 取 Data
    

    Must NOT do:

    • 不要引入 Newtonsoft.Json项目中已无引用
    • 不要将序列化逻辑写死到 ApiClient 之外

    Recommended Agent Profile:

    • Category: quick
      • Reason: 纯数据模型定义,无复杂逻辑
    • Skills: None

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 1, 3, 4)
    • Blocks: Tasks 5, 6
    • Blocked By: None

    References:

    • D:\Projects\IchniOnline\IchniOnline.Server\Models\Responses\GlobalResponse.cs — 服务端 GlobalResponse 实现
    • D:\Projects\IchniOnline\IchniOnline.Server\Models\Responses\ResponseCode.cs — 服务端 ResponseCode enum

    Acceptance Criteria:

    • ApiResponse.cs 中定义 ResponseCode enumOk/BadRequest/Unauthorized/Forbidden/NotFound/InternalServerError
    • GlobalResponse<T> class 包含 Code/Message/Data 三个字段
    • Unity 编译通过,无错误

    QA Scenarios:

    Scenario: 编译验证
      Tool: Unity Editor
      Preconditions: 文件创建完成Unity 编译通过
      Steps:
        1. 在任意脚本中添加 `using IchniOnline.Online.Network.Models;`
        2. 使用 `ResponseCode.Ok` 确认 enum 可用
      Expected Result: 编译零错误
      Evidence: .omo/evidence/task-2-compile.png
    

    Commit: YES (group with Tasks 1, 3, 4)


  • 3. AuthDtos.cs — 创建认证请求/响应 DTO

    What to do:

    • 新建文件: Assets/Scripts/Online/Network/Models/AuthDtos.cs
    • 定义以下 DTO匹配服务端 Models
    // 请求 DTO
    [System.Serializable]
    public class ThirdPartyLoginRequestDto {
        public string Token;        // accessToken.kid
        public string TokenType;    // accessToken.tokenType ("mac")
        public string MacKey;       // accessToken.macKey
        public string MacAlgorithm; // accessToken.macAlgorithm ("hmac-sha-1")
    }
    
    [System.Serializable]
    public class LoginRequestDto {
        public string Username;
        public string EncryptedPassword; // Base64 of XOR'd bytes
        public string SessionKey;
    }
    
    [System.Serializable]
    public class RegisterRequestDto {
        public string Username;
        public string Password;
        public string DisplayName;
    }
    
    // 响应 DTO与服务端 AuthResponse.cs 对应)
    [System.Serializable]
    public class SessionKeyResponseDto {
        public string sessionKey;
        public string expiresAt;
    }
    
    [System.Serializable]
    public class LoginResponseDto {
        public string Token;   // JWT
        public UserResponseDto User;
    }
    
    [System.Serializable]
    public class UserResponseDto {
        public string UserId;
        public string Username;
        public string DisplayName;
        public string AvatarUrl;
        public int Permission;  // 0=Guest, 1=Player, 2=Admin
    }
    

    Must NOT do:

    • 不要包含非认证相关的 DTO如 Beatmap 相关)

    Recommended Agent Profile:

    • Category: quick
      • Reason: 纯数据结构定义
    • Skills: None

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 1, 2, 4)
    • Blocks: Tasks 5, 7
    • Blocked By: None

    References:

    • D:\Projects\IchniOnline\IchniOnline.Server\Models\Requests\ThirdPartyLoginRequest.cs
    • D:\Projects\IchniOnline\IchniOnline.Server\Models\Requests\LoginRequest.cs
    • D:\Projects\IchniOnline\IchniOnline.Server\Models\Requests\RegisterRequest.cs
    • D:\Projects\IchniOnline\IchniOnline.Server\Models\Responses\AuthResponse.cs

    Acceptance Criteria:

    • 所有 DTO 定义为 [System.Serializable]
    • Unity 编译通过
    • 字段名小写JSON 反序列化兼容服务端 PascalCase → JsonUtility 需字段名匹配)

    QA Scenarios:

    Scenario: 编译验证
      Tool: Unity Editor
      Preconditions: 文件创建完成
      Steps: Unity 编译自动触发
      Expected Result: 无编译错误
      Evidence: .omo/evidence/task-3-compile.png
    

    Commit: YES (group with Tasks 1, 2, 4)


  • 4. LoginCacheData.cs — 扩展 JWT 和服务器用户数据字段

    What to do:

    • 编辑 Assets/Scripts/Online/Models/LoginCacheData.cs
    • 添加以下字段:
      public string jwtToken;       // JWT Bearer token
      public string userId;         // 服务端返回的 UserId (Guid 字符串)
      public string displayName;    // 服务端返回的 DisplayName
      public string avatarUrl;      // 服务端返回的 AvatarUrl
      public int permission;        // 服务端返回的 Permission
      public bool hasServerSession; // 标记是否已完成服务端认证
      
    • 更新 IsValid 逻辑:hasServerSession && !string.IsNullOrEmpty(jwtToken)
    • 保持向后兼容:构造旧字段保留,无 session 时 hasServerSession=false
    • 添加 UpdateFromServerResponse(LoginResponseDto response) 方法
    • 添加 ClearServerSession() 方法(仅清除 JWT 系列字段,保留 TapTap 原始信息)

    Must NOT do:

    • 不要删除已有字段 (openId/unionId/name/avatar/email/cacheTimestamp)
    • 不要破坏 LoginCacheEditor.cs 中使用的公开接口

    Recommended Agent Profile:

    • Category: quick
      • Reason: 小幅度字段扩展,结构简单
    • Skills: None

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 1, 2, 3)
    • Blocks: Task 6
    • Blocked By: None

    References:

    • Assets/Scripts/Online/Models/LoginCacheData.cs — 当前文件
    • D:\Projects\IchniOnline\IchniOnline.Server\Models\Responses\AuthResponse.cs — 服务端 LoginResponse/UserResponse

    Acceptance Criteria:

    • LoginCacheData 包含新字段 jwtToken, userId, displayName, avatarUrl, permission, hasServerSession
    • 旧 ES3 缓存数据加载后不会报错(缺失字段为默认值,hasServerSession=false
    • IsValidhasServerSession 为 true 时检查 jwtToken 非空
    • Unity 编译通过

    QA Scenarios:

    Scenario: 向后兼容测试
      Tool: Unity Editor + Bash (ES3 操作)
      Preconditions: LoginCacheData 已修改Unity 已编译
      Steps:
        1. 用 LoginCacheEditor.GenerateMockData 写入旧格式数据
        2. 读取 LoginCacheManager.CachedData
        3. 确认 hasServerSession=false旧字段值正确
      Expected Result: 旧缓存被正确加载,不丢失数据,不报错
      Evidence: .omo/evidence/task-4-backward-compat.png
    

    Commit: YES (group with Tasks 1, 2, 3)


  • 5. ApiClient.cs — BestHTTP 封装单例

    What to do:

    • 新建文件: Assets/Scripts/Online/Network/ApiClient.cs
    • 创建 IchniOnlineApiClient 类(非 MonoBehaviour 单例,或挂载到 DontDestroyOnLoad 对象)
    • 核心功能:
      1. Base URL 配置: public string BaseUrl { get; set; },初始值 http://localhost:5433
      2. JWT 管理: public string JwtToken { get; set; },设置后自动在请求头注入
      3. GET 请求: Task<ApiResult<T>> GetAsync<T>(string endpoint)
      4. POST 请求: Task<ApiResult<T>> PostAsync<T>(string endpoint, object body)
      5. 内部实现: 使用 BestHTTP 的 HTTPRequest + GetHTTPResponseAsync()
        • POST body 序列化: request.RawData = Encoding.UTF8.GetBytes(JsonUtility.ToJson(body))
        • Header 设置: request.SetHeader("Content-Type", "application/json")
        • JWT 注入: request.SetHeader("Authorization", $"Bearer {JwtToken}")
      6. 响应解析: 从 resp.DataAsText 反序列化为 GlobalResponse<T>,提取 Data
      7. 错误处理:
        • HTTP 状态码错误 → ApiResult 含错误信息
        • 解析失败 → ApiResult 含异常信息
        • 网络超时 → ApiResult.Timeout
        • 服务器返回失败码 → 按 Code 分类处理

    Error Handling Design:

    public class ApiResult<T> {
        public bool IsSuccess;
        public T Data;
        public int Code;           // ResponseCode
        public string Message;     // 服务端返回的 Message
        public string ErrorDetail; // 客户端错误详情
    
        public static ApiResult<T> Ok(T data) => ...;
        public static ApiResult<T> Fail(int code, string msg, string detail = null) => ...;
    }
    

    Must NOT do:

    • 不要阻塞 Unity 主线程(所有请求使用 async/await
    • 不要硬编码 URLBaseUrl 必须可配置)
    • 不要在 ApiClient 内处理业务逻辑(仅做 HTTP 通信)

    Recommended Agent Profile:

    • Category: unspecified-high
      • Reason: 需要理解 BestHTTP API、异步模式、JSON 序列化,有较多细节
    • Skills: None (BestHTTP API 文档已通过前期研究覆盖)

    Parallelization:

    • Can Run In Parallel: YES (with Task 6)
    • Parallel Group: Wave 2 (with Task 6)
    • Blocks: Task 7
    • Blocked By: Tasks 1, 2, 3

    References:

    • Packages/com.tivadar.best.http/Runtime/HTTP/HTTPRequestAsyncExtensions.cs — BestHTTP async extension methods
    • Packages/com.tivadar.best.http/Runtime/HTTP/HTTPRequest.cs — HTTPRequest class API
    • D:\Projects\IchniOnline\IchniOnline.Server\Models\Responses\GlobalResponse.cs — 服务端响应格式
    • Assets/Scripts/Online/Network/Models/ApiResponse.cs — 客户端响应模型(同 Wave 1 Task 2

    Acceptance Criteria:

    • IchniOnlineApiClient 可配置 BaseUrl
    • GetAsync<T> 发送 GET 请求并正确解析 GlobalResponse<T>
    • PostAsync<T> 发送 POST 请求body 序列化为 JSON
    • JWT Token 自动注入到 Authorization Header
    • 网络错误/服务端错误被正确封装为 ApiResult<T>
    • Unity 编译通过

    QA Scenarios:

    Scenario: GET 请求 + 响应解析
      Tool: Unity Editor + Bash (可启动测试后端或 mock)
      Preconditions: IchniOnline 后端在 localhost:5433 运行(或 mock 端点)
      Steps:
        1. 在 Unity Start() 中调用 ApiClient.GetAsync<object>("/api/test/health")
        2. 检查 Console 输出请求/响应日志
      Expected Result: 请求发送成功,响应被正确解析
      Evidence: .omo/evidence/task-5-get-request.png
    
    Scenario: JWT 注入验证
      Tool: Unity Editor
      Preconditions: ApiClient.JwtToken 设置为 "test-token"
      Steps:
        1. 发起 PostAsync 请求
        2. 检查请求 Header 包含 "Authorization: Bearer test-token"
      Expected Result: Authorization Header 正确注入
      Evidence: .omo/evidence/task-5-jwt-header.png
    

    Commit: YES (group with Task 6)

    • Message: feat(online): implement API client and extend login cache

  • 6. LoginCacheManager.cs — 扩展 JWT 存取方法

    What to do:

    • 编辑 Assets/Scripts/Online/Logic/LoginCacheManager.cs
    • 添加新方法:
      // 保存完整认证会话JWT + 用户数据)
      public static void SaveAuthSession(string jwtToken, LoginResponseDto response)
      
      // 清除会话(保留 TapTap 原始数据,清除 JWT/服务端数据)
      public static void ClearSession()
      
      // 获取缓存的 JWT Token
      public static string CachedJwtToken { get; }
      
      // 检查是否有有效的服务端会话
      public static bool HasValidSession { get; }
      
    • ES3 key 保持不变: Ichni_LoginCache
    • HasCachedLogin → 改为检查 HasValidSession
    • CachedData → 保持返回完整数据
    • SaveFromTapTapAccount → 保持原有行为(只存 TapTap 数据,不清除已有 JWT

    Must NOT do:

    • 不要修改 ES3 key 名称
    • 不要改变已有方法的签名

    Recommended Agent Profile:

    • Category: quick
      • Reason: 简单的方法扩展
    • Skills: None

    Parallelization:

    • Can Run In Parallel: YES (with Task 5)
    • Parallel Group: Wave 2 (with Task 5)
    • Blocks: Task 7
    • Blocked By: Tasks 1, 4

    References:

    • Assets/Scripts/Online/Logic/LoginCacheManager.cs — 当前文件
    • Assets/Scripts/Online/Models/LoginCacheData.cs — 扩展后的数据模型

    Acceptance Criteria:

    • SaveAuthSession(jwt, response) 正确写入 ES3
    • CachedJwtToken 从 ES3 读取正确
    • HasValidSession 在 jwt 为空或无 session 时返回 false
    • 编辑器中 Ichni/Login Cache 菜单仍正常工作
    • Unity 编译通过

    QA Scenarios:

    Scenario: 保存/读取 JWT 会话
      Tool: Unity Editor
      Preconditions: 已有 LoginCacheData 模型扩展
      Steps:
        1. LoginCacheManager.SaveAuthSession("test-jwt", mockLoginResponse)
        2. 读取 LoginCacheManager.CachedJwtToken
        3. 读取 LoginCacheManager.HasValidSession
      Expected Result: jwt="test-jwt", HasValidSession=true
      Evidence: .omo/evidence/task-6-save-jwt.png
    
    Scenario: 清除会话
      Steps:
        1. LoginCacheManager.ClearSession()
        2. 检查 HasValidSession
      Expected Result: HasValidSession=false, TapTap 原始数据保留
      Evidence: .omo/evidence/task-6-clear.png
    

    Commit: YES (group with Task 5)


  • 7. AuthService.cs — 认证编排服务

    What to do:

    • 新建文件: Assets/Scripts/Online/Logic/AuthService.cs
    • 创建 IchniOnlineAuthService 类(非 MonoBehaviour 静态类 或 通过 ThirdPartyServiceManager 实例管理)
    • 核心接口:
    public static class IchniOnlineAuthService {
        // 事件
        public static event Action<LoginResponseDto> OnLoginSuccess;
        public static event Action<string> OnLoginFailed;     // error message
        public static event Action OnLoginCanceled;
    
        // 属性
        public static bool IsLoggingIn { get; }
        public static bool IsLoggedIn { get; }                // HasValidSession
    
        // TapTap 第三方登录流程
        // 1. 调用 ThirdPartyServiceManager.StartTapTapLogin()
        // 2. 在 OnLoginSuccess 回调中:
        //    a. 从 TapTapAccount 取出 accessToken
        //    b. 构造 ThirdPartyLoginRequestDto
        //    c. 调用 ApiClient.PostAsync<LoginResponseDto>("/api/auth/third-party/login", body)
        //    d. 成功 → LoginCacheManager.SaveAuthSession(jwt, response)
        //    e. 失败 → 抛 OnLoginFailed
        //    f. ThirdParty.Unbound → 抛 OnLoginFailed("TapTap account not bound")
        public static void LoginWithTapTap();
    
        // 密码登录流程
        // 1. GET /api/auth/session-key → 得到 sessionKey
        // 2. 密码 XOR 加密:
        //    byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
        //    byte[] sessionBytes = Encoding.UTF8.GetBytes(sessionKey);
        //    byte[] encrypted = new byte[passwordBytes.Length];
        //    for (int i = 0; i < passwordBytes.Length; i++)
        //        encrypted[i] = (byte)(passwordBytes[i] ^ sessionBytes[i % sessionBytes.Length]);
        //    string encryptedPassword = Convert.ToBase64String(encrypted);
        // 3. POST /api/auth/login with { Username, EncryptedPassword, SessionKey }
        // 4. 成功 → LoginCacheManager.SaveAuthSession(jwt, response)
        public static async void LoginWithPassword(string username, string password);
    
        // 注册流程
        // POST /api/auth/register with { Username, Password, DisplayName }
        // 注意: 注册后不自动返回 JWT需要用户手动登录
        public static async void Register(string username, string password, string displayName);
    
        // 登出
        public static void Logout();
    
        // 密码 XOR 加密工具方法(可复用)
        public static string EncryptPassword(string password, string sessionKey);
    }
    

    Must NOT do:

    • 不要直接调用 TapTap SDK 的 LoginWithScopes由 ThirdPartyServiceManager 封装)
    • 不要在 AuthService 中管理 UI 状态(只触发事件,由 LoginPage 处理 UI
    • 不要硬编码 API 路径(用字符串常量集中管理)

    Recommended Agent Profile:

    • Category: unspecified-high
      • Reason: 涉及多步异步流程编排、事件管理、加密算法实现
    • Skills: None

    Parallelization:

    • Can Run In Parallel: NO
    • Parallel Group: Wave 3 (sequential)
    • Blocks: Tasks 8, 9, 10
    • Blocked By: Tasks 5, 6

    References:

    • Assets/Scripts/Online/Logic/ThirdPartyServiceManager.cs — TapTap SDK 调用封装
    • Assets/Scripts/Online/Network/ApiClient.cs — HTTP 通信Task 5
    • Assets/Scripts/Online/Logic/LoginCacheManager.cs — 缓存管理Task 6
    • D:\Projects\IchniOnline\IchniOnline.Server\Service\UserService.cs:213-225 — 服务端 XOR 解密算法(客户端需反转实现加密)
    • D:\Projects\IchniOnline\IchniOnline.Server\Controller\AuthController.cs — 服务端 API 端点定义

    Acceptance Criteria:

    • LoginWithTapTap() 完整流程: TapTap SDK → API call → JWT → 事件
    • LoginWithPassword() 完整流程: session key → XOR 加密 → API call → JWT → 事件
    • Register() 调用 POST /api/auth/register
    • Logout() 清除 JWT + TapTap 登出
    • EncryptPassword() 与服务端 DecryptPassword 互为逆运算
    • 所有事件正确触发OnLoginSuccess/OnLoginFailed/OnLoginCanceled
    • Unity 编译通过

    QA Scenarios:

    Scenario: 密码 XOR 加密验证
      Tool: C# 脚本验证
      Preconditions: AuthService.EncryptPassword 已实现
      Steps:
        1. 设 password="test123", sessionKey="base64encodedkey=="
        2. 调用 EncryptPassword 得到 encrypted
        3. 用服务端 DecryptPassword 逻辑解密: 取 encrypted Base64 → XOR with sessionKey bytes → 得到明文
      Expected Result: 解密后明文等于原始 password
      Evidence: .omo/evidence/task-7-xor-verify.png
    
    Scenario: TapTap 登录流程(模拟)
      Tool: Unity Editor Play Mode
      Preconditions: ThirdPartyServiceManager 已初始化
      Steps:
        1. AuthService.LoginWithTapTap()
        2. Console 观察: TapTap SDK 登录 → API POST 请求 → JWT 缓存
      Expected Result: 流程完整无异常
      Evidence: .omo/evidence/task-7-taaptap-flow.png
    

    Commit: YES

    • Message: feat(online): implement auth service with TapTap/password/register flows
    • Files: Assets/Scripts/Online/Logic/AuthService.cs
    • Pre-commit: Unity 编译检查

  • 8. ThirdPartyServiceManager.cs — 集成 AuthService

    What to do:

    • 编辑 Assets/Scripts/Online/Logic/ThirdPartyServiceManager.cs
    • StartTapTapLogin()LoginWithScopes 成功后,不再直接调用 LoginCacheManager.SaveFromTapTapAccount(account)(该操作移至 AuthService 流程中)
    • 添加事件 OnTapTapAccessTokenReceived(AccessToken token) 或修改流程,使 AuthService 能拿到 accessToken
    • 可选方案 A: ThirdPartyServiceManager 保留纯 SDK 封装AuthService 通过 TapTapLogin.Instance.GetCurrentTapAccount() 获取 account
    • 可选方案 B: ThirdPartyServiceManager 的 OnLoginSuccess 事件中附带 accessToken
    • 推荐方案 B: 扩展 OnLoginSuccess 事件参数或添加新事件
      // 新增事件: 携带 accessToken供 AuthService 使用)
      public event Action<TapTapAccount, AccessToken> OnLoginWithToken;
      
    • StartTapTapLogin() 保持不变(依然触发 OnLoginSuccess/OnLoginCanceled/OnLoginFailed

    Must NOT do:

    • 不要移除已有的事件OnLoginSuccess/OnLoginCanceled/OnLoginFailed
    • 不要改变 TapTap SDK 初始化逻辑

    Recommended Agent Profile:

    • Category: quick
      • Reason: 小幅接口扩展
    • Skills: None

    Parallelization:

    • Can Run In Parallel: YES (with Tasks 9, 10)
    • Parallel Group: Wave 4 (with Tasks 9, 10)
    • Blocks: None
    • Blocked By: Task 7

    References:

    • Assets/Scripts/Online/Logic/ThirdPartyServiceManager.cs — 当前文件
    • D:\Projects\Open\TapSDKLogin-Unity\Runtime\Public\TapTapAccount.cs — TapTapAccount 结构(含 accessToken 属性)

    Acceptance Criteria:

    • OnLoginWithToken 事件在 TapTap 登录成功后触发
    • 事件参数包含完整的 TapTapAccountAccessToken
    • Unity 编译通过

    QA Scenarios:

    Scenario: 事件触发测试
      Tool: Unity Editor Play Mode
      Preconditions: ThirdPartyServiceManager 已初始化
      Steps:
        1. 订阅 ThirdPartyServiceManager.Instance.OnLoginWithToken
        2. 调用 StartTapTapLogin()
        3. TapTap 登录成功后检查事件是否触发
      Expected Result: 事件触发accessToken.kid/macKey/macAlgorithm 非空
      Evidence: .omo/evidence/task-8-event.png
    

    Commit: YES (group with Tasks 9, 10)

    • Message: feat(ui): integrate auth service into login UI

  • 9. LoginPage.cs — 使用新 AuthService 驱动流程

    What to do:

    • 编辑 Assets/Scripts/UI/LoginPage/LoginPage.cs
    • 修改 OnTapTapClicked():
      • 不再直接调用 ThirdPartyServiceManager.Instance.StartTapTapLogin()
      • 改为调用 IchniOnlineAuthService.LoginWithTapTap()
    • 修改 OnTapTapLoginSuccess(TapTapAccount account):
      • 不再立即 FadeOut改为等待 AuthService 的服务端验证完成)
      • 改为 IchniOnlineAuthService.OnLoginSuccess += OnApiLoginSuccess
    • 添加新回调 OnApiLoginSuccess(LoginResponseDto response):
      • 服务端验证成功后 FadeOut + 恢复 StartPage
    • 添加 UI 加载状态:
      • 显示加载指示器(或按钮禁用+文字变化)在等待服务端响应期间
    • 事件订阅:
      • 订阅 AuthService.OnLoginSuccess / OnLoginFailed / OnLoginCanceled
      • 替代原有的 ThirdPartyServiceManager 事件(或共存)
    • 账户密码登录 UI:
      • 如果 LoginPage 上已有用户名/密码输入框 → 绑定到 IchniOnlineAuthService.LoginWithPassword()
      • 如果没有 → 增加简单的用户名/密码输入框 + 登录按钮

    Must NOT do:

    • 不要删除 closeButton 和原有的 UI 结构
    • 不要修改 UIPageBase 基类

    Recommended Agent Profile:

    • Category: visual-engineering
      • Reason: 涉及 UI 逻辑更新和用户交互流程
    • Skills: None

    Parallelization:

    • Can Run In Parallel: YES (with Tasks 8, 10)
    • Parallel Group: Wave 4 (with Tasks 8, 10)
    • Blocks: None
    • Blocked By: Task 7

    References:

    • Assets/Scripts/UI/LoginPage/LoginPage.cs — 当前文件
    • Assets/Scripts/Online/Logic/AuthService.cs — 认证服务Task 7
    • Assets/Scripts/UI/StartPage/StartUIPage.cs — StartPage 交互恢复
    • Assets/Scripts/UI/StartPage/StartUIPage.cs:TouchToStart() — 缓存检测入口

    Acceptance Criteria:

    • TapTap 按钮触发 AuthService.LoginWithTapTap() 而非直接调用 ThirdPartyServiceManager
    • 登录请求加载状态有 UI 反馈
    • 服务端返回成功后 FadeOut + 恢复 StartPage
    • 失败时恢复按钮交互
    • Unity 编译通过

    QA Scenarios:

    Scenario: TapTap 登录 UI 流程
      Tool: Unity Editor Play Mode
      Preconditions: LoginPage 在 MenuScene 中打开
      Steps:
        1. 点击 TapTap 按钮
        2. 观察按钮变为非交互状态
        3. TapTap SDK 登录窗口出现
        4. 完成 TapTap 登录 → 等待服务端验证
        5. 成功后 LoginPage 淡出 → 恢复 StartPage
      Expected Result: UI 流程完整,无中断
      Evidence: .omo/evidence/task-9-ui-flow.png
    

    Commit: YES (group with Tasks 8, 10)


  • 10. StartUIPage.cs — 校验会话有效性

    What to do:

    • 编辑 Assets/Scripts/UI/StartPage/StartUIPage.cs
    • TouchToStart() 中改用 LoginCacheManager.HasValidSession 替代 LoginCacheManager.HasCachedLogin
    • 添加可选的 Token 过期检测:
      • JWT 本身包含过期时间(可解析 payload 中的 exp 字段)
      • 如果 JWT 已过期 → 清除 session → 显示 LoginPage
      • 简单方案: 暂不做 JWT 解码解析,只检查 HasValidSession
    • 如有 RestoreInteraction() 方法保持现有逻辑

    Must NOT do:

    • 不要修改不相关的 StartUIPage 逻辑
    • 不要引入 JWT 解码库(暂不解码 JWT payload

    Recommended Agent Profile:

    • Category: visual-engineering
    • Skills: None

    Parallelization:

    • Can Run In Parallel: YES (with Tasks 8, 9)
    • Parallel Group: Wave 4 (with Tasks 8, 9)
    • Blocks: None
    • Blocked By: Task 7

    References:

    • Assets/Scripts/UI/StartPage/StartUIPage.cs — 当前文件
    • Assets/Scripts/Online/Logic/LoginCacheManager.cs — 扩展后的缓存管理器Task 6

    Acceptance Criteria:

    • TouchToStart() 使用 LoginCacheManager.HasValidSession 判断
    • 有有效 session → 直接进 ChapterSelection
    • 无有效 session → 显示 LoginPage
    • Unity 编译通过

    QA Scenarios:

    Scenario: 有 JWT session 时跳过登录
      Tool: Unity Editor Play Mode
      Preconditions: ES3 中存在有效 JWT 缓存
      Steps:
        1. 进入 MenuScene
        2. 点击 TouchToStart
      Expected Result: 直接进入 ChapterSelection不显示 LoginPage
      Evidence: .omo/evidence/task-10-skip-login.png
    
    Scenario: 无 JWT session 时显示登录
      Preconditions: 清除 ES3 缓存
      Steps:
        1. 进入 MenuScene
        2. 点击 TouchToStart
      Expected Result: LoginPage 显示
      Evidence: .omo/evidence/task-10-show-login.png
    

    Commit: YES (group with Tasks 8, 9)


Final Verification Wave

  • F1. Plan Compliance Auditoracle Read the plan end-to-end. For each "Must Have": verify implementation exists (read file, check compilation). For each "Must NOT Have": search codebase for forbidden patterns — reject with file:line if found. Check evidence files exist in .omo/evidence/. Compare deliverables against plan. Output: Must Have [N/N] | Must NOT Have [N/N] | Tasks [N/N] | VERDICT: APPROVE/REJECT

  • F2. Code Quality Reviewunspecified-high Check compilation status. Review changed files: proper async patterns (no sync-over-async), no try/catch swallowing, proper error handling, no magic strings (use constants), correct [Serializable] attributes on DTOs. Output: Build [PASS/FAIL] | Code Quality [N clean/N issues] | VERDICT

  • F3. Real Manual QAunspecified-high Start from clean scene state. Execute QA scenarios from EVERY task — follow exact steps, capture evidence. Test integration flow: TapTap login → API call → JWT cache → restart → skip login. Test edge cases: network error, invalid credentials, ThirdParty.Unbound. Output: Scenarios [N/N pass] | Integration [N/N] | Edge Cases [N tested] | VERDICT

  • F4. Scope Fidelity Checkdeep For each task: read "What to do", read actual diff (git log/diff). Verify 1:1 — everything in spec was built (no missing), nothing beyond spec was built (no creep). Check "Must NOT do" compliance. Detect cross-task contamination. Output: Tasks [N/N compliant] | Contamination [CLEAN/N issues] | Unaccounted [CLEAN/N files] | VERDICT


Commit Strategy

  • C1 (Task 1-4): feat(online): add network models and asmdef references
  • C2 (Task 5-6): feat(online): implement API client and extend login cache
  • C3 (Task 7): feat(online): implement auth service with TapTap/password/register flows
  • C4 (Task 8-10): feat(ui): integrate auth service into login UI

Success Criteria

Verification Commands

# In Unity Editor:
# 1. Open MenuScene
# 2. Enter Play Mode
# 3. Click TapTap Login button → should call POST /api/auth/third-party/login
# 4. Check Console for network request/response logs
# 5. Exit Play Mode → check ES3 cache contains JWT data

Final Checklist

  • No compilation errors in IchniOnline.asmdef or dependent assemblies
  • TapTap login → server API call → JWT → ES3 cache
  • Password login (if server running) → JWT → ES3 cache
  • Register (if server running) → user created
  • LoginPage responds to all auth states (loading/success/failure)