# 用户认证系统 **本文档引用的文件** - [src/index.ts](file://src/index.ts) - [src/command/index.ts](file://src/command/index.ts) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts) - [src/command/handlers/logout.ts](file://src/command/handlers/logout.ts) - [src/command/handlers/history.ts](file://src/command/handlers/history.ts) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts) - [src/command/handlers/events.ts](file://src/command/handlers/events.ts) - [src/command/handlers/eventDetails.ts](file://src/command/handlers/eventDetails.ts) - [wrangler.jsonc](file://wrangler.jsonc) - [package.json](file://package.json) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考量](#性能考量) 8. [故障排除指南](#故障排除指南) 9. [结论](#结论) ## 简介 本项目是一个基于 Cloudflare Workers 的 Telegram 机器人,提供用户认证与活动预约功能。认证系统采用交互式对话完成用户名/密码登录,通过 Cosmoe API 获取 Token 并持久化到 KV 存储;后续需要授权的操作(如查看历史、预约活动)均需使用已存储的凭证进行鉴权。系统未实现中间件层,而是通过各命令处理器在执行前主动检查凭证有效性。 ## 项目结构 项目采用按职责分层的组织方式: - 入口与路由:src/index.ts 负责初始化 Bot、注册命令菜单与 Webhook 回调 - 命令与对话:src/command/index.ts 安装对话插件、注册所有命令与回调处理器 - 认证客户端:src/client/cosmoe.ts 提供与外部 Cosmoe API 的交互能力,包括 Token 获取与受保护接口调用 - 处理器:src/command/handlers 下的各文件分别处理不同命令与业务流程 - 配置:wrangler.jsonc 定义 Worker 名称、触发器、KV 绑定等运行时配置 ```mermaid graph TB A["入口: src/index.ts"] --> B["命令注册: src/command/index.ts"] B --> C["登录处理器: src/command/handlers/login.ts"] B --> D["登出处理器: src/command/handlers/logout.ts"] B --> E["历史查询: src/command/handlers/history.ts"] B --> F["活动预约: src/command/handlers/bookEvent.ts"] B --> G["活动列表: src/command/handlers/events.ts"] B --> H["活动详情: src/command/handlers/eventDetails.ts"] C --> I["认证客户端: src/client/cosmoe.ts"] D --> I E --> I F --> I G --> I H --> I A --> J["配置: wrangler.jsonc"] B --> K["依赖: package.json"] ``` **图表来源** - [src/index.ts](file://src/index.ts#L1-L47) - [src/command/index.ts](file://src/command/index.ts#L1-L110) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L113-L171) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L1-L75) - [src/command/handlers/logout.ts](file://src/command/handlers/logout.ts#L1-L34) - [src/command/handlers/history.ts](file://src/command/handlers/history.ts#L1-L107) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts#L1-L226) - [src/command/handlers/events.ts](file://src/command/handlers/events.ts#L1-L27) - [src/command/handlers/eventDetails.ts](file://src/command/handlers/eventDetails.ts#L1-L61) - [wrangler.jsonc](file://wrangler.jsonc#L1-L31) - [package.json](file://package.json#L1-L24) **章节来源** - [src/index.ts](file://src/index.ts#L1-L47) - [src/command/index.ts](file://src/command/index.ts#L1-L110) - [wrangler.jsonc](file://wrangler.jsonc#L1-L31) - [package.json](file://package.json#L1-L24) ## 核心组件 - Bot 与环境绑定:入口文件初始化 Bot 并注入 KV 命名空间,同时设置命令菜单与 Webhook 回调 - 对话系统:通过 @grammyjs/conversations 插件与 KV 适配器实现对话状态持久化 - 认证客户端:封装与 Cosmoe API 的交互,支持获取 Token、设置凭证、判断认证状态以及调用受保护接口 - 登录处理器:交互式对话收集用户名/密码,调用认证客户端获取 Token,并将 user_id、token 与时间戳写入 KV - 登出处理器:根据 Telegram 用户 ID 读取并删除 KV 中的凭证 - 历史与预约处理器:在执行前从 KV 读取凭证,设置到认证客户端,再调用受保护接口 **章节来源** - [src/index.ts](file://src/index.ts#L6-L11) - [src/command/index.ts](file://src/command/index.ts#L20-L57) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L113-L171) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L13-L74) - [src/command/handlers/logout.ts](file://src/command/handlers/logout.ts#L10-L34) - [src/command/handlers/history.ts](file://src/command/handlers/history.ts#L4-L30) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts#L11-L40) ## 架构总览 下图展示了认证系统的关键交互路径:用户通过 /login 进入对话,输入凭据后由认证客户端向外部 API 请求 Token,并将凭证存入 KV;后续命令在执行前从 KV 读取凭证并设置到认证客户端以完成鉴权。 ```mermaid sequenceDiagram participant U as "用户" participant T as "Telegram Bot" participant L as "登录处理器(login.ts)" participant C as "认证客户端(cosmoe.ts)" participant KV as "KV存储(COSMOE_CREDENTIALS)" participant API as "Cosmoe API" U->>T : "/login" T->>L : 进入对话 L->>U : "请输入用户名" U-->>L : 用户名 L->>U : "请输入密码" U-->>L : 密码 L->>C : getToken(用户名, 密码) C->>API : POST /CreatToken.php API-->>C : 返回 {code, data : {user_id, token}} C-->>L : 认证结果 alt 成功 L->>KV : put(Telegram用户ID, {user_id, token, timestamp}) L-->>U : "登录成功,凭证已存储" else 失败 L-->>U : "登录失败:用户名或密码错误" end ``` **图表来源** - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L13-L74) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L124-L140) - [wrangler.jsonc](file://wrangler.jsonc#L21-L30) **章节来源** - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L13-L74) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L113-L171) - [wrangler.jsonc](file://wrangler.jsonc#L21-L30) ## 详细组件分析 ### 登录流程(用户名/密码验证、Token 获取与存储) - 输入收集:通过对话等待用户输入用户名与密码,避免明文泄露 - 凭证校验:调用认证客户端的 getToken 方法,向外部 API 发送表单数据 - 成功分支:若返回 code=200 且包含 user_id 与 token,则将凭证写入 KV,键为 Telegram 用户 ID - 失败分支:提示用户名或密码错误 - 异常处理:捕获并记录错误,向用户反馈“稍后重试” ```mermaid flowchart TD Start(["进入登录对话"]) --> AskUser["提示输入用户名"] AskUser --> GetUser["等待用户名输入"] GetUser --> CheckUser{"用户名存在?"} CheckUser --> |否| Cancel1["取消登录并提示"] CheckUser --> |是| AskPass["提示输入密码"] AskPass --> GetPass["等待密码输入"] GetPass --> CheckPass{"密码存在?"} CheckPass --> |否| Cancel2["取消登录并提示"] CheckPass --> |是| CallAPI["调用getToken获取Token"] CallAPI --> Result{"code=200且有token?"} Result --> |是| SaveKV["以Telegram用户ID为键保存凭证到KV"] SaveKV --> Done(["回复登录成功"]) Result --> |否| Fail(["回复登录失败"]) Cancel1 --> End(["结束"]) Cancel2 --> End Done --> End Fail --> End ``` **图表来源** - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L19-L74) **章节来源** - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L13-L74) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L124-L140) ### 登录后会话状态管理与凭证存储 - KV 键设计:以 Telegram 用户 ID 作为键,值为包含 user_id、token、timestamp 的 JSON - 读取与设置:历史查询与预约等处理器在执行前从 KV 读取凭证,设置到认证客户端 - 认证状态:认证客户端提供 isAuthenticated 与 getCredentials 方法用于判断与获取当前凭证 ```mermaid classDiagram class CosmoeClient { -number userId -string token +getToken(username, password) ApiResponse +setCredentials(userId, token) void +getCredentials() {userId, token}|null +isAuthenticated() boolean } class LoginHandler { +handleInteractiveLogin(conversation, ctx, env) Promise } class HistoryHandler { +handleHistoryCommand(ctx, env) Promise } class BookEventHandler { +handleBookEvent(ctx, env) Promise } LoginHandler --> CosmoeClient : "获取Token并设置凭证" HistoryHandler --> CosmoeClient : "读取凭证并调用受保护接口" BookEventHandler --> CosmoeClient : "读取凭证并调用受保护接口" ``` **图表来源** - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L113-L171) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L47-L65) - [src/command/handlers/history.ts](file://src/command/handlers/history.ts#L13-L30) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts#L22-L40) **章节来源** - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L49-L65) - [src/command/handlers/history.ts](file://src/command/handlers/history.ts#L13-L30) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts#L22-L40) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L156-L171) ### 登出流程(清理机制、安全考虑与最佳实践) - 身份确认:从上下文提取 Telegram 用户 ID - 凭证检查:读取 KV 中是否存在对应凭证 - 删除操作:存在则删除该键,不存在则提示先登录 - 反馈与异常:统一回复与错误日志记录 ```mermaid sequenceDiagram participant U as "用户" participant T as "Telegram Bot" participant LO as "登出处理器(logout.ts)" participant KV as "KV存储(COSMOE_CREDENTIALS)" U->>T : "/logout" T->>LO : 处理登出请求 LO->>LO : 获取Telegram用户ID LO->>KV : get(用户ID) alt 无凭证 LO-->>U : "请先使用 /login 命令登录" else 有凭证 LO->>KV : delete(用户ID) LO-->>U : "成功登出,账户信息已清除" end ``` **图表来源** - [src/command/handlers/logout.ts](file://src/command/handlers/logout.ts#L10-L34) - [wrangler.jsonc](file://wrangler.jsonc#L21-L30) **章节来源** - [src/command/handlers/logout.ts](file://src/command/handlers/logout.ts#L10-L34) ### 认证状态检查与错误处理策略 - 认证状态检查:历史查询与预约处理器在调用受保护接口前,先从 KV 读取凭证并设置到认证客户端,随后通过 isAuthenticated 判断 - 错误处理:对 KV 读取失败、API 返回非 200、凭证无效等情况分别给出明确提示 - 最佳实践:对敏感操作(如变更密码、取消预约)在处理器内部再次校验认证状态,确保安全性 ```mermaid flowchart TD Enter(["进入受保护操作"]) --> ReadKV["从KV读取凭证"] ReadKV --> Parse{"解析成功?"} Parse --> |否| PromptLogin["提示先登录"] Parse --> |是| SetCreds["设置到认证客户端"] SetCreds --> CheckAuth{"isAuthenticated()?"} CheckAuth --> |否| PromptReauth["提示凭证无效并重新登录"] CheckAuth --> |是| CallAPI["调用受保护接口"] CallAPI --> Resp{"返回code=200?"} Resp --> |是| Success(["处理成功响应"]) Resp --> |否| ShowErr(["显示错误消息"]) PromptLogin --> End(["结束"]) PromptReauth --> End Success --> End ShowErr --> End ``` **图表来源** - [src/command/handlers/history.ts](file://src/command/handlers/history.ts#L13-L30) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts#L22-L40) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L169-L171) **章节来源** - [src/command/handlers/history.ts](file://src/command/handlers/history.ts#L13-L30) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts#L22-L40) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L169-L171) ### 会话超时与自动登出策略 - 当前实现:系统未对凭证设置过期时间或定期轮询校验,KV 中仅保存 user_id、token 与 timestamp - 建议策略: - 在 KV 值中增加 expires_in 或 refresh_token 字段,结合定时任务定期刷新 - 在处理器中增加对 timestamp 的校验,超过阈值则提示重新登录 - 结合外部 API 的 Token 有效期,在调用失败时自动触发刷新或提示重新登录 [本节为通用建议,无需特定文件引用] ### 认证中间件工作机制 - 当前架构:未实现全局中间件,各命令处理器在执行前自行检查凭证 - 实现建议:可在命令注册前安装中间件,拦截命令请求,统一完成凭证读取、校验与上下文注入,减少重复逻辑 [本节为概念性说明,无需特定文件引用] ## 依赖关系分析 - 运行时依赖:grammy、@grammyjs/conversations、@grammyjs/storage-cloudflare - 配置依赖:wrangler.jsonc 中定义 KV 命名空间绑定与触发器 - 内部依赖:命令处理器依赖认证客户端;登录/登出/历史/预约处理器共同依赖 KV 存储 ```mermaid graph LR P["package.json"] --> G["grammy"] P --> C["@grammyjs/conversations"] P --> S["@grammyjs/storage-cloudflare"] W["wrangler.jsonc"] --> KV1["COSMOE_CREDENTIALS"] W --> KV2["COSMOE_STORAGE"] CMD["命令注册(command/index.ts)"] --> H1["login.ts"] CMD --> H2["logout.ts"] CMD --> H3["history.ts"] CMD --> H4["bookEvent.ts"] CMD --> H5["events.ts"] CMD --> H6["eventDetails.ts"] H1 --> CL["cosmoe.ts"] H2 --> CL H3 --> CL H4 --> CL H5 --> CL H6 --> CL CL --> API["Cosmoe API"] ``` **图表来源** - [package.json](file://package.json#L18-L22) - [wrangler.jsonc](file://wrangler.jsonc#L21-L30) - [src/command/index.ts](file://src/command/index.ts#L1-L110) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L113-L171) **章节来源** - [package.json](file://package.json#L18-L22) - [wrangler.jsonc](file://wrangler.jsonc#L21-L30) - [src/command/index.ts](file://src/command/index.ts#L1-L110) ## 性能考量 - KV 读写:对话状态与用户凭证均通过 KV 读写,注意控制键数量与大小,避免频繁写入 - API 调用:受保护接口需携带 user_id 与 token,建议在处理器内复用认证客户端实例 - 消息长度:历史查询等场景对消息长度有限制,应做截断处理 [本节提供一般性指导,无需特定文件引用] ## 故障排除指南 - 登录失败 - 检查用户名/密码是否正确 - 查看认证客户端返回的错误信息 - 确认外部 API 可达 - 无法获取 Telegram 用户身份 - 检查上下文中的 from 字段是否可用 - 确认命令触发者为真实用户 - KV 读取失败 - 检查 KV 命名空间绑定是否正确 - 确认键格式与权限 - 凭证无效 - 重新执行 /login 获取新 Token - 检查认证客户端的 isAuthenticated 状态 - 预约失败 - 确认时间段仍有余位 - 检查优惠券是否可用 - 查看返回的错误消息 **章节来源** - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L67-L73) - [src/command/handlers/logout.ts](file://src/command/handlers/logout.ts#L14-L33) - [src/command/handlers/history.ts](file://src/command/handlers/history.ts#L15-L37) - [src/command/handlers/bookEvent.ts](file://src/command/handlers/bookEvent.ts#L43-L117) ## 结论 本认证系统通过交互式对话完成登录,使用 KV 存储凭证并在受保护操作前进行状态检查,整体流程清晰、易于维护。建议后续引入中间件统一鉴权、增加凭证过期与自动刷新机制,并完善错误日志与监控,以进一步提升安全性与用户体验。