# 后端 API(WxCheckMvc)规范 ## Purpose 本能力描述 `WxCheckMvc` 暴露给微信小程序与管理侧的 HTTP API:登录/注册、会话记录的增删改查、文件上传、地址补全,以及 Redis Stream 的消息读取。 路由约定:统一为 `/api/{Controller}/{Action}`。 ## Requirements ### Requirement: 小程序登录(基于微信 code) 系统 SHALL 提供登录接口,用 `wx.login()` 获取的 `code` 向微信换取 `openid`,并以 `openid` 作为系统内部 `UserKey`。 接口:`POST /api/Login/Login` 请求: - `Code: string` 响应(成功): - `success: true` - `data`: 用户对象(包含 `UserKey/openid` 与 `Token`) 行为: - 若 `xcx_users` 不存在该 `UserKey`,系统 SHALL 自动插入一条用户记录(资料可为空)。 - 若用户 `IsDisabled = 1`,系统 SHALL 返回 `success: false`。 #### Scenario: 首次登录自动建档 - **WHEN** 提交的 `code` 能换取有效 `openid` - **AND** 数据库中不存在该 `openid` - **THEN** 系统创建用户记录并返回 `Token` #### Scenario: 禁用用户登录 - **WHEN** 用户 `IsDisabled = 1` - **THEN** 系统返回 `success: false` 且提示用户已禁用 ### Requirement: 小程序注册/完善资料 系统 SHALL 提供接口用于完善用户资料(用户名、微信名、手机号、头像链接)。 接口:`POST /api/Login/Register` 请求: - `UserKey: string`(openid) - `UserName: string` - `WeChatName: string` - `PhoneNumber: string` - `AvatarUrl: string` 校验: - `UserKey` 必填 - `PhoneNumber` SHALL 清洗为纯数字后满足 `^1\d{10}$` - `UserName` SHALL 去除标点/符号/空白后仍非空 响应(成功): - `success: true` - `data`: 更新后的用户对象(包含 `Token`) #### Scenario: 正常完善资料 - **WHEN** 提交合法的用户名与手机号 - **AND** `UserKey` 对应用户存在 - **THEN** 系统更新用户资料并返回新 `Token` #### Scenario: 用户不存在 - **WHEN** `UserKey` 对应用户不存在 - **THEN** 系统返回 404 ### Requirement: 上传文件并可更新头像 系统 SHALL 提供文件上传接口,保存到后端 `wwwroot/{rootPathType}` 下,并返回可访问 URL。 接口:`POST /api/Check/UploadFile`(`multipart/form-data`) 表单字段: - `file`: 上传文件 - `rootPathType: string`(可选;默认 `Avatar`,仅允许字母/数字/下划线) - `userKey: string`(可选;若提供则更新 `xcx_users.AvatarUrl`) 响应: - `success: true/false` - `url`: 公开访问 URL - `path`: 相对路径 #### Scenario: 上传头像并更新用户表 - **WHEN** 上传文件并提供 `userKey` - **THEN** 系统保存文件并更新该用户 `AvatarUrl` ### Requirement: 新增会话记录 系统 SHALL 提供接口新增会话记录(软实时写入数据库),并尝试将消息投递到 Redis Stream。 接口:`POST /api/Check/AddConversation` 请求(核心字段): - `UserKey: string` - `ConversationContent: string` - `SendMethod: string` - `UserLocation: string`(当前实现中会尝试解析 `lat,lng`) - `MessageType: int`(默认 1) - `Guid?: string`(可选;缺省则服务端生成) - `SpeakingTime?: int` 行为: - 系统 SHALL 写入 `xcx_conversation`,并生成/使用 `Guid`。 - 系统 MAY 将会话与用户信息写入 Redis Stream(key: `xcx_msg`,group: `xcx_group`)。 #### Scenario: 新增会话并返回 guid - **WHEN** 提交包含 `UserKey` 与 `ConversationContent` 的请求 - **THEN** 系统创建会话记录并返回 `conversationGuid` ### Requirement: 查询会话记录(按用户) 系统 SHALL 提供按 `UserKey` 查询会话记录的接口,默认只返回未删除记录。 接口:`POST /api/Check/GetConversations` 请求: - `UserKey: string` - `MessageType: int`(0 不过滤;1 公有;2 私有;当前实现仅在 `MessageType == 1` 时追加过滤) #### Scenario: 查询用户所有未删除会话 - **WHEN** 提交 `UserKey` - **THEN** 系统返回该用户 `IsDeleted = 0` 的会话记录 ### Requirement: 分页查询会话记录 系统 SHALL 提供分页接口。 接口:`POST /api/Check/GetConversationsByPage` 请求: - `UserKey: string` - `Page: int`(<1 视为 1) - `PageSize: int`(1..100,否则默认 10) - `MessageType: int`(当前实现仅在 `MessageType == 1` 时追加过滤) 响应: - `data.conversations` - `data.totalCount / page / pageSize / totalPages` #### Scenario: PageSize 超限 - **WHEN** `PageSize > 100` - **THEN** 系统按默认值 10 处理 ### Requirement: 更新会话记录 系统 SHALL 提供接口按 `Guid + UserKey` 更新会话内容与发送方式。 接口:`POST /api/Check/UpdateConversation` 请求: - `Guid: string` - `UserKey: string` - `ConversationContent: string` - `SendMethod: string` - `MessageType: int` #### Scenario: 非本人更新 - **WHEN** `Guid` 存在但 `UserKey` 不匹配 - **THEN** 系统返回 404(记录不存在或无权限修改) ### Requirement: 删除会话记录(软删除) 系统 SHALL 提供接口按 `Guid + UserKey` 软删除会话。 接口:`POST /api/Check/DeleteConversation` 请求: - `Guid: string` - `UserKey: string` #### Scenario: 删除已删除记录 - **WHEN** 记录已被删除 - **THEN** 系统返回 404 ### Requirement: 按 Guid 查询会话(不受软删除影响) 系统 SHALL 提供接口按 `Guid` 查询单条会话记录,不过滤 `IsDeleted`。 接口:`POST /api/Check/GetConversationByGuid` #### Scenario: 查询已删除会话 - **WHEN** `Guid` 对应记录存在但 `IsDeleted = 1` - **THEN** 系统仍返回该记录 ### Requirement: 地址补全(经纬度→地址) 系统 SHALL 提供接口按 `Guid` 查找会话记录经纬度,并使用高德逆地理编码生成地址写回 `UserLocation`。 接口:`POST /api/Check/CheckAddress` #### Scenario: 记录不存在 - **WHEN** `Guid` 对应记录不存在或已删除 - **THEN** 系统返回 404 ### Requirement: Redis Stream 读取消息 系统 SHALL 提供接口从 Redis Stream 读取消息。 接口:`POST /api/Check/ReadMessageFromRedis` 请求: - `GroupName?: string`(默认 `xcx_group`) - `ConsumerName?: string`(默认 `consumer_{ticks}`) - `Count?: int`(默认 1) #### Scenario: 无新消息 - **WHEN** Stream 中无可读消息 - **THEN** 返回 `success: true` 且数据为空 ## Known Limitations - 当前 JWT 配置存在,但认证中间件与授权标注未启用;接口默认可匿名访问。 - `MessageType` 的过滤条件存在实现差异:部分接口仅在 `MessageType == 1` 时追加过滤(不覆盖 2)。 - 文件上传未实现内容类型/大小的强制限制。