756 lines
16 KiB
Markdown
756 lines
16 KiB
Markdown
# java-mock 接口文档 & 数据设计
|
||
|
||
> Base URL: `http://localhost:8080`
|
||
>
|
||
> 认证方式: JWT Bearer Token(除登录/注册外,所有接口均需在请求头附加 `Authorization: Bearer <token>`)
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
- [接口总览](#接口总览)
|
||
- [一、认证接口](#一认证接口)
|
||
- [1.1 登录](#11-登录)
|
||
- [1.2 注册](#12-注册)
|
||
- [1.3 获取当前用户信息](#13-获取当前用户信息)
|
||
- [二、AI 代理接口(转发 Python)](#二ai-代理接口转发-python)
|
||
- [2.1 获取场景列表](#21-获取场景列表)
|
||
- [2.2 开始语音对话](#22-开始语音对话)
|
||
- [2.3 停止语音对话](#23-停止语音对话)
|
||
- [2.4 写入历史上下文](#24-写入历史上下文)
|
||
- [三、对话记录接口](#三对话记录接口)
|
||
- [3.1 保存对话](#31-保存对话)
|
||
- [3.2 对话列表(分页)](#32-对话列表分页)
|
||
- [3.3 对话详情](#33-对话详情)
|
||
- [3.4 追加消息](#34-追加消息)
|
||
- [3.5 删除对话](#35-删除对话)
|
||
- [四、其他](#四其他)
|
||
- [4.1 健康检查](#41-健康检查)
|
||
- [数据结构设计](#数据结构设计)
|
||
- [User(用户表)](#user用户表)
|
||
- [Conversation(对话表)](#conversation对话表)
|
||
- [Message(消息子文档)](#message消息子文档)
|
||
- [内部转发签名协议](#内部转发签名协议)
|
||
- [通用错误响应](#通用错误响应)
|
||
- [环境变量](#环境变量)
|
||
|
||
---
|
||
|
||
## 接口总览
|
||
|
||
| 方法 | 路径 | 是否需要 Token | 说明 |
|
||
|---|---|---|---|
|
||
| GET | `/health` | 否 | 健康检查 |
|
||
| POST | `/api/auth/login` | 否 | 登录 |
|
||
| POST | `/api/auth/register` | 否 | 注册 |
|
||
| GET | `/api/auth/me` | ✅ | 获取当前用户信息 |
|
||
| POST | `/api/ai/getScenes` | ✅ | 获取场景列表(转发 Python) |
|
||
| POST | `/api/ai/proxy?Action=StartVoiceChat` | ✅ | 开始语音对话(转发 Python) |
|
||
| POST | `/api/ai/proxy?Action=StopVoiceChat` | ✅ | 停止语音对话(转发 Python) |
|
||
| POST | `/api/ai/session/history` | ✅ | 写入历史上下文(转发 Python) |
|
||
| POST | `/api/ai/conversations` | ✅ | 保存对话记录 |
|
||
| GET | `/api/ai/conversations` | ✅ | 对话列表(分页) |
|
||
| GET | `/api/ai/conversations/:id` | ✅ | 对话详情 |
|
||
| POST | `/api/ai/conversations/:id/append` | ✅ | 追加消息到对话 |
|
||
| DELETE | `/api/ai/conversations/:id` | ✅ | 删除对话 |
|
||
|
||
---
|
||
|
||
## 一、认证接口
|
||
|
||
### 1.1 登录
|
||
|
||
```
|
||
POST /api/auth/login
|
||
```
|
||
|
||
**请求体(JSON):**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| username | string | ✅ | 用户名 |
|
||
| password | string | ✅ | 密码(明文) |
|
||
|
||
**请求示例:**
|
||
|
||
```json
|
||
{
|
||
"username": "admin",
|
||
"password": "admin123"
|
||
}
|
||
```
|
||
|
||
**成功响应 200:**
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"name": "管理员",
|
||
"sex": "male",
|
||
"isDriver": false,
|
||
"deptId": 1,
|
||
"deptName": "办公室",
|
||
"roleList": ["admin", "user"]
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
|
||
| HTTP 状态码 | code | message |
|
||
|---|---|---|
|
||
| 400 | 400 | 用户名和密码不能为空 |
|
||
| 401 | 401 | 用户名或密码错误 |
|
||
|
||
---
|
||
|
||
### 1.2 注册
|
||
|
||
```
|
||
POST /api/auth/register
|
||
```
|
||
|
||
**请求体(JSON):**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| username | string | ✅ | 用户名(唯一) |
|
||
| password | string | ✅ | 密码(明文) |
|
||
| nickname | string | ❌ | 昵称,不传时默认等于 username |
|
||
|
||
**请求示例:**
|
||
|
||
```json
|
||
{
|
||
"username": "newuser",
|
||
"password": "pass123",
|
||
"nickname": "新用户"
|
||
}
|
||
```
|
||
|
||
**成功响应 200:**
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"name": "新用户",
|
||
"sex": "unknown",
|
||
"isDriver": false,
|
||
"deptId": 0,
|
||
"deptName": "",
|
||
"roleList": ["user"]
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
|
||
| HTTP 状态码 | code | message |
|
||
|---|---|---|
|
||
| 400 | 400 | 用户名和密码不能为空 |
|
||
| 409 | 409 | 用户名已存在 |
|
||
|
||
> 注册成功后新用户默认属性:`sex=unknown`、`isDriver=false`、`deptId=0`、`roleList=["user"]`
|
||
|
||
---
|
||
|
||
### 1.3 获取当前用户信息
|
||
|
||
```
|
||
GET /api/auth/me
|
||
```
|
||
|
||
**成功响应 200:**
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"name": "管理员",
|
||
"sex": "male",
|
||
"isDriver": false,
|
||
"deptId": 1,
|
||
"deptName": "办公室",
|
||
"roleList": ["admin", "user"]
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
|
||
| HTTP 状态码 | code | message |
|
||
|---|---|---|
|
||
| 401 | 401 | 未提供 Authorization Token |
|
||
| 401 | 401 | Token 无效或已过期 |
|
||
| 404 | 404 | 用户不存在 |
|
||
|
||
---
|
||
|
||
## 二、AI 代理接口(转发 Python)
|
||
|
||
> 所有 AI 代理接口均带 HMAC-SHA256 内部签名后转发至 Python 后端(`PYTHON_BACKEND_URL`),响应内容原样透传。
|
||
>
|
||
> 详细签名规则见 [内部转发签名协议](#内部转发签名协议)。
|
||
|
||
---
|
||
|
||
### 2.1 获取场景列表
|
||
|
||
```
|
||
POST /api/ai/getScenes
|
||
```
|
||
|
||
**请求体:** `{}` 或空
|
||
|
||
**成功响应(Python 原样返回):**
|
||
|
||
```json
|
||
{
|
||
"ResponseMetadata": {
|
||
"Action": "getScenes"
|
||
},
|
||
"Result": {
|
||
"scenes": [
|
||
{
|
||
"scene": {
|
||
"id": "Custom",
|
||
"botName": "BotUser001",
|
||
"isInterruptMode": true,
|
||
"isVision": false,
|
||
"isScreenMode": false,
|
||
"isAvatarScene": false,
|
||
"avatarBgUrl": null
|
||
},
|
||
"rtc": {
|
||
"AppId": "6xxxxxxx",
|
||
"RoomId": "room-abc123",
|
||
"UserId": "user-xyz",
|
||
"Token": "AQBhMGI3Zm...",
|
||
"TaskId": "task-001"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2.2 开始语音对话
|
||
|
||
```
|
||
POST /api/ai/proxy?Action=StartVoiceChat
|
||
```
|
||
|
||
**请求体(JSON):**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| SceneID | string | ✅ | 场景 ID(从 getScenes 获取) |
|
||
|
||
**请求示例:**
|
||
|
||
```json
|
||
{
|
||
"SceneID": "Custom"
|
||
}
|
||
```
|
||
|
||
**成功响应(Python 原样返回):**
|
||
|
||
```json
|
||
{
|
||
"ResponseMetadata": {
|
||
"RequestId": "2024xxxxxxxxxx",
|
||
"Action": "StartVoiceChat",
|
||
"Version": "2024-12-01",
|
||
"Service": "rtc"
|
||
},
|
||
"Result": {
|
||
"Message": "success"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2.3 停止语音对话
|
||
|
||
```
|
||
POST /api/ai/proxy?Action=StopVoiceChat
|
||
```
|
||
|
||
**请求体(JSON):**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| SceneID | string | ✅ | 场景 ID |
|
||
|
||
**请求示例:**
|
||
|
||
```json
|
||
{
|
||
"SceneID": "Custom"
|
||
}
|
||
```
|
||
|
||
**成功响应(Python 原样返回):**
|
||
|
||
```json
|
||
{
|
||
"ResponseMetadata": {
|
||
"RequestId": "2024xxxxxxxxxx",
|
||
"Action": "StopVoiceChat",
|
||
"Version": "2024-12-01",
|
||
"Service": "rtc"
|
||
},
|
||
"Result": {
|
||
"Message": "success"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2.4 写入历史上下文
|
||
|
||
> 在 StartVoiceChat 之前调用,将历史对话上下文注入 Python Session,以便 AI 延续上下文。
|
||
|
||
```
|
||
POST /api/ai/session/history
|
||
```
|
||
|
||
**请求体(JSON):**
|
||
|
||
转发到 Python `/api/session/history`,具体字段由 Python 接口定义,典型示例:
|
||
|
||
```json
|
||
{
|
||
"roomId": "room-abc123",
|
||
"history": [
|
||
{ "role": "user", "content": "你好" },
|
||
{ "role": "assistant", "content": "你好!有什么可以帮你?" }
|
||
]
|
||
}
|
||
```
|
||
|
||
**成功响应:** Python 原样返回
|
||
|
||
---
|
||
|
||
## 三、对话记录接口
|
||
|
||
### 3.1 保存对话
|
||
|
||
```
|
||
POST /api/ai/conversations
|
||
```
|
||
|
||
**请求体(JSON):**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| sceneId | string | ✅ | 场景 ID |
|
||
| roomId | string | ❌ | RTC 房间 ID |
|
||
| messages | array | ✅ | 消息数组(见下表) |
|
||
|
||
**messages 数组每条消息字段:**
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|---|---|---|
|
||
| role | string | `"user"` 或 `"assistant"`(优先使用) |
|
||
| content | string | 消息文本 |
|
||
| time | string | ISO 时间戳,不传取服务器当前时间 |
|
||
| userId | string | 兼容旧格式。与当前登录用户 ID 相同则视为 `user`,否则视为 `assistant` |
|
||
| text | string | 兼容旧格式,与 `content` 二选一 |
|
||
|
||
**请求示例:**
|
||
|
||
```json
|
||
{
|
||
"sceneId": "Custom",
|
||
"roomId": "room-abc123",
|
||
"messages": [
|
||
{ "role": "assistant", "content": "你好,我是小块", "time": "2026-04-02T10:00:00Z" },
|
||
{ "role": "user", "content": "今天出勤情况咋样", "time": "2026-04-02T10:00:05Z" },
|
||
{ "role": "assistant", "content": "今天出勤率 90%", "time": "2026-04-02T10:00:08Z" }
|
||
]
|
||
}
|
||
```
|
||
|
||
**成功响应 200:**
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"sessionId": "9864c5ef-cdee-4c35-8f6a-81a9a4f6d323"
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
|
||
| HTTP 状态码 | code | message |
|
||
|---|---|---|
|
||
| 400 | 400 | sceneId 和 messages 不能为空 |
|
||
|
||
---
|
||
|
||
### 3.2 对话列表(分页)
|
||
|
||
```
|
||
GET /api/ai/conversations?page=1&size=20
|
||
```
|
||
|
||
**Query 参数:**
|
||
|
||
| 字段 | 类型 | 默认值 | 说明 |
|
||
|---|---|---|---|
|
||
| page | number | 1 | 页码(从 1 开始) |
|
||
| size | number | 20 | 每页条数(上限 100) |
|
||
|
||
**成功响应 200:**
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"total": 5,
|
||
"page": 1,
|
||
"size": 20,
|
||
"list": [
|
||
{
|
||
"id": "9864c5ef-cdee-4c35-8f6a-81a9a4f6d323",
|
||
"sceneId": "Custom",
|
||
"roomId": "room-abc123",
|
||
"startedAt": "2026-04-02T10:00:00Z",
|
||
"endedAt": "2026-04-02T10:00:08Z",
|
||
"messageCount": 3,
|
||
"firstMessage": "今天出勤情况咋样"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
> - 列表按 `createdAt` **倒序**排列
|
||
> - `firstMessage` 取第一条 `role === "user"` 的消息内容,无用户消息时为空串
|
||
> - 仅返回**当前登录用户**的对话记录
|
||
|
||
---
|
||
|
||
### 3.3 对话详情
|
||
|
||
```
|
||
GET /api/ai/conversations/:id
|
||
```
|
||
|
||
**Path 参数:**
|
||
|
||
| 字段 | 说明 |
|
||
|---|---|
|
||
| id | 对话 UUID |
|
||
|
||
**成功响应 200:**
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"id": "9864c5ef-cdee-4c35-8f6a-81a9a4f6d323",
|
||
"userId": "user-admin-001",
|
||
"sceneId": "Custom",
|
||
"roomId": "room-abc123",
|
||
"startedAt": "2026-04-02T10:00:00Z",
|
||
"endedAt": "2026-04-02T10:00:08Z",
|
||
"createdAt": "2026-04-02T10:00:10Z",
|
||
"messages": [
|
||
{ "role": "assistant", "content": "你好,我是小块", "createdAt": "2026-04-02T10:00:00Z" },
|
||
{ "role": "user", "content": "今天出勤情况咋样", "createdAt": "2026-04-02T10:00:05Z" },
|
||
{ "role": "assistant", "content": "今天出勤率 90%", "createdAt": "2026-04-02T10:00:08Z" }
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
|
||
| HTTP 状态码 | code | message |
|
||
|---|---|---|
|
||
| 404 | 404 | 对话记录不存在 |
|
||
| 403 | 403 | 无权访问该对话 |
|
||
|
||
---
|
||
|
||
### 3.4 追加消息
|
||
|
||
> 对话保存后如需继续追加消息(例如语音会话延续),调用此接口。
|
||
|
||
```
|
||
POST /api/ai/conversations/:id/append
|
||
```
|
||
|
||
**Path 参数:**
|
||
|
||
| 字段 | 说明 |
|
||
|---|---|
|
||
| id | 对话 UUID |
|
||
|
||
**请求体(JSON):**
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| messages | array | ✅ | 追加的消息数组,格式同 3.1 |
|
||
|
||
**请求示例:**
|
||
|
||
```json
|
||
{
|
||
"messages": [
|
||
{ "role": "user", "content": "那明天呢", "time": "2026-04-02T10:01:00Z" },
|
||
{ "role": "assistant", "content": "明天是周末,无需考勤", "time": "2026-04-02T10:01:03Z" }
|
||
]
|
||
}
|
||
```
|
||
|
||
**成功响应 200:**
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": {
|
||
"sessionId": "9864c5ef-cdee-4c35-8f6a-81a9a4f6d323"
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
|
||
| HTTP 状态码 | code | message |
|
||
|---|---|---|
|
||
| 400 | 400 | messages 不能为空 |
|
||
| 404 | 404 | 对话记录不存在 |
|
||
| 403 | 403 | 无权操作该对话 |
|
||
|
||
---
|
||
|
||
### 3.5 删除对话
|
||
|
||
```
|
||
DELETE /api/ai/conversations/:id
|
||
```
|
||
|
||
**Path 参数:**
|
||
|
||
| 字段 | 说明 |
|
||
|---|---|
|
||
| id | 对话 UUID |
|
||
|
||
**成功响应 200:**
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
|
||
| HTTP 状态码 | code | message |
|
||
|---|---|---|
|
||
| 404 | 404 | 对话记录不存在 |
|
||
| 403 | 403 | 无权删除该对话 |
|
||
|
||
---
|
||
|
||
## 四、其他
|
||
|
||
### 4.1 健康检查
|
||
|
||
```
|
||
GET /health
|
||
```
|
||
|
||
**响应(无需 Token):**
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"service": "java-mock",
|
||
"timestamp": "2026-04-02T10:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 数据结构设计
|
||
|
||
### User(用户表)
|
||
|
||
> 文件存储路径:`data/users.json`(数组)
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| id | string | ✅ | 唯一 ID,格式 `user-<uuid>` 或 `user-admin-001`(内置账号) |
|
||
| username | string | ✅ | 登录用户名,全局唯一 |
|
||
| password | string | ✅ | 明文密码(Mock 环境,生产请改用 bcrypt 哈希) |
|
||
| name | string | ✅ | 显示名 / 昵称 |
|
||
| sex | string | ✅ | 性别:`"male"` / `"female"` / `"unknown"` |
|
||
| isDriver | boolean | ✅ | 是否为司机角色 |
|
||
| deptId | number | ✅ | 部门 ID,`0` 表示无部门 |
|
||
| deptName | string | ✅ | 部门名称 |
|
||
| roleList | string[] | ✅ | 角色列表,如 `["admin","user"]` / `["user"]` |
|
||
| createdAt | string | ✅ | 创建时间 ISO8601 |
|
||
|
||
**示例数据:**
|
||
|
||
```json
|
||
[
|
||
{
|
||
"id": "user-admin-001",
|
||
"username": "admin",
|
||
"password": "admin123",
|
||
"name": "管理员",
|
||
"sex": "male",
|
||
"isDriver": false,
|
||
"deptId": 1,
|
||
"deptName": "办公室",
|
||
"roleList": ["admin", "user"],
|
||
"createdAt": "2026-01-01T00:00:00.000Z"
|
||
},
|
||
{
|
||
"id": "user-001",
|
||
"username": "user1",
|
||
"password": "user123",
|
||
"name": "测试用户",
|
||
"sex": "female",
|
||
"isDriver": false,
|
||
"deptId": 2,
|
||
"deptName": "工程部",
|
||
"roleList": ["user"],
|
||
"createdAt": "2026-01-01T00:00:00.000Z"
|
||
}
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### Conversation(对话表)
|
||
|
||
> 文件存储路径:`data/conversations.json`(数组)
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| id | string | ✅ | UUID v4,全局唯一 |
|
||
| userId | string | ✅ | 所属用户 ID(关联 User.id) |
|
||
| sceneId | string | ✅ | 场景 ID,如 `"Custom"` |
|
||
| roomId | string | ✅ | RTC 房间 ID,无则为空串 |
|
||
| startedAt | string | ✅ | 对话开始时间(取第一条消息 `createdAt`) |
|
||
| endedAt | string | ✅ | 对话结束时间(取最后一条消息 `createdAt`) |
|
||
| createdAt | string | ✅ | 记录入库时间 |
|
||
| messages | Message[] | ✅ | 消息数组(见 Message 结构) |
|
||
|
||
**示例数据:**
|
||
|
||
```json
|
||
{
|
||
"id": "85ce1273-7279-44fb-b018-3e49295c89f7",
|
||
"userId": "user-admin-001",
|
||
"sceneId": "Custom",
|
||
"roomId": "4fea87af-5b23-445d-9754-d4d878a1c705",
|
||
"startedAt": "2026-04-02T05:06:57.828Z",
|
||
"endedAt": "2026-04-02T05:07:18.750Z",
|
||
"createdAt": "2026-04-02T05:07:28.729Z",
|
||
"messages": [...]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Message(消息子文档)
|
||
|
||
> 嵌套在 Conversation.messages 数组中
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| role | string | ✅ | `"user"`(用户说)或 `"assistant"`(AI 说) |
|
||
| content | string | ✅ | 消息文本内容 |
|
||
| createdAt | string | ✅ | 消息时间 ISO8601 |
|
||
|
||
**示例:**
|
||
|
||
```json
|
||
[
|
||
{ "role": "assistant", "content": "你好,我是小块,有什么需要帮忙的吗?", "createdAt": "2026-04-02T05:06:57.828Z" },
|
||
{ "role": "user", "content": "今天办公室出勤情况咋样", "createdAt": "2026-04-02T05:07:12.023Z" },
|
||
{ "role": "assistant", "content": "今天办公室一共九个人,出勤率 90%。", "createdAt": "2026-04-02T05:07:18.750Z" }
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## 内部转发签名协议
|
||
|
||
> 转发到 Python 时,java-mock 会附加以下请求头,Python 侧需验证签名合法性。
|
||
|
||
| Header | 说明 |
|
||
|---|---|
|
||
| `X-Internal-Service` | 固定值 `java-gateway` |
|
||
| `X-Internal-User-Id` | 当前登录用户 ID |
|
||
| `X-Internal-Timestamp` | 毫秒级 Unix 时间戳(字符串) |
|
||
| `X-Internal-Signature` | HMAC-SHA256 签名,见下方算法 |
|
||
| `X-User-Name` | URL 编码的用户显示名 |
|
||
| `X-User-Sex` | 性别字符串 |
|
||
| `X-User-Is-Driver` | `"true"` 或 `"false"` |
|
||
| `X-User-Dept-Id` | 部门 ID 字符串 |
|
||
| `X-User-Dept-Name` | URL 编码的部门名称 |
|
||
| `X-User-Role-List` | URL 编码的 JSON 数组字符串,如 `%5B%22admin%22%5D` |
|
||
|
||
**签名算法:**
|
||
|
||
```
|
||
message = "java-gateway:{userId}:{timestamp}"
|
||
signature = HMAC-SHA256(INTERNAL_SERVICE_SECRET, message) // hex 输出
|
||
```
|
||
|
||
---
|
||
|
||
## 通用错误响应
|
||
|
||
所有接口在出错时均返回如下结构:
|
||
|
||
```json
|
||
{
|
||
"code": <HTTP状态码>,
|
||
"message": "错误描述"
|
||
}
|
||
```
|
||
|
||
| code | 含义 |
|
||
|---|---|
|
||
| 400 | 请求参数缺失或格式错误 |
|
||
| 401 | 未登录或 Token 无效 / 过期 |
|
||
| 403 | 无权限访问该资源 |
|
||
| 404 | 资源不存在 |
|
||
| 409 | 资源冲突(如用户名重复) |
|
||
| 502 | 无法连接 Python 后端 |
|
||
| 500 | 服务器内部错误 |
|
||
|
||
---
|
||
|
||
## 环境变量
|
||
|
||
| 变量名 | 默认值 | 说明 |
|
||
|---|---|---|
|
||
| `PORT` | `8080` | 服务监听端口 |
|
||
| `JWT_SECRET` | — | JWT 签名密钥(**必填**) |
|
||
| `JWT_EXPIRES_IN` | `7d` | JWT 过期时间 |
|
||
| `PYTHON_BACKEND_URL` | `http://localhost:3001` | Python 后端地址 |
|
||
| `INTERNAL_SERVICE_SECRET` | — | 内部签名密钥(**必填**) |
|