Files
2026-10-27 22:48:36 +08:00

346 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# portal-mini-go 小程序端接口文档
> 基于 Go + gin 的微信小程序后端服务。本文档描述**小程序端(`/api/ma`)**相关接口。后台管理接口见 `admin-api.md`。
## 1. 概述
- **基础路径**:`/api/ma`
- **服务地址**:由 `configs/config.yaml` 中 `server.port` 决定(默认 `8080`),例如 `http://localhost:8080`。
- **统一响应**:所有接口均返回 `HTTP 200`,业务结果通过响应体中的 `code` 字段判断(见 §2.4)。
- **鉴权方式**:小程序端所有接口需在请求头携带 `X-App-ID`,由 `MiniAppAuth` 中间件校验该小程序是否被纳管且启用。
- **全局接口**:`GET /healthz` 为健康检查,无需鉴权,返回 `{"code":0,"msg":"ok","data":{"status":"ok"}}`。
## 2. 公共约定
### 2.1 请求头
| Header | 必填 | 说明 |
| --- | --- | --- |
| `Content-Type` | 是(POST/PUT) | 固定为 `application/json`。 |
| `X-App-ID` | 是 | 微信小程序的 AppID,标识调用方小程序身份;必须对应一个「已启用且未删除」的被纳管小程序。 |
| `Authorization` | 否(登录态接口必填) | 用户会话凭证,格式 `Bearer <token>`;也可使用 `X-Session-Token: <token>`。由 `login` 下发,用于识别当前登录用户。 |
### 2.2 统一响应结构
```json
{
"code": 0,
"msg": "ok",
"data": { }
}
```
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | int | 业务码。`0` 表示成功;非 `0` 表示失败(见 §2.4)。 |
| `msg` | string | 结果描述。`0` 时为 `ok`,失败时为人可读信息。 |
| `data` | object/null | 业务数据,成功时有内容,失败时通常省略。 |
### 2.3 鉴权中间件
| 中间件 | 校验逻辑 |
| --- | --- |
| `MiniAppAuth` | 校验请求头 `X-App-ID` 对应一个「已启用且未删除」的被纳管小程序;通过后将 `app_id` 与小程序主键 `mini_id` 注入上下文,供后续 handler/service 使用。 |
| `MiniUserAuth` | 校验用户会话 token(`Authorization: Bearer <token>` 或 `X-Session-Token`),经 `session.Store` 解析出当前用户主键 `user_id` 并注入上下文。**必须在 `MiniAppAuth` 之后使用**,依赖其注入的 `mini_id` 确保会话归属到正确的小程序。token 无效/过期返回 `40100`。 |
> 全局还挂载 `Recovery`(panic 恢复)、`Trace`(注入 trace_id)、`ErrorHandler`(统一错误转换)。错误由中间件转换,handler 内不直接散落 `c.JSON` 处理错误。
### 2.4 错误码
| 业务码 | 常量 | 含义 |
| --- | --- | --- |
| `0` | — | 成功 |
| `40000` | `ErrBadRequest` | 请求参数错误 / 非法输入 |
| `40100` | `ErrUnauthorized` | 未认证 / 登录失败 / token 无效或过期 |
| `40300` | `ErrForbidden` | 禁止访问 |
| `40400` | `ErrNotFound` | 资源不存在 / 小程序未注册(如 `X-App-ID` 无效) |
| `50000` | `ErrServer` | 服务内部错误(含微信调用、会话写入等) |
> 非 `BizError` 的未知错误一律归为 `50000`。
## 3. 接口列表
### 3.1 `POST /api/ma/login`
小程序登录:通过微信 `code2session` 换取 `openid`/`unionid`,落库用户并向客户端下发会话 token。
**请求头**
```
X-App-ID: <微信小程序 AppID>
```
**请求体**
```json
{
"code": "081abc..." // 必填,微信登录 code
}
```
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `code` | string | 是 | 小程序 `wx.login` 获取的临时登录 code。 |
**响应**
```json
{
"code": 0,
"msg": "ok",
"data": {
"token": "a1b2c3...48位hex字符串",
"user": {
"id": 1,
"openid": "oABC...",
"union_id": "oXYZ...",
"nickname": "",
"avatar": "",
"created_at": 1698211200
}
}
}
```
> `token` 为 `24` 字节随机值的 hex 编码(48 字符),对应会话保留 `7` 天。后续小程序业务接口需在请求头携带 `Authorization: Bearer <token>`(或 `X-Session-Token: <token>`),由 `MiniUserAuth` 中间件解析当前用户身份。
> `user` 为 `ProfileView`:`created_at` 为 Unix 秒级时间戳;不含 `mini_id`/`updated_at`,且返回的是当前用户自身数据(含 `openid`/`union_id`)。
**curl 示例**
```bash
curl -X POST http://localhost:8080/api/ma/login \
-H "Content-Type: application/json" \
-H "X-App-ID: wx1234567890" \
-d '{"code":"081abc..."}'
```
### 3.2 `POST /api/ma/logout`
注销当前用户会话:删除 `login` 下发的会话 token,使该 token 立即失效。
**请求头**
```
X-App-ID: <微信小程序 AppID>
Authorization: Bearer <session_token>
```
**响应**
```json
{ "code": 0, "msg": "ok", "data": null }
```
> 需在 `MiniUserAuth` 之后调用;token 缺失/无效返回 `40100`。
**curl 示例**
```bash
curl -X POST http://localhost:8080/api/ma/logout \
-H "X-App-ID: wx1234567890" \
-H "Authorization: Bearer a1b2c3..."
```
---
### 3.3 `GET /api/ma/profile`
获取当前登录用户资料(自身数据,含 `openid`/`union_id`)。
**请求头**
```
X-App-ID: <微信小程序 AppID>
Authorization: Bearer <session_token>
```
**响应**
```json
{
"code": 0,
"msg": "ok",
"data": {
"id": 1,
"openid": "oABC...",
"union_id": "oXYZ...",
"nickname": "张三",
"avatar": "https://...",
"created_at": 1698211200
}
}
```
> 返回结构为 `ProfileView`(`created_at` 为 Unix 秒级时间戳)。用户身份由 `MiniUserAuth` 从 token 解析,查询强制按 `mini_id` 过滤,杜绝跨小程序访问。
**curl 示例**
```bash
curl http://localhost:8080/api/ma/profile \
-H "X-App-ID: wx1234567890" \
-H "Authorization: Bearer a1b2c3..."
```
---
### 3.4 `PUT /api/ma/profile`
修改当前登录用户的昵称/头像。留空的字段不更新(支持部分更新)。
**请求头**
```
X-App-ID: <微信小程序 AppID>
Authorization: Bearer <session_token>
Content-Type: application/json
```
**请求体**
```json
{
"nickname": "新昵称",
"avatar": "https://new-avatar.png"
}
```
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `nickname` | string | 否 | 新昵称;为空则不更新。 |
| `avatar` | string | 否 | 新头像 URL;为空则不更新。 |
**响应**
```json
{ "code": 0, "msg": "ok", "data": null }
```
> 用户身份由 `MiniUserAuth` 解析;更新限定在 `mini_id` 范围内,目标用户不存在返回 `40400`。
**curl 示例**
```bash
curl -X PUT http://localhost:8080/api/ma/profile \
-H "Content-Type: application/json" \
-H "X-App-ID: wx1234567890" \
-H "Authorization: Bearer a1b2c3..." \
-d '{"nickname":"新昵称"}'
```
---
### 3.5 `GET /api/ma/users/:id`
获取同小程序下指定用户的公开信息(仅 `id`/`nickname`/`avatar`,剔除 `openid`/`union_id` 等敏感字段)。
**请求头**
```
X-App-ID: <微信小程序 AppID>
Authorization: Bearer <session_token>
```
**路径参数**
| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `id` | uint64 | 目标用户主键。 |
**响应**
```json
{
"code": 0,
"msg": "ok",
"data": {
"id": 2,
"nickname": "李四",
"avatar": "https://..."
}
}
```
> 返回结构为 `PublicUserView`。查询按 `mini_id` 过滤,目标用户不存在或不属于该小程序返回 `40400`。
**curl 示例**
```bash
curl http://localhost:8080/api/ma/users/2 \
-H "X-App-ID: wx1234567890" \
-H "Authorization: Bearer a1b2c3..."
```
---
### 3.6 `GET /api/ma/users`
分页列出同小程序下的用户(公开字段)。
**请求头**
```
X-App-ID: <微信小程序 AppID>
Authorization: Bearer <session_token>
```
**查询参数**
| 参数 | 类型 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| `page` | int | 否 | `1` | 页码,从 1 开始。 |
| `size` | int | 否 | `20` | 每页条数,范围 `1`~`100`,超出则按 `20` 处理。 |
**响应**
```json
{
"code": 0,
"msg": "ok",
"data": {
"list": [
{ "id": 1, "nickname": "张三", "avatar": "https://..." },
{ "id": 2, "nickname": "李四", "avatar": "https://..." }
],
"total": 100,
"page": 1,
"size": 20
}
}
```
> 返回结构为 `PublicUserView` 数组(已剔除敏感字段)。列表按 `id` 倒序,查询按 `mini_id` 过滤。
**curl 示例**
```bash
curl "http://localhost:8080/api/ma/users?page=1&size=20" \
-H "X-App-ID: wx1234567890" \
-H "Authorization: Bearer a1b2c3..."
```
---
## 4. 数据模型
### 4.1 User(小程序用户)
| 字段 | 类型 | JSON | 说明 |
| --- | --- | --- | --- |
| `ID` | uint64 | `id` | 主键,自增。 |
| `MiniID` | uint64 | `mini_id` | 所属小程序主键(关联 `mini_programs.id`)。 |
| `OpenID` | string | `openid` | 微信 openid。 |
| `UnionID` | string | `union_id` | 微信 unionid(可为空)。 |
| `Nickname` | string | `nickname` | 昵称。 |
| `Avatar` | string | `avatar` | 头像 URL。 |
| `CreatedAt` | time | `created_at` | 创建时间。 |
| `UpdatedAt` | time | `updated_at` | 更新时间。 |
> 用户由 `(mini_id, open_id)` 唯一标识。
## 5. 备注与后续扩展
- **多小程序**:新增 `app2`..`app10` 时,按 `internal/handler/miniapp/app1`、`internal/service/miniapp/app1` 的目录结构各自建包,在 `router.New` 中注册即可,互不耦合。新增小程序后须在本文档补充其接口。
- **会话存储**:默认 `MemoryStore`(单实例),多实例部署应替换为 Redis 实现(复用 `session.Store` 接口)。
- **业务接口**:`/api/ma` 已实现 `login`/`logout`、用户资料查询与修改(`/profile`)、按 ID 查询用户(`/users/:id`)与用户分页列表(`/users`)。新增业务接口时,应在路由中增加基于会话 token 的用户身份中间件(`MiniUserAuth`)后再登记,并同步更新本文档 §3。
- **文件上传**:`/api/ma/files/*`(上传 `POST /upload`、元数据 `GET /:id`、文件流 `GET /:id/raw`、删除 `DELETE /:id`)的小程序端接口见 [file-api.md](./file-api.md);后台端(`/api/admin/files/*`)同见该文档(含配置、存储与错误码)。