feat: 实现微信小程序后端接口与用户认证系统

新增微信登录/注册合一接口、资料完善接口和token刷新接口
重构用户服务层,支持自动维护用户类型和资料完整度
引入JWT认证中间件和请求验证中间件
更新文档与测试用例,支持dist构建部署
This commit is contained in:
2026-03-20 18:32:58 +08:00
parent 6d713c22ed
commit 72e974672e
89 changed files with 18233 additions and 365 deletions

View File

@@ -1,332 +1,285 @@
# API接口文档
# API 接口文档
## 接口概述
## 文档说明
本文档定义了微信小程序前端调用的API接口包括认证、用户、数据等相关接口。
本文档描述当前项目中**已经真实实现**并可直接调用的后端接口。
当前接口统一特征如下:
## 基础信息
- 基础路径(生产):`https://bai-api.blv-oa.com/api`
- 基础路径(本地):`http://localhost:3000/api`
- 响应格式JSON
- 业务响应结构统一为:`code``msg``data`
- 当前公开接口统一使用 **POST** 方法
- 微信写接口统一要求 `Content-Type: application/json`
- API基础路径: `http://localhost:3000/api`
- 响应格式: JSON
- 认证方式: JWT
---
## 认证接口
## 一、统一响应格式
### 1. 用户登录
**接口地址**: `/auth/login`
**请求方式**: POST
**请求参数**:
| 参数名 | 类型 | 必选 | 描述 |
|-------|------|------|------|
| username | string | 是 | 用户名 |
| password | string | 是 | 密码 |
**响应示例**:
### 成功响应
```json
{
"code": 200,
"message": "登录成功",
"msg": "操作成功",
"data": {}
}
```
### 失败响应
```json
{
"code": 400,
"msg": "错误信息",
"data": {}
}
```
---
## 二、系统接口
### 1. HelloWorld 测试接口
- **接口地址**`/test-helloworld`
- **请求方式**`POST`
- **请求头**:无特殊要求
- **请求体**:可为空 `{}`
#### 响应示例
```json
{
"code": 200,
"msg": "请求成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"message": "Hello, World!",
"timestamp": "2026-03-20T00:00:00.000Z",
"status": "success"
}
}
```
### 2. 健康检查接口
- **接口地址**`/health`
- **请求方式**`POST`
- **请求头**:无特殊要求
- **请求体**:可为空 `{}`
#### 响应示例
```json
{
"code": 200,
"msg": "服务运行正常",
"data": {
"status": "healthy",
"timestamp": "2026-03-20T00:00:00.000Z"
}
}
```
---
## 三、微信小程序接口
## 1. 登录/注册合一
- **接口地址**`/wechat/login`
- **请求方式**`POST`
- **请求头**
- `Content-Type: application/json`
### 请求参数
```json
{
"users_wx_code": "0a1b2c3d4e5f6g"
}
```
### 参数说明
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `users_wx_code` | string | 是 | 微信小程序登录临时凭证 code用于换取 `users_wx_openid` |
### 处理逻辑
- 使用 `users_wx_code` 向微信服务端换取 `users_wx_openid`
- 如果数据库中不存在该用户,则自动创建新账号:
- 初始化 `users_type = 游客`
- 如果数据库中已存在该用户,则直接登录
- 返回:
- `status`
- `is_info_complete`
- `token`
- 完整用户信息
### 响应示例
```json
{
"code": 200,
"msg": "登录成功",
"data": {
"status": "login_success",
"is_info_complete": true,
"token": "jwt-token",
"user": {
"id": "123",
"username": "admin",
"nickname": "管理员"
"users_id": "U202603190001",
"users_type": "注册用户",
"users_name": "张三",
"users_phone": "13800138000",
"users_phone_masked": "138****8000",
"users_picture": "https://example.com/avatar.png",
"users_wx_openid": "oAbCdEfGh123456789",
"company_id": "C10001",
"company": null,
"pb_id": "abc123xyz",
"created": "2026-03-20T00:00:00.000Z",
"updated": "2026-03-20T00:00:00.000Z"
}
}
}
```
### 2. 用户注册
---
**接口地址**: `/auth/register`
**请求方式**: POST
**请求参数**:
## 2. 完善/修改用户资料
| 参数名 | 类型 | 必选 | 描述 |
|-------|------|------|------|
| username | string | 是 | 用户名 |
| password | string | 是 | 密码 |
| nickname | string | 是 | 昵称 |
- **接口地址**`/wechat/profile`
- **请求方式**`POST`
- **请求头**
- `Content-Type: application/json`
- `users_wx_openid: 微信用户唯一标识`
- `Authorization: Bearer <token>`
**响应示例**:
### 请求参数
```json
{
"users_name": "张三",
"users_phone_code": "2b7d9f2e3c4a5b6d7e8f",
"users_picture": "https://example.com/avatar.png"
}
```
### 参数说明
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `users_name` | string | 是 | 用户姓名 |
| `users_phone_code` | string | 是 | 微信手机号获取凭证 code后端将据此换取真实手机号 |
| `users_picture` | string | 是 | 用户头像 URL |
### 处理逻辑
- 从请求头 `users_wx_openid` 读取当前用户身份
- 校验 `Authorization`
- 不再从 body 读取 `users_wx_code`
- 使用 `users_phone_code` 调微信官方接口换取真实手机号
- 将真实手机号写入数据库字段 `users_phone`
- 若用户首次从“三项资料均为空”变为“三项资料均完整”,则将 `users_type``游客` 升级为 `注册用户`
- 返回更新后的完整用户信息
### 响应示例
```json
{
"code": 200,
"message": "注册成功",
"msg": "信息更新成功",
"data": {
"id": "123",
"username": "user1",
"nickname": "新用户"
"status": "update_success",
"user": {
"users_id": "U202603190001",
"users_type": "注册用户",
"users_name": "张三",
"users_phone": "13800138000",
"users_phone_masked": "138****8000",
"users_picture": "https://example.com/avatar.png",
"users_wx_openid": "oAbCdEfGh123456789",
"company_id": "",
"company": null,
"pb_id": "abc123xyz",
"created": "2026-03-20T00:00:00.000Z",
"updated": "2026-03-20T00:10:00.000Z"
}
}
}
```
## 用户接口
---
### 1. 获取用户信息
## 3. 刷新 token
**接口地址**: `/user/info`
**请求方式**: GET
**请求头**:
- Authorization: Bearer {token}
- **接口地址**`/wechat/refresh-token`
- **请求方式**`POST`
- **请求头**
- `users_wx_openid: 微信用户唯一标识`
**响应示例**:
> 说明:本接口**不要求旧 `Authorization`**。
### 请求体
- 无 body 参数,可传 `{}` 或空体
### 处理逻辑
- 仅通过请求头中的 `users_wx_openid` 定位用户
- 若用户存在,则签发新的 JWT token
- 若用户不存在,则返回 `404`
### 响应示例
```json
{
"code": 200,
"message": "获取成功",
"msg": "刷新成功",
"data": {
"id": "123",
"username": "admin",
"nickname": "管理员",
"avatar": "https://example.com/avatar.jpg",
"createdAt": "2026-03-18T00:00:00Z"
"token": "new-jwt-token"
}
}
```
### 2. 更新用户信息
---
**接口地址**: `/user/update`
**请求方式**: PUT
**请求头**:
- Authorization: Bearer {token}
**请求参数**:
## 四、错误码说明
| 参数名 | 类型 | 必选 | 描述 |
|-------|------|------|------|
| nickname | string | 否 | 昵称 |
| avatar | string | 否 | 头像URL |
| 错误码 | 说明 |
|---|---|
| `200` | 成功 |
| `400` | 请求参数错误 |
| `401` | 请求头缺失、令牌无效或用户身份不匹配 |
| `404` | 用户不存在或路由不存在 |
| `415` | 请求体不是 `application/json` |
| `429` | 请求过于频繁 |
| `500` | 服务器内部错误 |
**响应示例**:
---
```json
{
"code": 200,
"message": "更新成功",
"data": {
"id": "123",
"username": "admin",
"nickname": "新昵称",
"avatar": "https://example.com/new-avatar.jpg"
}
}
## 五、调用建议
### 1. 所有微信写接口都使用 JSON
请求头应设置:
```http
Content-Type: application/json
```
## 数据接口
### 2. 资料接口与资料详情接口都要带标准 JWT 头
### 1. 获取数据列表
**接口地址**: `/data/list`
**请求方式**: GET
**请求参数**:
| 参数名 | 类型 | 必选 | 描述 |
|-------|------|------|------|
| page | number | 否 | 页码默认1 |
| size | number | 否 | 每页条数默认10 |
| keyword | string | 否 | 搜索关键词 |
**响应示例**:
```json
{
"code": 200,
"message": "获取成功",
"data": {
"list": [
{
"id": "1",
"title": "数据1",
"content": "内容1",
"createdAt": "2026-03-18T00:00:00Z"
}
],
"total": 1,
"page": 1,
"size": 10
}
}
```http
Authorization: Bearer <token>
```
### 2. 获取数据详情
### 3. `refresh-token` 接口当前只需要:
**接口地址**: `/data/detail/{id}`
**请求方式**: GET
**路径参数**:
- id: 数据ID
**响应示例**:
```json
{
"code": 200,
"message": "获取成功",
"data": {
"id": "1",
"title": "数据1",
"content": "内容1",
"createdAt": "2026-03-18T00:00:00Z",
"updatedAt": "2026-03-18T00:00:00Z"
}
}
```http
users_wx_openid: <openid>
```
## AI接口
### 1. 智能问答
**接口地址**: `/ai/chat`
**请求方式**: POST
**请求参数**:
| 参数名 | 类型 | 必选 | 描述 |
|-------|------|------|------|
| question | string | 是 | 问题 |
| context | string | 否 | 上下文 |
**响应示例**:
```json
{
"code": 200,
"message": "获取成功",
"data": {
"answer": "这是AI的回答",
"thinking": "AI的思考过程",
"tokens": 100
}
}
```
## 视频接口
### 1. 上传视频
**接口地址**: `/video/upload`
**请求方式**: POST
**请求头**:
- Content-Type: multipart/form-data
**请求参数**:
| 参数名 | 类型 | 必选 | 描述 |
|-------|------|------|------|
| video | file | 是 | 视频文件 |
| title | string | 是 | 视频标题 |
**响应示例**:
```json
{
"code": 200,
"message": "上传成功",
"data": {
"id": "1",
"title": "视频标题",
"url": "https://example.com/video.mp4",
"duration": 60
}
}
```
### 2. 获取视频列表
**接口地址**: `/video/list`
**请求方式**: GET
**请求参数**:
| 参数名 | 类型 | 必选 | 描述 |
|-------|------|------|------|
| page | number | 否 | 页码默认1 |
| size | number | 否 | 每页条数默认10 |
**响应示例**:
```json
{
"code": 200,
"message": "获取成功",
"data": {
"list": [
{
"id": "1",
"title": "视频1",
"url": "https://example.com/video1.mp4",
"duration": 60,
"createdAt": "2026-03-18T00:00:00Z"
}
],
"total": 1,
"page": 1,
"size": 10
}
}
```
## 错误码说明
| 错误码 | 描述 |
|-------|------|
| 400 | 请求参数错误 |
| 401 | 未授权,请登录 |
| 403 | 禁止访问 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
## 调用示例
### 使用axios调用
```javascript
// 登录
axios.post('/api/auth/login', {
username: 'admin',
password: '123456'
}).then(response => {
const token = response.data.data.token;
// 存储token
localStorage.setItem('token', token);
});
// 带认证的请求
axios.get('/api/user/info', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
```
### 使用fetch调用
```javascript
// 登录
fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: 'admin',
password: '123456'
})
}).then(response => response.json())
.then(data => {
const token = data.data.token;
// 存储token
localStorage.setItem('token', token);
});
// 带认证的请求
fetch('/api/user/info', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
}).then(response => response.json())
.then(data => console.log(data));
```
不需要旧 `Authorization`