复制项目
This commit is contained in:
84
docs/.generated_docs
Normal file
84
docs/.generated_docs
Normal file
@@ -0,0 +1,84 @@
|
||||
docs/.generated_docs
|
||||
docs/guide/en-US/cmd/iam-apiserver.md
|
||||
docs/guide/en-US/cmd/iam-authz-server.md
|
||||
docs/guide/en-US/cmd/iam-pump.md
|
||||
docs/guide/en-US/cmd/iam-watcher.md
|
||||
docs/guide/en-US/cmd/openim/openim.md
|
||||
docs/guide/en-US/cmd/openim/openim_color.md
|
||||
docs/guide/en-US/cmd/openim/openim_completion.md
|
||||
docs/guide/en-US/cmd/openim/openim_info.md
|
||||
docs/guide/en-US/cmd/openim/openim_jwt.md
|
||||
docs/guide/en-US/cmd/openim/openim_jwt_show.md
|
||||
docs/guide/en-US/cmd/openim/openim_jwt_sign.md
|
||||
docs/guide/en-US/cmd/openim/openim_jwt_verify.md
|
||||
docs/guide/en-US/cmd/openim/openim_new.md
|
||||
docs/guide/en-US/cmd/openim/openim_options.md
|
||||
docs/guide/en-US/cmd/openim/openim_policy.md
|
||||
docs/guide/en-US/cmd/openim/openim_policy_create.md
|
||||
docs/guide/en-US/cmd/openim/openim_policy_delete.md
|
||||
docs/guide/en-US/cmd/openim/openim_policy_get.md
|
||||
docs/guide/en-US/cmd/openim/openim_policy_list.md
|
||||
docs/guide/en-US/cmd/openim/openim_policy_update.md
|
||||
docs/guide/en-US/cmd/openim/openim_secret.md
|
||||
docs/guide/en-US/cmd/openim/openim_secret_create.md
|
||||
docs/guide/en-US/cmd/openim/openim_secret_delete.md
|
||||
docs/guide/en-US/cmd/openim/openim_secret_get.md
|
||||
docs/guide/en-US/cmd/openim/openim_secret_list.md
|
||||
docs/guide/en-US/cmd/openim/openim_secret_update.md
|
||||
docs/guide/en-US/cmd/openim/openim_set.md
|
||||
docs/guide/en-US/cmd/openim/openim-rpc-user.md
|
||||
docs/guide/en-US/cmd/openim/openim-rpc-user_create.md
|
||||
docs/guide/en-US/cmd/openim/openim-rpc-user_delete.md
|
||||
docs/guide/en-US/cmd/openim/openim-rpc-user_get.md
|
||||
docs/guide/en-US/cmd/openim/openim-rpc-user_list.md
|
||||
docs/guide/en-US/cmd/openim/openim-rpc-user_update.md
|
||||
docs/guide/en-US/cmd/openim/openim_validate.md
|
||||
docs/guide/en-US/cmd/openim/openim_version.md
|
||||
docs/guide/en-US/yaml/openim/openim.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_color.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_completion.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_info.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_jwt.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_new.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_options.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_policy.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_secret.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_set.yaml
|
||||
docs/guide/en-US/yaml/openim/openim-rpc-user.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_validate.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_version.yaml
|
||||
docs/man/man1/iam-apiserver.1
|
||||
docs/man/man1/iam-authz-server.1
|
||||
docs/man/man1/iam-pump.1
|
||||
docs/man/man1/iam-watcher.1
|
||||
docs/man/man1/openim-color.1
|
||||
docs/man/man1/openim-completion.1
|
||||
docs/man/man1/openim-info.1
|
||||
docs/man/man1/openim-jwt-show.1
|
||||
docs/man/man1/openim-jwt-sign.1
|
||||
docs/man/man1/openim-jwt-verify.1
|
||||
docs/man/man1/openim-jwt.1
|
||||
docs/man/man1/openim-new.1
|
||||
docs/man/man1/openim-options.1
|
||||
docs/man/man1/openim-policy-create.1
|
||||
docs/man/man1/openim-policy-delete.1
|
||||
docs/man/man1/openim-policy-get.1
|
||||
docs/man/man1/openim-policy-list.1
|
||||
docs/man/man1/openim-policy-update.1
|
||||
docs/man/man1/openim-policy.1
|
||||
docs/man/man1/openim-secret-create.1
|
||||
docs/man/man1/openim-secret-delete.1
|
||||
docs/man/man1/openim-secret-get.1
|
||||
docs/man/man1/openim-secret-list.1
|
||||
docs/man/man1/openim-secret-update.1
|
||||
docs/man/man1/openim-secret.1
|
||||
docs/man/man1/openim-set.1
|
||||
docs/man/man1/openim-user-create.1
|
||||
docs/man/man1/openim-user-delete.1
|
||||
docs/man/man1/openim-user-get.1
|
||||
docs/man/man1/openim-user-list.1
|
||||
docs/man/man1/openim-user-update.1
|
||||
docs/man/man1/openim-user.1
|
||||
docs/man/man1/openim-validate.1
|
||||
docs/man/man1/openim-version.1
|
||||
docs/man/man1/openim.1
|
||||
340
docs/API_SCHEDULED_TASK.md
Normal file
340
docs/API_SCHEDULED_TASK.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# 定时任务 API 接口文档
|
||||
|
||||
## 基础信息
|
||||
|
||||
- **基础路径**: `/scheduled_task`
|
||||
- **认证方式**: 所有接口都需要在请求头中携带 `token`(通过 `mw.CheckToken` 中间件验证)
|
||||
- **请求方法**: 所有接口均为 `POST`
|
||||
|
||||
## 公共数据结构
|
||||
|
||||
### ScheduledTaskMessage(消息内容)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": 1, // 消息类型:1-文本,2-图片,3-视频
|
||||
"content": "string", // 消息内容(文本内容、图片URL、视频URL等)
|
||||
"thumbnail": "string", // 缩略图URL(用于图片和视频,可选)
|
||||
"duration": 0, // 时长(秒,用于视频,可选)
|
||||
"fileSize": 0 // 文件大小(字节,用于图片和视频,可选)
|
||||
}
|
||||
```
|
||||
|
||||
### ScheduledTaskInfo(定时任务信息)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "string", // 任务ID
|
||||
"userID": "string", // 用户ID
|
||||
"name": "string", // 任务名称
|
||||
"cronExpression": "string", // Crontab表达式:分 时 日 月 周(例如:"0 9 * * *")
|
||||
"messages": [ // 消息列表(支持多条消息一起发送)
|
||||
{
|
||||
"type": 1,
|
||||
"content": "string",
|
||||
"thumbnail": "string",
|
||||
"duration": 0,
|
||||
"fileSize": 0
|
||||
}
|
||||
],
|
||||
"recvIDs": ["string"], // 接收者ID列表(单聊,可以多个)
|
||||
"groupIDs": ["string"], // 群组ID列表(群聊,可以多个)
|
||||
"status": 1, // 状态:0-已禁用,1-已启用
|
||||
"createTime": 1234567890123, // 创建时间(毫秒时间戳)
|
||||
"updateTime": 1234567890123 // 更新时间(毫秒时间戳)
|
||||
}
|
||||
```
|
||||
|
||||
### RequestPagination(分页信息)
|
||||
|
||||
```json
|
||||
{
|
||||
"pageNumber": 1, // 页码(从1开始)
|
||||
"showNumber": 10 // 每页显示数量
|
||||
}
|
||||
```
|
||||
|
||||
## Crontab 表达式说明
|
||||
|
||||
格式:`分 时 日 月 周`
|
||||
|
||||
- **分**: 0-59
|
||||
- **时**: 0-23
|
||||
- **日**: 1-31
|
||||
- **月**: 1-12
|
||||
- **周**: 0-7(0和7都表示周日)
|
||||
|
||||
### 示例
|
||||
|
||||
- `"0 9 * * *"` - 每天9点执行
|
||||
- `"*/5 * * * *"` - 每5分钟执行
|
||||
- `"0 0 * * 1"` - 每周一0点执行
|
||||
- `"0 12 * * 1-5"` - 周一到周五每天12点执行
|
||||
- `"0 0 1 * *"` - 每月1号0点执行
|
||||
|
||||
---
|
||||
|
||||
## 1. 创建定时任务
|
||||
|
||||
**接口地址**: `POST /scheduled_task/create`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "string", // 必填:任务名称
|
||||
"cronExpression": "string", // 必填:Crontab表达式
|
||||
"messages": [ // 必填:消息列表(至少一条)
|
||||
{
|
||||
"type": 1, // 必填:消息类型:1-文本,2-图片,3-视频
|
||||
"content": "string", // 必填:消息内容
|
||||
"thumbnail": "string", // 可选:缩略图URL
|
||||
"duration": 0, // 可选:时长(秒)
|
||||
"fileSize": 0 // 可选:文件大小(字节)
|
||||
}
|
||||
],
|
||||
"recvIDs": ["string"], // 可选:接收者ID列表(单聊,可以多个)
|
||||
"groupIDs": ["string"], // 可选:群组ID列表(群聊,可以多个)
|
||||
"status": 1 // 可选:状态:0-已禁用,1-已启用(默认为1)
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: `recvIDs` 和 `groupIDs` 至少需要提供一个,不能同时为空。
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"taskID": "string" // 创建的任务ID
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "每日问候",
|
||||
"cronExpression": "0 9 * * *",
|
||||
"messages": [
|
||||
{
|
||||
"type": 1,
|
||||
"content": "早上好!"
|
||||
},
|
||||
{
|
||||
"type": 2,
|
||||
"content": "https://example.com/image.jpg",
|
||||
"thumbnail": "https://example.com/thumb.jpg",
|
||||
"fileSize": 102400
|
||||
}
|
||||
],
|
||||
"recvIDs": ["user1", "user2"],
|
||||
"groupIDs": ["group1"],
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 获取定时任务详情
|
||||
|
||||
**接口地址**: `POST /scheduled_task/get`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"taskID": "string" // 必填:任务ID
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"task": { // 定时任务信息
|
||||
"id": "string",
|
||||
"userID": "string",
|
||||
"name": "string",
|
||||
"cronExpression": "string",
|
||||
"messages": [
|
||||
{
|
||||
"type": 1,
|
||||
"content": "string",
|
||||
"thumbnail": "string",
|
||||
"duration": 0,
|
||||
"fileSize": 0
|
||||
}
|
||||
],
|
||||
"recvIDs": ["string"],
|
||||
"groupIDs": ["string"],
|
||||
"status": 1,
|
||||
"createTime": 1234567890123,
|
||||
"updateTime": 1234567890123
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 获取定时任务列表
|
||||
|
||||
**接口地址**: `POST /scheduled_task/list`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"pagination": { // 必填:分页信息
|
||||
"pageNumber": 1, // 页码(从1开始)
|
||||
"showNumber": 10 // 每页显示数量
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"total": 100, // 总数
|
||||
"tasks": [ // 任务列表
|
||||
{
|
||||
"id": "string",
|
||||
"userID": "string",
|
||||
"name": "string",
|
||||
"cronExpression": "string",
|
||||
"messages": [
|
||||
{
|
||||
"type": 1,
|
||||
"content": "string",
|
||||
"thumbnail": "string",
|
||||
"duration": 0,
|
||||
"fileSize": 0
|
||||
}
|
||||
],
|
||||
"recvIDs": ["string"],
|
||||
"groupIDs": ["string"],
|
||||
"status": 1,
|
||||
"createTime": 1234567890123,
|
||||
"updateTime": 1234567890123
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 更新定时任务
|
||||
|
||||
**接口地址**: `POST /scheduled_task/update`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"taskID": "string", // 必填:任务ID
|
||||
"name": "string", // 可选:任务名称
|
||||
"cronExpression": "string", // 可选:Crontab表达式
|
||||
"messages": [ // 可选:消息列表
|
||||
{
|
||||
"type": 1,
|
||||
"content": "string",
|
||||
"thumbnail": "string",
|
||||
"duration": 0,
|
||||
"fileSize": 0
|
||||
}
|
||||
],
|
||||
"recvIDs": ["string"], // 可选:接收者ID列表
|
||||
"groupIDs": ["string"], // 可选:群组ID列表
|
||||
"status": 1 // 可选:状态:0-已禁用,1-已启用
|
||||
}
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- 所有字段都是可选的,只更新提供的字段
|
||||
- 如果更新 `recvIDs` 和 `groupIDs`,确保至少有一个不为空
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"taskID": "task123",
|
||||
"name": "更新后的任务名称",
|
||||
"status": 0
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 删除定时任务
|
||||
|
||||
**接口地址**: `POST /scheduled_task/delete`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"taskIDs": ["string"] // 必填:任务ID列表(支持批量删除)
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"taskIDs": ["task1", "task2", "task3"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误码说明
|
||||
|
||||
- `0`: 成功
|
||||
- 其他错误码请参考系统错误码定义
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 所有接口都需要在请求头中携带有效的 `token`
|
||||
2. 用户只能操作自己创建的定时任务
|
||||
3. `recvIDs` 和 `groupIDs` 可以同时提供,表示同时发送到多个单聊和群聊
|
||||
4. `messages` 数组支持多条消息,会按顺序发送
|
||||
5. Crontab 表达式需要符合标准格式,客户端负责解析和执行
|
||||
6. 服务端只负责存储配置,不执行定时任务
|
||||
|
||||
739
docs/API_SYSTEM_CONFIG.md
Normal file
739
docs/API_SYSTEM_CONFIG.md
Normal file
@@ -0,0 +1,739 @@
|
||||
# 系统配置管理 API 接口文档
|
||||
|
||||
## 基础信息
|
||||
|
||||
- **基础路径**: `/system_config`
|
||||
- **认证方式**: 所有接口都需要在请求头中携带 `token`(通过 `mw.CheckAdmin` 中间件验证,需要管理员权限)
|
||||
- **请求方法**: 所有接口均为 `POST`
|
||||
|
||||
## 响应格式说明
|
||||
|
||||
所有接口的响应格式统一为:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0, // 错误码,0 表示成功
|
||||
"errMsg": "", // 错误消息
|
||||
"errDlt": "", // 错误详情
|
||||
"data": {} // 响应数据
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: 为了兼容 Ant Design Pro Table,前端在 `request` 函数中需要将响应格式转换为 ProTable 期望的格式:
|
||||
|
||||
```typescript
|
||||
const request = async (params) => {
|
||||
const response = await request('/system_config/list', {
|
||||
method: 'POST',
|
||||
data: params,
|
||||
});
|
||||
|
||||
// 将 errCode 格式转换为 ProTable 期望的格式
|
||||
return {
|
||||
success: response.errCode === 0,
|
||||
data: response.data?.list || [],
|
||||
total: response.data?.total || 0,
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## 公共数据结构
|
||||
|
||||
### SystemConfigInfo(系统配置信息)
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "string", // 配置键(唯一标识)
|
||||
"title": "string", // 配置标题
|
||||
"value": "string", // 配置值(字符串形式存储,根据ValueType解析)
|
||||
"valueType": 1, // 配置值类型:1-字符串,2-数字,3-布尔,4-JSON
|
||||
"description": "string", // 配置描述
|
||||
"enabled": true, // 是否启用(用于开关类配置)
|
||||
"showInApp": false, // 是否在APP端展示(默认为false)
|
||||
"createTime": 1234567890123, // 创建时间(毫秒时间戳)
|
||||
"updateTime": 1234567890123 // 更新时间(毫秒时间戳)
|
||||
}
|
||||
```
|
||||
|
||||
### RequestPagination(分页信息)
|
||||
|
||||
```json
|
||||
{
|
||||
"pageNumber": 1, // 页码(从1开始)
|
||||
"showNumber": 10 // 每页显示数量
|
||||
}
|
||||
```
|
||||
|
||||
### StringValue(可选字符串值)
|
||||
|
||||
```json
|
||||
{
|
||||
"value": "string" // 字符串值,如果为 null 或未设置则表示不更新该字段
|
||||
}
|
||||
```
|
||||
|
||||
### Int32Value(可选整数值)
|
||||
|
||||
```json
|
||||
{
|
||||
"value": 1 // 整数值,如果为 null 或未设置则表示不更新该字段
|
||||
}
|
||||
```
|
||||
|
||||
### BoolValue(可选布尔值)
|
||||
|
||||
```json
|
||||
{
|
||||
"value": true // 布尔值,如果为 null 或未设置则表示不更新该字段
|
||||
}
|
||||
```
|
||||
|
||||
## 配置值类型说明
|
||||
|
||||
- **1 - 字符串类型 (ConfigValueTypeString)**: 普通文本字符串
|
||||
- **2 - 数字类型 (ConfigValueTypeNumber)**: 数值字符串,需要转换为数字使用
|
||||
- **3 - 布尔类型 (ConfigValueTypeBool)**: 布尔值字符串("true"/"false"),需要转换为布尔值使用
|
||||
- **4 - JSON类型 (ConfigValueTypeJSON)**: JSON 格式字符串,需要解析为 JSON 对象使用
|
||||
|
||||
## 常用配置键
|
||||
|
||||
- `wallet.enabled`: 是否开启钱包功能(布尔类型)
|
||||
- `register.phone.verify_code.enabled`: 手机号注册验证码功能是否开启(布尔类型)
|
||||
|
||||
---
|
||||
|
||||
## 1. 创建系统配置
|
||||
|
||||
**接口地址**: `POST /system_config/create`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "string", // 必填:配置键(唯一标识)
|
||||
"title": "string", // 可选:配置标题
|
||||
"value": "string|number|boolean|object", // 可选:配置值(支持多种类型,会根据 valueType 自动转换为字符串存储)
|
||||
"valueType": 1, // 可选:配置值类型:1-字符串,2-数字,3-布尔,4-JSON(默认为1)
|
||||
"description": "string", // 可选:配置描述
|
||||
"enabled": true, // 可选:是否启用(默认为true)
|
||||
"showInApp": false // 可选:是否在APP端展示(默认为false)
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: `value` 字段支持多种类型传参,系统会根据 `valueType` 自动转换并验证:
|
||||
- **字符串类型 (valueType=1)**: 可以传字符串,如 `"value": "hello"`
|
||||
- **数字类型 (valueType=2)**: 可以传数字,如 `"value": 100` 或 `"value": 3.14`,会自动转换为字符串存储
|
||||
- **布尔类型 (valueType=3)**: 可以传布尔值,如 `"value": true` 或 `"value": false`,会自动转换为 `"true"` 或 `"false"` 字符串存储
|
||||
- **JSON类型 (valueType=4)**: 可以传 JSON 对象,如 `"value": {"key": "value"}`,会自动序列化为 JSON 字符串存储
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
|
||||
**示例1: 布尔类型配置(推荐直接传布尔值)**
|
||||
```json
|
||||
{
|
||||
"key": "wallet.enabled",
|
||||
"title": "钱包功能开关",
|
||||
"value": true, // ✅ 直接传布尔值,不需要传字符串 "true"
|
||||
"valueType": 3,
|
||||
"description": "控制是否开启钱包功能",
|
||||
"enabled": true,
|
||||
"showInApp": true // 在APP端展示此配置
|
||||
}
|
||||
```
|
||||
|
||||
**示例2: 数字类型配置(推荐直接传数字)**
|
||||
```json
|
||||
{
|
||||
"key": "wallet.min_withdraw_amount",
|
||||
"title": "最小提现金额",
|
||||
"value": 100, // ✅ 直接传数字,会自动转换为字符串 "100" 存储
|
||||
"valueType": 2,
|
||||
"description": "用户提现的最小金额(单位:分)",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
**示例3: JSON类型配置(推荐直接传对象)**
|
||||
```json
|
||||
{
|
||||
"key": "wallet.withdraw_config",
|
||||
"title": "提现配置",
|
||||
"value": { // ✅ 直接传 JSON 对象,会自动序列化为字符串存储
|
||||
"dailyLimit": 10000,
|
||||
"monthlyLimit": 100000,
|
||||
"feeRate": 0.01
|
||||
},
|
||||
"valueType": 4,
|
||||
"description": "提现相关配置(JSON格式)",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
**示例4: 字符串类型配置**
|
||||
```json
|
||||
{
|
||||
"key": "wallet.welcome_message",
|
||||
"title": "欢迎消息",
|
||||
"value": "欢迎使用钱包功能", // ✅ 字符串类型直接传字符串
|
||||
"valueType": 1,
|
||||
"description": "钱包功能欢迎消息",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
**错误码**:
|
||||
- `ErrArgs`: 配置键为空
|
||||
- `ErrDuplicateKey`: 配置键已存在
|
||||
|
||||
---
|
||||
|
||||
## 2. 获取系统配置详情
|
||||
|
||||
**接口地址**: `POST /system_config/get`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "string" // 必填:配置键
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"config": { // 系统配置信息
|
||||
"key": "string",
|
||||
"title": "string",
|
||||
"value": "string",
|
||||
"valueType": 1,
|
||||
"description": "string",
|
||||
"enabled": true,
|
||||
"createTime": 1234567890123,
|
||||
"updateTime": 1234567890123
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "wallet.enabled"
|
||||
}
|
||||
```
|
||||
|
||||
**错误码**:
|
||||
- `ErrNotFound`: 配置不存在
|
||||
|
||||
---
|
||||
|
||||
## 3. 获取所有系统配置(分页)
|
||||
|
||||
**接口地址**: `POST /system_config/list`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"pagination": { // 必填:分页信息
|
||||
"pageNumber": 1, // 页码(从1开始)
|
||||
"showNumber": 10 // 每页显示数量
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"total": 100, // 总数
|
||||
"list": [ // 配置列表(Ant Design Pro 标准格式)
|
||||
{
|
||||
"key": "string",
|
||||
"title": "string",
|
||||
"value": "string|number|boolean|object", // 根据 valueType 自动转换为对应类型
|
||||
"valueType": 1,
|
||||
"description": "string",
|
||||
"enabled": true,
|
||||
"createTime": 1234567890123,
|
||||
"updateTime": 1234567890123
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: 返回的 `value` 字段会根据 `valueType` 自动转换为对应类型(详见"2. 获取系统配置详情"的说明)。
|
||||
|
||||
**请求示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"pagination": {
|
||||
"pageNumber": 1,
|
||||
"showNumber": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 更新系统配置
|
||||
|
||||
**接口地址**: `POST /system_config/update`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "string", // 必填:配置键
|
||||
"title": "string", // 可选:配置标题(如果为 null 则不更新)
|
||||
"value": "string|number|boolean|object", // 可选:配置值(支持多种类型,会根据 valueType 自动转换)
|
||||
"valueType": 1, // 可选:配置值类型(如果为 null 则不更新)
|
||||
"description": "string", // 可选:配置描述(如果为 null 则不更新)
|
||||
"enabled": true, // 可选:是否启用(如果为 null 则不更新)
|
||||
"showInApp": false // 可选:是否在APP端展示(如果为 null 则不更新)
|
||||
}
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- `value` 字段支持多种类型传参,系统会根据当前的 `valueType`(或新设置的 `valueType`)自动转换并验证
|
||||
- 如果同时更新 `value` 和 `valueType`,系统会验证新值是否符合新的类型要求
|
||||
- 如果只更新 `valueType`,系统会验证当前值是否符合新的类型要求
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
|
||||
**示例1: 更新布尔类型配置的值**
|
||||
```json
|
||||
{
|
||||
"key": "wallet.enabled",
|
||||
"value": false, // ✅ 直接传布尔值
|
||||
"description": "已关闭钱包功能"
|
||||
}
|
||||
```
|
||||
|
||||
**示例2: 更新数字类型配置的值**
|
||||
```json
|
||||
{
|
||||
"key": "wallet.min_withdraw_amount",
|
||||
"value": 200 // ✅ 直接传数字
|
||||
}
|
||||
```
|
||||
|
||||
**示例3: 同时更新值和类型**
|
||||
```json
|
||||
{
|
||||
"key": "wallet.min_amount",
|
||||
"value": 150, // ✅ 新值
|
||||
"valueType": 2 // 更新为数字类型
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: 只有提供的字段才会被更新,未提供的字段保持不变。
|
||||
|
||||
**错误码**:
|
||||
- `ErrNotFound`: 配置不存在
|
||||
|
||||
---
|
||||
|
||||
## 5. 更新系统配置值
|
||||
|
||||
**接口地址**: `POST /system_config/update_value`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "string", // 必填:配置键
|
||||
"value": "string" // 必填:新的配置值
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
|
||||
**示例1: 更新布尔类型配置**
|
||||
```json
|
||||
{
|
||||
"key": "wallet.enabled",
|
||||
"value": false // ✅ 直接传布尔值,不需要传字符串 "false"
|
||||
}
|
||||
```
|
||||
|
||||
**示例2: 更新数字类型配置**
|
||||
```json
|
||||
{
|
||||
"key": "wallet.min_withdraw_amount",
|
||||
"value": 200 // ✅ 直接传数字
|
||||
}
|
||||
```
|
||||
|
||||
**示例3: 更新 JSON 类型配置**
|
||||
```json
|
||||
{
|
||||
"key": "wallet.withdraw_config",
|
||||
"value": { // ✅ 直接传 JSON 对象
|
||||
"dailyLimit": 20000,
|
||||
"monthlyLimit": 200000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**错误码**:
|
||||
- `ErrNotFound`: 配置不存在
|
||||
|
||||
---
|
||||
|
||||
## 6. 更新系统配置启用状态
|
||||
|
||||
**接口地址**: `POST /system_config/update_enabled`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "string", // 必填:配置键
|
||||
"enabled": true // 必填:是否启用
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "wallet.enabled",
|
||||
"enabled": false
|
||||
}
|
||||
```
|
||||
|
||||
**错误码**:
|
||||
- `ErrNotFound`: 配置不存在
|
||||
|
||||
---
|
||||
|
||||
## 7. 删除系统配置
|
||||
|
||||
**接口地址**: `POST /system_config/delete`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"keys": ["string"] // 必填:配置键列表(支持批量删除)
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"keys": ["wallet.enabled", "register.phone.verify_code.enabled"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 获取所有已启用的配置
|
||||
|
||||
**接口地址**: `POST /system_config/get_enabled`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"list": [ // 已启用的配置列表(Ant Design Pro 标准格式)
|
||||
{
|
||||
"key": "string",
|
||||
"title": "string",
|
||||
"value": "string|number|boolean|object", // 根据 valueType 自动转换为对应类型
|
||||
"valueType": 1,
|
||||
"description": "string",
|
||||
"enabled": true,
|
||||
"createTime": 1234567890123,
|
||||
"updateTime": 1234567890123
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**注意**: 返回的 `value` 字段会根据 `valueType` 自动转换为对应类型(详见"2. 获取系统配置详情"的说明)。
|
||||
|
||||
**请求示例**:
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
**说明**: 此接口用于获取所有已启用(enabled=true)的配置,通常用于前端展示或业务逻辑判断。
|
||||
|
||||
---
|
||||
|
||||
## 使用场景示例
|
||||
|
||||
### 场景1: 开启/关闭钱包功能
|
||||
|
||||
1. **创建钱包功能配置**(推荐直接传布尔值):
|
||||
```json
|
||||
POST /system_config/create
|
||||
{
|
||||
"key": "wallet.enabled",
|
||||
"title": "钱包功能开关",
|
||||
"value": true, // ✅ 直接传布尔值,更直观
|
||||
"valueType": 3,
|
||||
"description": "控制是否开启钱包功能",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
2. **关闭钱包功能**(两种方式):
|
||||
|
||||
方式1: 更新 enabled 字段
|
||||
```json
|
||||
POST /system_config/update_enabled
|
||||
{
|
||||
"key": "wallet.enabled",
|
||||
"enabled": false
|
||||
}
|
||||
```
|
||||
|
||||
方式2: 更新 value 字段
|
||||
```json
|
||||
POST /system_config/update_value
|
||||
{
|
||||
"key": "wallet.enabled",
|
||||
"value": false // ✅ 直接传布尔值
|
||||
}
|
||||
```
|
||||
|
||||
3. **业务代码中检查钱包功能是否开启**:
|
||||
```go
|
||||
config, err := db.GetSystemConfig(ctx, "wallet.enabled")
|
||||
if err != nil || !config.Enabled {
|
||||
return errors.New("钱包功能未开启")
|
||||
}
|
||||
```
|
||||
|
||||
### 场景2: 配置手机号注册验证码功能
|
||||
|
||||
1. **创建验证码功能配置**(推荐直接传布尔值):
|
||||
```json
|
||||
POST /system_config/create
|
||||
{
|
||||
"key": "register.phone.verify_code.enabled",
|
||||
"title": "手机号注册验证码功能",
|
||||
"value": true, // ✅ 直接传布尔值
|
||||
"valueType": 3,
|
||||
"description": "控制手机号注册时是否需要验证码",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
### 场景3: 配置数字类型的参数(推荐直接传数字)
|
||||
|
||||
**创建配置**:
|
||||
```json
|
||||
POST /system_config/create
|
||||
{
|
||||
"key": "wallet.min_withdraw_amount",
|
||||
"title": "最小提现金额",
|
||||
"value": 100, // ✅ 直接传数字,会自动转换为字符串存储
|
||||
"valueType": 2,
|
||||
"description": "用户提现的最小金额(单位:分)",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
**获取配置**(返回时会自动转换为数字):
|
||||
```json
|
||||
GET /system_config/get
|
||||
{
|
||||
"key": "wallet.min_withdraw_amount"
|
||||
}
|
||||
|
||||
// 响应
|
||||
{
|
||||
"data": {
|
||||
"config": {
|
||||
"key": "wallet.min_withdraw_amount",
|
||||
"value": 100, // ✅ 返回数字类型,不是字符串 "100"
|
||||
"valueType": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**后端使用**(如果直接使用数据库模型,需要手动转换):
|
||||
```go
|
||||
config, _ := db.GetSystemConfig(ctx, "wallet.min_withdraw_amount")
|
||||
minAmount, _ := strconv.ParseInt(config.Value, 10, 64)
|
||||
```
|
||||
|
||||
### 场景4: 配置 JSON 类型的复杂参数(推荐直接传对象)
|
||||
|
||||
**创建配置**:
|
||||
```json
|
||||
POST /system_config/create
|
||||
{
|
||||
"key": "wallet.withdraw_config",
|
||||
"title": "提现配置",
|
||||
"value": { // ✅ 直接传 JSON 对象,会自动序列化为字符串存储
|
||||
"dailyLimit": 10000,
|
||||
"monthlyLimit": 100000,
|
||||
"feeRate": 0.01
|
||||
},
|
||||
"valueType": 4,
|
||||
"description": "提现相关配置(JSON格式)",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
**获取配置**(返回时会自动解析为对象):
|
||||
```json
|
||||
GET /system_config/get
|
||||
{
|
||||
"key": "wallet.withdraw_config"
|
||||
}
|
||||
|
||||
// 响应
|
||||
{
|
||||
"data": {
|
||||
"config": {
|
||||
"key": "wallet.withdraw_config",
|
||||
"value": { // ✅ 返回 JSON 对象,不是字符串
|
||||
"dailyLimit": 10000,
|
||||
"monthlyLimit": 100000,
|
||||
"feeRate": 0.01
|
||||
},
|
||||
"valueType": 4
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**后端使用**(如果直接使用数据库模型,需要手动解析):
|
||||
```go
|
||||
config, _ := db.GetSystemConfig(ctx, "wallet.withdraw_config")
|
||||
var withdrawConfig map[string]interface{}
|
||||
json.Unmarshal([]byte(config.Value), &withdrawConfig)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误码说明
|
||||
|
||||
- `ErrArgs`: 参数错误(如必填字段为空)
|
||||
- `ErrNotFound`: 资源不存在(如配置键不存在)
|
||||
- `ErrDuplicateKey`: 重复键(如创建配置时键已存在)
|
||||
- `ErrNoPermission`: 无权限(非管理员用户)
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **配置键唯一性**: 配置键(key)在整个系统中必须唯一,不能重复。
|
||||
|
||||
2. **配置值类型转换**:
|
||||
- **传参时**: `value` 字段支持多种类型(字符串、数字、布尔、JSON对象),系统会根据 `valueType` 自动转换为字符串存储
|
||||
- **返回时**: `value` 字段会根据 `valueType` 自动转换为对应类型返回(数字返回数字,布尔返回布尔,JSON返回对象)
|
||||
- **数据库存储**: 所有值都以字符串形式存储在数据库中
|
||||
- **后端使用**: 如果直接使用数据库模型,需要根据 `valueType` 手动转换:
|
||||
- 字符串类型:直接使用
|
||||
- 数字类型:使用 `strconv.ParseInt` 或 `strconv.ParseFloat` 转换
|
||||
- 布尔类型:使用 `strconv.ParseBool` 转换
|
||||
- JSON类型:使用 `json.Unmarshal` 解析
|
||||
|
||||
3. **类型验证**: 系统会在创建和更新时自动验证 `value` 是否符合 `valueType` 的要求:
|
||||
- 数字类型:必须是有效的数字
|
||||
- 布尔类型:必须是 `true` 或 `false`
|
||||
- JSON类型:必须是有效的 JSON
|
||||
|
||||
4. **可选字段更新**: 在 `UpdateSystemConfig` 接口中,如果字段为 `null` 或未设置,则不会更新该字段。
|
||||
|
||||
5. **启用状态**: `enabled` 字段用于控制配置是否生效。即使配置存在,如果 `enabled` 为 `false`,业务代码也应该认为该功能未开启。
|
||||
|
||||
6. **APP端展示**: `showInApp` 字段用于控制配置是否在APP端展示。只有 `showInApp` 为 `true` 的配置才会在APP端显示给用户。此字段默认为 `false`。
|
||||
|
||||
7. **推荐用法**:
|
||||
- 创建/更新布尔类型配置时,推荐直接传布尔值:`"value": true` 而不是 `"value": "true"`
|
||||
- 创建/更新数字类型配置时,推荐直接传数字:`"value": 100` 而不是 `"value": "100"`
|
||||
- 创建/更新 JSON 类型配置时,推荐直接传对象:`"value": {"key": "value"}` 而不是 `"value": "{\"key\":\"value\"}"`
|
||||
|
||||
5. **管理员权限**: 所有接口都需要管理员权限,普通用户无法访问。
|
||||
|
||||
6. **请求头**: 所有请求都需要在请求头中携带 `token` 和 `operationid`。
|
||||
453
docs/API_WALLET_SUMMARY.md
Normal file
453
docs/API_WALLET_SUMMARY.md
Normal file
@@ -0,0 +1,453 @@
|
||||
# 钱包相关接口汇总
|
||||
|
||||
## 基础信息
|
||||
|
||||
- **基础路径**: `/wallet`
|
||||
- **认证方式**: 所有接口都需要在请求头中携带 `token`(通过 `mw.CheckToken` 中间件验证)
|
||||
- **请求方法**: 所有接口均为 `POST`
|
||||
- **Content-Type**: `application/json`
|
||||
|
||||
---
|
||||
|
||||
## 接口列表
|
||||
|
||||
### 1. 获取钱包余额
|
||||
|
||||
**接口地址**: `POST /wallet/balance`
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应参数**:
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"data": {
|
||||
"balance": 10000 // 余额(单位:分)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 获取钱包详细信息
|
||||
|
||||
**接口地址**: `POST /wallet/info`
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应参数**:
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"data": {
|
||||
"balance": 10000, // 余额(单位:分)
|
||||
"withdrawAccount": "string", // 提现账号
|
||||
"withdrawAccountType": 1, // 提现账号类型:1-支付宝,2-微信,3-银行卡
|
||||
"withdrawReceiveAccount": "string", // 提现收款账号
|
||||
"hasPaymentPassword": true, // 是否已设置支付密码
|
||||
"realNameAuth": { // 实名认证信息(可选)
|
||||
"idCard": "string",
|
||||
"idCardPhotoFront": "string",
|
||||
"idCardPhotoBack": "string",
|
||||
"name": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 获取余额明细
|
||||
|
||||
**接口地址**: `POST /wallet/balance_records`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 字段名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| pagination | object | 是 | 分页参数 |
|
||||
| pagination.pageNumber | int32 | 是 | 页码(从1开始) |
|
||||
| pagination.showNumber | int32 | 是 | 每页显示数量 |
|
||||
| type | int32 | 否 | 变动类型(可选,0表示查询所有类型):1-充值,2-提现/提款,3-消费,4-退款,5-奖励,6-后台充值,7-发红包,8-抢红包,99-其他 |
|
||||
|
||||
**响应参数**:
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"data": {
|
||||
"total": 100,
|
||||
"records": [
|
||||
{
|
||||
"id": "string",
|
||||
"userID": "string",
|
||||
"amount": 10000,
|
||||
"type": 1,
|
||||
"beforeBalance": 0,
|
||||
"afterBalance": 10000,
|
||||
"orderID": "string",
|
||||
"transactionID": "string",
|
||||
"redPacketID": "string",
|
||||
"remark": "string",
|
||||
"createTime": 1234567890123
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**变动类型说明**:
|
||||
|
||||
| 类型值 | 类型名称 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 1 | 充值 | 用户充值 |
|
||||
| 2 | 提现/提款 | 提现申请 |
|
||||
| 3 | 消费 | 消费支出 |
|
||||
| 4 | 退款 | 退款收入 |
|
||||
| 5 | 奖励 | 奖励收入 |
|
||||
| 6 | 后台充值 | 管理员后台充值 |
|
||||
| 7 | 发红包 | 发红包(减少余额) |
|
||||
| 8 | 抢红包 | 抢红包(增加余额) |
|
||||
| 99 | 其他 | 其他类型 |
|
||||
|
||||
**业务逻辑**:
|
||||
|
||||
1. 从 token 中获取当前用户ID
|
||||
2. 如果指定了 type,按类型查询;否则查询所有类型
|
||||
3. 按创建时间倒序排列
|
||||
4. 支持分页查询
|
||||
|
||||
**请求示例**:
|
||||
|
||||
查询所有记录:
|
||||
```json
|
||||
{
|
||||
"pagination": {
|
||||
"pageNumber": 1,
|
||||
"showNumber": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
按类型查询(只查询充值记录):
|
||||
```json
|
||||
{
|
||||
"pagination": {
|
||||
"pageNumber": 1,
|
||||
"showNumber": 10
|
||||
},
|
||||
"type": 1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 设置支付密码
|
||||
|
||||
**接口地址**: `POST /wallet/payment_password/set`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 字段名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| newPassword | string | 是 | 新支付密码 |
|
||||
| oldPassword | string | 条件必填 | 旧支付密码(修改时必填,首次设置时不需要) |
|
||||
|
||||
**响应参数**:
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**业务逻辑**:
|
||||
|
||||
1. **首次设置支付密码**:
|
||||
- 钱包不存在或未设置支付密码
|
||||
- 不需要提供 `oldPassword`
|
||||
- 如果提供了 `oldPassword`,会返回错误
|
||||
- 如果钱包不存在,会自动创建钱包并设置支付密码
|
||||
|
||||
2. **修改支付密码**:
|
||||
- 已设置支付密码
|
||||
- 必须提供 `oldPassword`
|
||||
- 验证旧密码是否正确
|
||||
- 验证新密码不能与旧密码相同
|
||||
|
||||
**错误码说明**:
|
||||
|
||||
| 错误信息 | 说明 |
|
||||
|---------|------|
|
||||
| 新支付密码不能为空 | newPassword 为空 |
|
||||
| 修改支付密码需要提供旧密码 | 已设置密码但未提供 oldPassword |
|
||||
| 首次设置支付密码不需要提供旧密码 | 未设置密码但提供了 oldPassword |
|
||||
| 旧支付密码错误 | oldPassword 验证失败 |
|
||||
| 新密码不能与旧密码相同 | newPassword == oldPassword |
|
||||
|
||||
**请求示例**:
|
||||
|
||||
首次设置:
|
||||
```json
|
||||
{
|
||||
"newPassword": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
修改密码:
|
||||
```json
|
||||
{
|
||||
"newPassword": "654321",
|
||||
"oldPassword": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 设置提现账号
|
||||
|
||||
**接口地址**: `POST /wallet/withdraw_account/set`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 字段名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| account | string | 是 | 提现账号 |
|
||||
| accountType | int32 | 是 | 账号类型:1-支付宝,2-微信,3-银行卡 |
|
||||
|
||||
**响应参数**:
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**账号类型说明**:
|
||||
|
||||
| 类型值 | 类型名称 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 1 | 支付宝 | 支付宝账号 |
|
||||
| 2 | 微信 | 微信账号 |
|
||||
| 3 | 银行卡 | 银行卡号 |
|
||||
|
||||
**错误码说明**:
|
||||
|
||||
| 错误信息 | 说明 |
|
||||
|---------|------|
|
||||
| 提现账号不能为空 | account 为空 |
|
||||
| 账号类型无效,必须是1-支付宝,2-微信,3-银行卡 | accountType 不在有效范围内 |
|
||||
|
||||
**请求示例**:
|
||||
|
||||
设置支付宝账号:
|
||||
```json
|
||||
{
|
||||
"account": "13800138000",
|
||||
"accountType": 1
|
||||
}
|
||||
```
|
||||
|
||||
设置银行卡:
|
||||
```json
|
||||
{
|
||||
"account": "6222021234567890123",
|
||||
"accountType": 3
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 申请提现
|
||||
|
||||
**接口地址**: `POST /wallet/withdraw/apply`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 字段名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| amount | int64 | 是 | 提现金额(单位:分,必须 > 0) |
|
||||
| paymentPassword | string | 是 | 支付密码 |
|
||||
| ip | string | 否 | 申请IP |
|
||||
| deviceID | string | 否 | 设备ID |
|
||||
| platform | string | 否 | 平台(iOS、Android、Web等) |
|
||||
| deviceModel | string | 否 | 设备型号 |
|
||||
| deviceBrand | string | 否 | 设备品牌 |
|
||||
| osVersion | string | 否 | 操作系统版本 |
|
||||
| appVersion | string | 否 | 应用版本 |
|
||||
|
||||
**注意**:
|
||||
- 提现账号从用户钱包中自动获取,不需要在请求中提供
|
||||
- 如果钱包中未设置提现账号,会返回错误提示"请先在钱包中设置提现账号"
|
||||
- 备注(remark)由后台管理员在审核时填写,用户申请时不需要提供
|
||||
|
||||
**响应参数**:
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"data": {
|
||||
"applicationID": "uuid-string" // 申请ID
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**错误码说明**:
|
||||
|
||||
| 错误信息 | 说明 |
|
||||
|---------|------|
|
||||
| 提现金额必须大于0 | amount <= 0 |
|
||||
| 支付密码不能为空 | paymentPassword 为空 |
|
||||
| 钱包不存在,无法申请提现 | 用户钱包不存在 |
|
||||
| 请先设置支付密码 | 用户未设置支付密码 |
|
||||
| 支付密码错误 | 支付密码验证失败 |
|
||||
| 余额不足,无法申请提现 | 钱包余额 < 提现金额 |
|
||||
| 请先在钱包中设置提现账号 | 钱包中未设置提现账号 |
|
||||
|
||||
---
|
||||
|
||||
### 6. 获取提现申请列表
|
||||
|
||||
**接口地址**: `POST /wallet/withdraw/list`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 字段名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| pagination | object | 是 | 分页参数 |
|
||||
| pagination.pageNumber | int32 | 是 | 页码(从1开始) |
|
||||
| pagination.showNumber | int32 | 是 | 每页显示数量 |
|
||||
|
||||
**响应参数**:
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"data": {
|
||||
"total": 100,
|
||||
"applications": [
|
||||
{
|
||||
"id": "string",
|
||||
"userID": "string",
|
||||
"amount": 10000,
|
||||
"withdrawAccount": "string",
|
||||
"withdrawAccountType": 1,
|
||||
"status": 1,
|
||||
"auditorID": "string",
|
||||
"auditTime": 1234567890123,
|
||||
"auditRemark": "string",
|
||||
"ip": "string",
|
||||
"deviceID": "string",
|
||||
"platform": "string",
|
||||
"deviceModel": "string",
|
||||
"deviceBrand": "string",
|
||||
"osVersion": "string",
|
||||
"appVersion": "string",
|
||||
"remark": "string",
|
||||
"createTime": 1234567890123,
|
||||
"updateTime": 1234567890123
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**状态值说明**:
|
||||
|
||||
| 状态值 | 状态名称 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 1 | 待审核 | 用户已提交,等待管理员审核 |
|
||||
| 2 | 已通过 | 管理员审核通过 |
|
||||
| 3 | 已拒绝 | 管理员审核拒绝 |
|
||||
| 4 | 处理中 | 审核通过后,正在处理提现 |
|
||||
| 5 | 已完成 | 提现已完成 |
|
||||
|
||||
---
|
||||
|
||||
## RPC 接口对应关系
|
||||
|
||||
| HTTP 接口 | RPC 方法 | 请求消息 | 响应消息 |
|
||||
|----------|---------|---------|---------|
|
||||
| `/wallet/balance` | `GetWalletBalance` | `GetWalletBalanceReq` | `GetWalletBalanceResp` |
|
||||
| `/wallet/info` | `GetWalletInfo` | `GetWalletInfoReq` | `GetWalletInfoResp` |
|
||||
| `/wallet/balance_records` | `GetWalletBalanceRecords` | `GetWalletBalanceRecordsReq` | `GetWalletBalanceRecordsResp` |
|
||||
| `/wallet/payment_password/set` | `SetPaymentPassword` | `SetPaymentPasswordReq` | `SetPaymentPasswordResp` |
|
||||
| `/wallet/withdraw_account/set` | `SetWithdrawAccount` | `SetWithdrawAccountReq` | `SetWithdrawAccountResp` |
|
||||
| `/wallet/withdraw/apply` | `CreateWithdrawApplication` | `CreateWithdrawApplicationReq` | `CreateWithdrawApplicationResp` |
|
||||
| `/wallet/withdraw/list` | `GetWithdrawApplications` | `GetWithdrawApplicationsReq` | `GetWithdrawApplicationsResp` |
|
||||
|
||||
---
|
||||
|
||||
## 使用流程示例
|
||||
|
||||
### 1. 首次使用钱包
|
||||
|
||||
```bash
|
||||
# 1. 获取钱包信息(检查是否已设置支付密码)
|
||||
POST /wallet/info
|
||||
|
||||
# 2. 如果 hasPaymentPassword 为 false,设置支付密码
|
||||
POST /wallet/payment_password/set
|
||||
{
|
||||
"newPassword": "123456"
|
||||
}
|
||||
|
||||
# 3. 设置提现账号
|
||||
POST /wallet/withdraw_account/set
|
||||
{
|
||||
"account": "13800138000",
|
||||
"accountType": 1 // 1-支付宝,2-微信,3-银行卡
|
||||
}
|
||||
|
||||
# 4. 申请提现(提现账号从钱包中自动获取)
|
||||
POST /wallet/withdraw/apply
|
||||
{
|
||||
"amount": 10000,
|
||||
"paymentPassword": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 修改支付密码
|
||||
|
||||
```bash
|
||||
# 修改支付密码
|
||||
POST /wallet/payment_password/set
|
||||
{
|
||||
"newPassword": "654321",
|
||||
"oldPassword": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 查看提现记录
|
||||
|
||||
```bash
|
||||
# 获取提现申请列表
|
||||
POST /wallet/withdraw/list
|
||||
{
|
||||
"pagination": {
|
||||
"pageNumber": 1,
|
||||
"showNumber": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **支付密码设置**:
|
||||
- 首次设置不需要旧密码
|
||||
- 修改时必须提供正确的旧密码
|
||||
- 新密码不能与旧密码相同
|
||||
|
||||
2. **提现申请**:
|
||||
- 必须已设置支付密码
|
||||
- 必须验证支付密码
|
||||
- 余额必须足够
|
||||
- 需要设置提现账号
|
||||
|
||||
3. **权限控制**:
|
||||
- 所有接口都需要 token 认证
|
||||
- 用户只能操作自己的钱包
|
||||
- 用户只能查看自己的提现申请列表
|
||||
|
||||
4. **数据单位**:
|
||||
- 所有金额单位均为"分"(1元 = 100分)
|
||||
- 时间戳单位为毫秒
|
||||
486
docs/API_WITHDRAW_ADMIN.md
Normal file
486
docs/API_WITHDRAW_ADMIN.md
Normal file
@@ -0,0 +1,486 @@
|
||||
# 提现管理后台接口文档
|
||||
|
||||
## 接口列表
|
||||
|
||||
| 接口地址 | 请求方法 | 说明 |
|
||||
|---------|---------|------|
|
||||
| `/withdraw/get` | POST | 获取提现申请详情 |
|
||||
| `/withdraw/list` | POST | 获取提现申请列表(支持按状态筛选) |
|
||||
| `/withdraw/user_list` | POST | 获取用户的提现申请列表 |
|
||||
| `/withdraw/audit` | POST | 审核提现申请 |
|
||||
|
||||
**注意:**
|
||||
- 所有接口都需要管理员权限(需要 `mw.CheckAdmin` 中间件验证)
|
||||
- 这些接口操作的是 `withdraw_applications` 集合(用户申请的提现)
|
||||
- 用户在前端申请提现后,后台管理员通过这些接口进行审核
|
||||
|
||||
---
|
||||
|
||||
## 1. 获取提现申请详情
|
||||
|
||||
**接口地址**: `/withdraw/get`
|
||||
|
||||
**请求方法**: `POST`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"withdrawID": "string" // 提现申请ID(必填)
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"withdraw": {
|
||||
"id": "string", // 提现申请ID
|
||||
"userID": "string", // 用户ID
|
||||
"amount": 10000, // 提现金额(单位:分)
|
||||
"withdrawAccount": "string", // 提现账号
|
||||
"status": 1, // 申请状态:1-待审核,2-已通过,3-已拒绝,4-处理中,5-已完成
|
||||
"auditorID": "string", // 审核人ID(管理员ID)
|
||||
"auditTime": 1699000000000, // 审核时间(毫秒时间戳)
|
||||
"auditRemark": "string", // 审核备注
|
||||
"ip": "string", // 申请IP
|
||||
"deviceID": "string", // 设备ID
|
||||
"platform": "string", // 平台(iOS、Android、Web等)
|
||||
"deviceModel": "string", // 设备型号
|
||||
"deviceBrand": "string", // 设备品牌
|
||||
"osVersion": "string", // 操作系统版本
|
||||
"appVersion": "string", // 应用版本
|
||||
"createTime": 1699000000000, // 创建时间(毫秒时间戳)
|
||||
"updateTime": 1699000000000 // 更新时间(毫秒时间戳)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**状态说明**:
|
||||
|
||||
| 状态值 | 状态名称 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 1 | 待审核 | 用户已提交申请,等待审核 |
|
||||
| 2 | 已通过 | 审核通过,提现成功(余额已在申请时扣除) |
|
||||
| 3 | 已拒绝 | 审核拒绝,余额已退回 |
|
||||
| 4 | 处理中 | 审核通过后,正在处理中 |
|
||||
| 5 | 已完成 | 提现已完成 |
|
||||
|
||||
**错误码说明**:
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 400 | applicationID is required | 提现申请ID不能为空 |
|
||||
| 404 | 记录不存在 | 提现申请不存在 |
|
||||
| 401 | 无权限 | 需要管理员权限 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 获取提现申请列表(支持按状态筛选)
|
||||
|
||||
**接口地址**: `/withdraw/list`
|
||||
|
||||
**请求方法**: `POST`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 1, // 状态筛选(可选):0-全部,1-待审核,2-已通过,3-已拒绝,4-处理中,5-已完成。默认为0(全部)
|
||||
"pagination": {
|
||||
"pageNumber": 1, // 页码(必填,从1开始)
|
||||
"showNumber": 10 // 每页数量(必填)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"total": 100, // 总数
|
||||
"list": [
|
||||
{
|
||||
"id": "string", // 提现申请ID
|
||||
"userID": "string", // 用户ID
|
||||
"amount": 10000, // 提现金额(单位:分)
|
||||
"withdrawAccount": "string", // 提现账号
|
||||
"status": 1, // 申请状态:1-待审核,2-已通过,3-已拒绝,4-处理中,5-已完成
|
||||
"auditorID": "string", // 审核人ID(管理员ID)
|
||||
"auditTime": 1699000000000, // 审核时间(毫秒时间戳)
|
||||
"auditRemark": "string", // 审核备注
|
||||
"ip": "string", // 申请IP
|
||||
"deviceID": "string", // 设备ID
|
||||
"platform": "string", // 平台(iOS、Android、Web等)
|
||||
"deviceModel": "string", // 设备型号
|
||||
"deviceBrand": "string", // 设备品牌
|
||||
"osVersion": "string", // 操作系统版本
|
||||
"appVersion": "string", // 应用版本
|
||||
"createTime": 1699000000000, // 创建时间(毫秒时间戳)
|
||||
"updateTime": 1699000000000 // 更新时间(毫秒时间戳)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**请求示例**:
|
||||
|
||||
```json
|
||||
// 获取所有提现申请
|
||||
{
|
||||
"status": 0,
|
||||
"pagination": {
|
||||
"pageNumber": 1,
|
||||
"showNumber": 20
|
||||
}
|
||||
}
|
||||
|
||||
// 获取待审核的提现申请
|
||||
{
|
||||
"status": 1,
|
||||
"pagination": {
|
||||
"pageNumber": 1,
|
||||
"showNumber": 20
|
||||
}
|
||||
}
|
||||
|
||||
// 获取已通过的提现申请
|
||||
{
|
||||
"status": 2,
|
||||
"pagination": {
|
||||
"pageNumber": 1,
|
||||
"showNumber": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**错误码说明**:
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 400 | invalid request | 请求参数格式错误 |
|
||||
| 401 | 无权限 | 需要管理员权限 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 获取用户的提现申请列表
|
||||
|
||||
**接口地址**: `/withdraw/user_list`
|
||||
|
||||
**请求方法**: `POST`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"userID": "string", // 用户ID(必填)
|
||||
"pagination": {
|
||||
"pageNumber": 1, // 页码(必填,从1开始)
|
||||
"showNumber": 10 // 每页数量(必填)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"total": 10, // 总数
|
||||
"list": [
|
||||
{
|
||||
"id": "string", // 提现申请ID
|
||||
"userID": "string", // 用户ID
|
||||
"amount": 10000, // 提现金额(单位:分)
|
||||
"withdrawAccount": "string", // 提现账号
|
||||
"status": 1, // 申请状态:1-待审核,2-已通过,3-已拒绝,4-处理中,5-已完成
|
||||
"auditorID": "string", // 审核人ID(管理员ID)
|
||||
"auditTime": 1699000000000, // 审核时间(毫秒时间戳)
|
||||
"auditRemark": "string", // 审核备注
|
||||
"ip": "string", // 申请IP
|
||||
"deviceID": "string", // 设备ID
|
||||
"platform": "string", // 平台(iOS、Android、Web等)
|
||||
"deviceModel": "string", // 设备型号
|
||||
"deviceBrand": "string", // 设备品牌
|
||||
"osVersion": "string", // 操作系统版本
|
||||
"appVersion": "string", // 应用版本
|
||||
"createTime": 1699000000000, // 创建时间(毫秒时间戳)
|
||||
"updateTime": 1699000000000 // 更新时间(毫秒时间戳)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**错误码说明**:
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 400 | userID is required | 用户ID不能为空 |
|
||||
| 400 | invalid request | 请求参数格式错误 |
|
||||
| 401 | 无权限 | 需要管理员权限 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 批量审核提现申请
|
||||
|
||||
**接口地址**: `/withdraw/audit`
|
||||
|
||||
**请求方法**: `POST`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"withdrawIDs": ["string", "string"], // 提现申请ID列表(必填,支持批量审核)
|
||||
"status": 2, // 审核状态(必填):2-已通过,3-已拒绝
|
||||
"auditRemark": "string" // 审核备注(可选)
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"successCount": 5, // 成功审核的数量
|
||||
"failCount": 2, // 失败审核的数量
|
||||
"failedIDs": ["id1", "id2"] // 失败的提现申请ID列表
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**状态说明**:
|
||||
|
||||
| 状态值 | 状态名称 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 2 | 已通过 | 审核通过,提现成功(余额已在用户申请时扣除,无需额外操作) |
|
||||
| 3 | 已拒绝 | 审核拒绝,系统会自动将余额退回用户钱包,并创建退款记录 |
|
||||
|
||||
**业务逻辑**:
|
||||
|
||||
1. 验证必填字段(提现申请ID列表、审核状态)
|
||||
2. 验证审核状态必须是2(通过)或3(拒绝)
|
||||
3. **批量处理每个提现申请**:
|
||||
- 检查提现申请是否存在
|
||||
- 检查提现申请状态,允许"待审核"和"已通过"状态的提现申请被审核(已通过的提现申请可以重新审核为拒绝)
|
||||
- 更新提现申请状态和审核信息
|
||||
- **如果审核拒绝**:
|
||||
- 自动将提现金额退回用户钱包(包括从"待审核"改为"已拒绝",或从"已通过"改为"已拒绝")
|
||||
- 创建一条类型为"退款"的余额变动记录
|
||||
- 备注为"提现审核拒绝,退回余额"
|
||||
4. 返回成功和失败的数量,以及失败的ID列表
|
||||
|
||||
**错误码说明**:
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 400 | withdrawIDs is required and cannot be empty | 提现申请ID列表不能为空 |
|
||||
| 400 | status must be 2 (approved) or 3 (rejected) | 审核状态必须是2或3 |
|
||||
| 401 | 无权限 | 需要管理员权限 |
|
||||
|
||||
**注意**:
|
||||
- 批量审核时,部分成功部分失败是正常的
|
||||
- 失败的提现申请会在 `failedIDs` 中返回
|
||||
- 失败的常见原因:
|
||||
- 提现申请不存在
|
||||
- 提现申请状态不是"待审核"或"已通过"
|
||||
- 更新状态时出错
|
||||
|
||||
**请求示例**:
|
||||
|
||||
```json
|
||||
// 批量审核通过
|
||||
{
|
||||
"withdrawIDs": ["xxx-xxx-xxx-1", "xxx-xxx-xxx-2", "xxx-xxx-xxx-3"],
|
||||
"status": 2,
|
||||
"auditRemark": "批量审核通过,已打款"
|
||||
}
|
||||
|
||||
// 批量审核拒绝
|
||||
{
|
||||
"withdrawIDs": ["xxx-xxx-xxx-1", "xxx-xxx-xxx-2"],
|
||||
"status": 3,
|
||||
"auditRemark": "提现账号信息有误,已拒绝"
|
||||
}
|
||||
|
||||
// 单个审核(也支持)
|
||||
{
|
||||
"withdrawIDs": ["xxx-xxx-xxx"],
|
||||
"status": 2,
|
||||
"auditRemark": "审核通过,已打款"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
// 全部成功
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"successCount": 3,
|
||||
"failCount": 0,
|
||||
"failedIDs": []
|
||||
}
|
||||
}
|
||||
|
||||
// 部分成功
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"successCount": 2,
|
||||
"failCount": 1,
|
||||
"failedIDs": ["xxx-xxx-xxx-3"] // 这个ID可能不存在或状态不对
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 通用说明
|
||||
|
||||
### 分页参数说明
|
||||
|
||||
所有列表接口都使用统一的分页参数格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"pagination": {
|
||||
"pageNumber": 1, // 页码,从1开始
|
||||
"showNumber": 10 // 每页显示数量
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 时间戳说明
|
||||
|
||||
所有时间字段都是毫秒时间戳(Unix timestamp in milliseconds),例如:
|
||||
- `1699000000000` 表示 `2023-11-03 12:00:00`(UTC时间)
|
||||
|
||||
### 金额单位说明
|
||||
|
||||
所有金额字段的单位都是**分**(cent),例如:
|
||||
- `10000` 表示 `100.00` 元
|
||||
- `100` 表示 `1.00` 元
|
||||
|
||||
### 权限说明
|
||||
|
||||
所有接口都需要管理员权限,需要在请求头中携带有效的管理员 token。
|
||||
|
||||
### 响应格式说明
|
||||
|
||||
所有接口都遵循统一的响应格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0, // 错误码,0表示成功
|
||||
"errMsg": "", // 错误信息
|
||||
"errDlt": "", // 错误详情
|
||||
"data": {} // 响应数据
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 接口调用示例
|
||||
|
||||
### 示例1:获取待审核的提现申请列表
|
||||
|
||||
```bash
|
||||
curl -X POST http://your-domain/withdraw/list \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: your-admin-token" \
|
||||
-d '{
|
||||
"status": 1,
|
||||
"pagination": {
|
||||
"pageNumber": 1,
|
||||
"showNumber": 20
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### 示例2:批量审核提现申请(通过)
|
||||
|
||||
```bash
|
||||
curl -X POST http://your-domain/withdraw/audit \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: your-admin-token" \
|
||||
-d '{
|
||||
"withdrawIDs": ["withdraw-123", "withdraw-124", "withdraw-125"],
|
||||
"status": 2,
|
||||
"auditRemark": "批量审核通过,已打款"
|
||||
}'
|
||||
```
|
||||
|
||||
### 示例3:批量审核提现申请(拒绝)
|
||||
|
||||
```bash
|
||||
curl -X POST http://your-domain/withdraw/audit \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: your-admin-token" \
|
||||
-d '{
|
||||
"withdrawIDs": ["withdraw-123", "withdraw-124"],
|
||||
"status": 3,
|
||||
"auditRemark": "提现账号信息有误,已拒绝"
|
||||
}'
|
||||
```
|
||||
|
||||
### 示例4:单个审核(也支持)
|
||||
|
||||
```bash
|
||||
curl -X POST http://your-domain/withdraw/audit \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: your-admin-token" \
|
||||
-d '{
|
||||
"withdrawIDs": ["withdraw-123"],
|
||||
"status": 2,
|
||||
"auditRemark": "审核通过,已打款"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 状态流转图
|
||||
|
||||
```
|
||||
待审核 (1)
|
||||
├─ 审核通过 (2) → 已通过(余额已在用户申请时扣除,无需额外操作)
|
||||
│ ├─ 重新审核拒绝 (3) → 已拒绝(余额自动退回用户钱包)
|
||||
│ └─ 处理中 (4) → 已完成 (5)
|
||||
└─ 审核拒绝 (3) → 已拒绝(余额自动退回用户钱包)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **用户申请提现**:用户在前端申请提现时,余额会立即扣除,创建提现申请记录
|
||||
2. **后台审核**:管理员通过后台接口审核用户的提现申请,支持批量审核
|
||||
3. **批量审核**:可以一次审核多个提现申请,返回成功和失败的数量
|
||||
4. **审核通过**:余额已在用户申请时扣除,审核通过时无需额外操作
|
||||
5. **审核拒绝**:审核拒绝时会自动退回余额到用户钱包,并创建退款记录(包括从"已通过"改为"已拒绝"的情况)
|
||||
6. **状态检查**:允许"待审核"和"已通过"状态的提现申请被审核,已通过的提现申请可以重新审核为拒绝
|
||||
7. **部分失败处理**:批量审核时,部分成功部分失败是正常的,失败的ID会在响应中返回
|
||||
8. **金额单位**:所有金额都以"分"为单位,前端显示时需要除以100
|
||||
9. **数据来源**:所有接口操作的都是 `withdraw_applications` 集合(用户申请的提现),不是 `withdraws` 集合
|
||||
439
docs/API_WITHDRAW_APPLICATION.md
Normal file
439
docs/API_WITHDRAW_APPLICATION.md
Normal file
@@ -0,0 +1,439 @@
|
||||
# 提现申请接口文档
|
||||
|
||||
## 基础信息
|
||||
|
||||
- **基础路径**: `/wallet`
|
||||
- **认证方式**: 所有接口都需要在请求头中携带 `token`(通过 `mw.CheckToken` 中间件验证)
|
||||
- **请求方法**: 所有接口均为 `POST`
|
||||
- **Content-Type**: `application/json`
|
||||
|
||||
## 响应格式说明
|
||||
|
||||
所有接口的响应格式统一为:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0, // 错误码,0 表示成功
|
||||
"errMsg": "", // 错误消息
|
||||
"errDlt": "", // 错误详情
|
||||
"data": {} // 响应数据
|
||||
}
|
||||
```
|
||||
|
||||
## 公共数据结构
|
||||
|
||||
### RequestPagination(分页信息)
|
||||
|
||||
```json
|
||||
{
|
||||
"pageNumber": 1, // 页码(从1开始)
|
||||
"showNumber": 10 // 每页显示数量
|
||||
}
|
||||
```
|
||||
|
||||
### WithdrawApplicationInfo(提现申请信息)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "string", // 申请ID
|
||||
"userID": "string", // 用户ID
|
||||
"amount": 10000, // 提现金额(单位:分)
|
||||
"withdrawAccount": "string", // 提现账号
|
||||
"withdrawAccountType": 1, // 提现账号类型:1-支付宝,2-微信,3-银行卡
|
||||
"status": 1, // 申请状态:1-待审核,2-已通过,3-已拒绝,4-处理中,5-已完成
|
||||
"auditorID": "string", // 审核人ID(管理员ID)
|
||||
"auditTime": 1234567890123, // 审核时间(毫秒时间戳,未审核时为0)
|
||||
"auditRemark": "string", // 审核备注
|
||||
"ip": "string", // 申请IP
|
||||
"deviceID": "string", // 设备ID
|
||||
"platform": "string", // 平台(iOS、Android、Web等)
|
||||
"deviceModel": "string", // 设备型号
|
||||
"deviceBrand": "string", // 设备品牌
|
||||
"osVersion": "string", // 操作系统版本
|
||||
"appVersion": "string", // 应用版本
|
||||
"remark": "string", // 申请备注
|
||||
"createTime": 1234567890123, // 创建时间(毫秒时间戳)
|
||||
"updateTime": 1234567890123 // 更新时间(毫秒时间戳)
|
||||
}
|
||||
```
|
||||
|
||||
### 提现申请状态说明
|
||||
|
||||
| 状态值 | 状态名称 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 1 | 待审核 | 用户已提交申请,等待管理员审核 |
|
||||
| 2 | 已通过 | 管理员审核通过 |
|
||||
| 3 | 已拒绝 | 管理员审核拒绝 |
|
||||
| 4 | 处理中 | 审核通过后,正在处理提现 |
|
||||
| 5 | 已完成 | 提现已完成 |
|
||||
|
||||
---
|
||||
|
||||
## 接口列表
|
||||
|
||||
### 1. 设置支付密码
|
||||
|
||||
**接口地址**: `/wallet/payment_password/set`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"newPassword": "string", // 新支付密码(必填)
|
||||
"oldPassword": "string" // 旧支付密码(修改时必填,首次设置时不需要)
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**业务逻辑**:
|
||||
|
||||
1. **首次设置支付密码**(钱包不存在或未设置支付密码):
|
||||
- 不需要提供 `oldPassword`
|
||||
- 如果提供了 `oldPassword`,会返回错误
|
||||
- 如果钱包不存在,会自动创建钱包并设置支付密码
|
||||
|
||||
2. **修改支付密码**(已设置支付密码):
|
||||
- 必须提供 `oldPassword`
|
||||
- 验证旧密码是否正确
|
||||
- 验证新密码不能与旧密码相同
|
||||
- 更新支付密码
|
||||
|
||||
**错误码说明**:
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 400 | 新支付密码不能为空 | newPassword 为空 |
|
||||
| 400 | 修改支付密码需要提供旧密码 | 已设置密码但未提供 oldPassword |
|
||||
| 400 | 首次设置支付密码不需要提供旧密码 | 未设置密码但提供了 oldPassword |
|
||||
| 400 | 旧支付密码错误 | oldPassword 验证失败 |
|
||||
| 400 | 新密码不能与旧密码相同 | newPassword == oldPassword |
|
||||
|
||||
**请求示例**:
|
||||
|
||||
首次设置:
|
||||
```json
|
||||
{
|
||||
"newPassword": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
修改密码:
|
||||
```json
|
||||
{
|
||||
"newPassword": "654321",
|
||||
"oldPassword": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 设置提现账号
|
||||
|
||||
**接口地址**: `/wallet/withdraw_account/set`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"account": "string", // 提现账号(必填)
|
||||
"accountType": 1 // 账号类型(必填):1-支付宝,2-微信,3-银行卡
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**账号类型说明**:
|
||||
|
||||
| 类型值 | 类型名称 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 1 | 支付宝 | 支付宝账号 |
|
||||
| 2 | 微信 | 微信账号 |
|
||||
| 3 | 银行卡 | 银行卡号 |
|
||||
|
||||
**错误码说明**:
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 400 | 提现账号不能为空 | account 为空 |
|
||||
| 400 | 账号类型无效,必须是1-支付宝,2-微信,3-银行卡 | accountType 不在有效范围内 |
|
||||
|
||||
**业务逻辑**:
|
||||
|
||||
1. 验证必填字段(账号、账号类型)
|
||||
2. 验证账号类型是否有效(1-3)
|
||||
3. 更新钱包中的提现账号和账号类型
|
||||
|
||||
---
|
||||
|
||||
### 3. 申请提现
|
||||
|
||||
**接口地址**: `/wallet/withdraw/apply`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"amount": 10000, // 提现金额(单位:分,必填,必须大于0)
|
||||
"paymentPassword": "string", // 支付密码(必填)
|
||||
"ip": "string", // 申请IP(可选)
|
||||
"deviceID": "string", // 设备ID(可选)
|
||||
"platform": "string", // 平台(可选,如:iOS、Android、Web)
|
||||
"deviceModel": "string", // 设备型号(可选,如:iPhone 14 Pro)
|
||||
"deviceBrand": "string", // 设备品牌(可选,如:Apple)
|
||||
"osVersion": "string", // 操作系统版本(可选,如:iOS 17.0)
|
||||
"appVersion": "string" // 应用版本(可选)
|
||||
}
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- 提现账号从用户钱包中自动获取,不需要在请求中提供
|
||||
- 如果钱包中未设置提现账号,会返回错误提示
|
||||
- 备注(remark)由后台管理员在审核时填写,用户申请时不需要提供
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"applicationID": "uuid-string" // 申请ID
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**错误码说明**:
|
||||
|
||||
| 错误码 | 错误信息 | 说明 |
|
||||
|--------|---------|------|
|
||||
| 400 | 提现金额必须大于0 | amount <= 0 |
|
||||
| 400 | 支付密码不能为空 | paymentPassword 为空 |
|
||||
| 400 | 钱包不存在,无法申请提现 | 用户钱包不存在 |
|
||||
| 400 | 请先设置支付密码 | 用户未设置支付密码 |
|
||||
| 400 | 支付密码错误 | 支付密码验证失败 |
|
||||
| 400 | 余额不足,无法申请提现 | 钱包余额 < 提现金额 |
|
||||
| 400 | 请先在钱包中设置提现账号 | 钱包中未设置提现账号 |
|
||||
|
||||
**业务逻辑**:
|
||||
|
||||
1. 验证必填字段(金额、支付密码)
|
||||
2. 获取用户钱包信息
|
||||
3. 验证支付密码
|
||||
4. 检查余额是否足够
|
||||
5. 检查钱包中是否设置了提现账号(从钱包中获取)
|
||||
6. 创建提现申请记录(状态:待审核,提现账号从钱包中获取)
|
||||
|
||||
---
|
||||
|
||||
### 4. 获取提现申请列表
|
||||
|
||||
**接口地址**: `/wallet/withdraw/list`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"pagination": {
|
||||
"pageNumber": 1, // 页码(从1开始)
|
||||
"showNumber": 10 // 每页显示数量
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**响应参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"total": 100, // 总数
|
||||
"applications": [ // 申请列表
|
||||
{
|
||||
"id": "string",
|
||||
"userID": "string",
|
||||
"amount": 10000,
|
||||
"withdrawAccount": "string",
|
||||
"status": 1,
|
||||
"auditorID": "string",
|
||||
"auditTime": 1234567890123,
|
||||
"auditRemark": "string",
|
||||
"ip": "string",
|
||||
"deviceID": "string",
|
||||
"platform": "string",
|
||||
"deviceModel": "string",
|
||||
"deviceBrand": "string",
|
||||
"osVersion": "string",
|
||||
"appVersion": "string",
|
||||
"remark": "string",
|
||||
"createTime": 1234567890123,
|
||||
"updateTime": 1234567890123
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**业务逻辑**:
|
||||
|
||||
1. 从 token 中获取当前用户ID
|
||||
2. 根据用户ID查询提现申请列表
|
||||
3. 按创建时间倒序排列
|
||||
4. 支持分页查询
|
||||
|
||||
---
|
||||
|
||||
## 请求示例
|
||||
|
||||
### 申请提现
|
||||
|
||||
```bash
|
||||
curl -X POST http://your-domain/wallet/withdraw/apply \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: your-token" \
|
||||
-d '{
|
||||
"amount": 10000,
|
||||
"paymentPassword": "123456",
|
||||
"ip": "192.168.1.1",
|
||||
"deviceID": "device-uuid",
|
||||
"platform": "iOS",
|
||||
"deviceModel": "iPhone 14 Pro",
|
||||
"deviceBrand": "Apple",
|
||||
"osVersion": "iOS 17.0",
|
||||
"appVersion": "1.0.0"
|
||||
}'
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"applicationID": "550e8400-e29b-41d4-a716-446655440000"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 获取提现申请列表
|
||||
|
||||
```bash
|
||||
curl -X POST http://your-domain/wallet/withdraw/list \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: your-token" \
|
||||
-d '{
|
||||
"pagination": {
|
||||
"pageNumber": 1,
|
||||
"showNumber": 10
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"errCode": 0,
|
||||
"errMsg": "",
|
||||
"errDlt": "",
|
||||
"data": {
|
||||
"total": 5,
|
||||
"applications": [
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"userID": "user123",
|
||||
"amount": 10000,
|
||||
"withdrawAccount": "6222021234567890123",
|
||||
"status": 1,
|
||||
"auditorID": "",
|
||||
"auditTime": 0,
|
||||
"auditRemark": "",
|
||||
"ip": "192.168.1.1",
|
||||
"deviceID": "device-uuid",
|
||||
"platform": "iOS",
|
||||
"deviceModel": "iPhone 14 Pro",
|
||||
"deviceBrand": "Apple",
|
||||
"osVersion": "iOS 17.0",
|
||||
"appVersion": "1.0.0",
|
||||
"remark": "提现备注",
|
||||
"createTime": 1704067200000,
|
||||
"updateTime": 1704067200000
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RPC 接口说明
|
||||
|
||||
### gRPC 服务
|
||||
|
||||
**服务名**: `openim.chat.Chat`
|
||||
|
||||
### 1. CreateWithdrawApplication
|
||||
|
||||
**RPC 方法**: `CreateWithdrawApplication`
|
||||
|
||||
**请求消息**: `CreateWithdrawApplicationReq`
|
||||
|
||||
**响应消息**: `CreateWithdrawApplicationResp`
|
||||
|
||||
### 2. SetPaymentPassword
|
||||
|
||||
**RPC 方法**: `SetPaymentPassword`
|
||||
|
||||
**请求消息**: `SetPaymentPasswordReq`
|
||||
|
||||
**响应消息**: `SetPaymentPasswordResp`
|
||||
|
||||
### 3. GetWithdrawApplications
|
||||
|
||||
**RPC 方法**: `GetWithdrawApplications`
|
||||
|
||||
**请求消息**: `GetWithdrawApplicationsReq`
|
||||
|
||||
**响应消息**: `GetWithdrawApplicationsResp`
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **支付密码验证**: 申请提现时必须提供正确的支付密码
|
||||
2. **余额检查**: 系统会检查钱包余额是否足够,余额不足时无法申请
|
||||
3. **提现账号**: 如果钱包中已设置提现账号,可以不传 `withdrawAccount`,系统会使用钱包中的账号
|
||||
4. **申请状态**: 新创建的申请状态为"待审核"(status=1),需要管理员审核
|
||||
5. **分页查询**: 列表接口支持分页,默认按创建时间倒序排列
|
||||
6. **权限控制**: 用户只能查看自己的提现申请列表
|
||||
|
||||
---
|
||||
|
||||
## 相关接口
|
||||
|
||||
- [钱包余额查询](./API_WALLET.md#获取钱包余额)
|
||||
- [钱包信息查询](./API_WALLET.md#获取钱包详细信息)
|
||||
- [设置支付密码](#设置支付密码)
|
||||
1
docs/CODEOWNERS
Normal file
1
docs/CODEOWNERS
Normal file
@@ -0,0 +1 @@
|
||||
* @Bloomingg @FGadvancer @skiffer-git @withchao
|
||||
129
docs/contrib/cicd-actions.md
Normal file
129
docs/contrib/cicd-actions.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Continuous Integration and Automation
|
||||
|
||||
Every change on the OpenIM repository, either made through a pull request or direct push, triggers the continuous integration pipelines defined within the same repository. Needless to say, all the OpenIM contributions can be merged until all the checks pass (AKA having green builds).
|
||||
|
||||
- [Continuous Integration and Automation](#continuous-integration-and-automation)
|
||||
- [CI Platforms](#ci-platforms)
|
||||
- [GitHub Actions](#github-actions)
|
||||
- [Running locally](#running-locally)
|
||||
|
||||
## CI Platforms
|
||||
|
||||
Currently, there are two different platforms involved in running the CI processes:
|
||||
|
||||
- GitHub actions
|
||||
- Drone pipelines on CNCF infrastructure
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
All the existing GitHub Actions are defined as YAML files under the `.github/workflows` directory. These can be grouped into:
|
||||
|
||||
- **PR Checks**. These actions run all the required validations upon PR creation and update. Covering the DCO compliance check, `x86_64` test batteries (unit, integration, smoke), and code coverage.
|
||||
- **Repository automation**. Currently, it only covers issues and epic grooming.
|
||||
|
||||
Everything runs on GitHub's provided runners; thus, the tests are limited to run in `x86_64` architectures.
|
||||
|
||||
|
||||
## Running locally
|
||||
|
||||
A contributor should verify their changes locally to speed up the pull request process. Fortunately, all the CI steps can be on local environments, except for the publishing ones, through either of the following methods:
|
||||
|
||||
**User Makefile:**
|
||||
```bash
|
||||
root@PS2023EVRHNCXG:~/workspaces/openim/Open-IM-Server# make help 😊
|
||||
|
||||
Usage: make <TARGETS> <OPTIONS> ...
|
||||
|
||||
Targets:
|
||||
|
||||
all Run tidy, gen, add-copyright, format, lint, cover, build 🚀
|
||||
build Build binaries by default 🛠️
|
||||
multiarch Build binaries for multiple platforms. See option PLATFORMS. 🌍
|
||||
tidy tidy go.mod ✨
|
||||
vendor vendor go.mod 📦
|
||||
style code style -> fmt,vet,lint 💅
|
||||
fmt Run go fmt against code. ✨
|
||||
vet Run go vet against code. ✅
|
||||
lint Check syntax and styling of go sources. ✔️
|
||||
format Gofmt (reformat) package sources (exclude vendor dir if existed). 🔄
|
||||
test Run unit test. 🧪
|
||||
cover Run unit test and get test coverage. 📊
|
||||
updates Check for updates to go.mod dependencies 🆕
|
||||
imports task to automatically handle import packages in Go files using goimports tool 📥
|
||||
clean Remove all files that are created by building. 🗑️
|
||||
image Build docker images for host arch. 🐳
|
||||
image.multiarch Build docker images for multiple platforms. See option PLATFORMS. 🌍🐳
|
||||
push Build docker images for host arch and push images to registry. 📤🐳
|
||||
push.multiarch Build docker images for multiple platforms and push images to registry. 🌍📤🐳
|
||||
tools Install dependent tools. 🧰
|
||||
gen Generate all necessary files. 🧩
|
||||
swagger Generate swagger document. 📖
|
||||
serve-swagger Serve swagger spec and docs. 🚀📚
|
||||
verify-copyright Verify the license headers for all files. ✅
|
||||
add-copyright Add copyright ensure source code files have license headers. 📄
|
||||
release release the project 🎉
|
||||
help Show this help info. ℹ️
|
||||
help-all Show all help details info. ℹ️📚
|
||||
|
||||
Options:
|
||||
|
||||
DEBUG Whether or not to generate debug symbols. Default is 0. ❓
|
||||
|
||||
BINS Binaries to build. Default is all binaries under cmd. 🛠️
|
||||
This option is available when using: make {build}(.multiarch) 🧰
|
||||
Example: make build BINS="openim-api openim_cms_api".
|
||||
|
||||
PLATFORMS Platform to build for. Default is linux_arm64 and linux_amd64. 🌍
|
||||
This option is available when using: make {build}.multiarch 🌍
|
||||
Example: make multiarch PLATFORMS="linux_s390x linux_mips64
|
||||
linux_mips64le darwin_amd64 windows_amd64 linux_amd64 linux_arm64".
|
||||
|
||||
V Set to 1 enable verbose build. Default is 0. 📝
|
||||
```
|
||||
|
||||
|
||||
How to Use Makefile to Help Contributors Build Projects Quickly 😊
|
||||
|
||||
The `make help` command is a handy tool that provides useful information on how to utilize the Makefile effectively. By running this command, contributors will gain insights into various targets and options available for building projects swiftly.
|
||||
|
||||
Here's a breakdown of the targets and options provided by the Makefile:
|
||||
|
||||
**Targets 😃**
|
||||
|
||||
1. `all`: This target runs multiple tasks like `tidy`, `gen`, `add-copyright`, `format`, `lint`, `cover`, and `build`. It ensures comprehensive project building.
|
||||
2. `build`: The primary target that compiles binaries by default. It is particularly useful for creating the necessary executable files.
|
||||
3. `multiarch`: A target that builds binaries for multiple platforms. Contributors can specify the desired platforms using the `PLATFORMS` option.
|
||||
4. `tidy`: This target cleans up the `go.mod` file, ensuring its consistency.
|
||||
5. `vendor`: A target that updates the project dependencies based on the `go.mod` file.
|
||||
6. `style`: Checks the code style using tools like `fmt`, `vet`, and `lint`. It ensures a consistent coding style throughout the project.
|
||||
7. `fmt`: Formats the code using the `go fmt` command, ensuring proper indentation and formatting.
|
||||
8. `vet`: Runs the `go vet` command to identify common errors in the code.
|
||||
9. `lint`: Validates the syntax and styling of Go source files using a linter.
|
||||
10. `format`: Reformats the package sources using `gofmt`. It excludes the vendor directory if it exists.
|
||||
11. `test`: Executes unit tests to ensure the functionality and stability of the code.
|
||||
12. `cover`: Performs unit tests and calculates the test coverage of the code.
|
||||
13. `updates`: Checks for updates to the project's dependencies specified in the `go.mod` file.
|
||||
14. `imports`: Automatically handles import packages in Go files using the `goimports` tool.
|
||||
15. `clean`: Removes all files generated during the build process, effectively cleaning up the project directory.
|
||||
16. `image`: Builds Docker images for the host architecture.
|
||||
17. `image.multiarch`: Similar to the `image` target, but it builds Docker images for multiple platforms. Contributors can specify the desired platforms using the `PLATFORMS` option.
|
||||
18. `push`: Builds Docker images for the host architecture and pushes them to a registry.
|
||||
19. `push.multiarch`: Builds Docker images for multiple platforms and pushes them to a registry. Contributors can specify the desired platforms using the `PLATFORMS` option.
|
||||
20. `tools`: Installs the necessary tools or dependencies required by the project.
|
||||
21. `gen`: Generates all the required files automatically.
|
||||
22. `swagger`: Generates the swagger document for the project.
|
||||
23. `serve-swagger`: Serves the swagger specification and documentation.
|
||||
24. `verify-copyright`: Verifies the license headers for all project files.
|
||||
25. `add-copyright`: Adds copyright headers to the source code files.
|
||||
26. `release`: Releases the project, presumably for distribution.
|
||||
27. `help`: Displays information about available targets and options.
|
||||
28. `help-all`: Shows detailed information about all available targets and options.
|
||||
|
||||
**Options 😄**
|
||||
|
||||
1. `DEBUG`: A boolean option that determines whether or not to generate debug symbols. The default value is 0 (false).
|
||||
2. `BINS`: Specifies the binaries to build. By default, it builds all binaries under the `cmd` directory. Contributors can provide a list of specific binaries using this option.
|
||||
3. `PLATFORMS`: Specifies the platforms to build for. The default platforms are `linux_arm64` and `linux_amd64`. Contributors can specify multiple platforms by providing a space-separated list of platform names.
|
||||
4. `V`: A boolean option that enables verbose build output when set to 1 (true). The default value is 0 (false).
|
||||
|
||||
With these targets and options in place, contributors can efficiently build projects using the Makefile. Happy coding! 🚀😊
|
||||
38
docs/contrib/code_conventions.md
Normal file
38
docs/contrib/code_conventions.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Code conventions
|
||||
|
||||
- [Code conventions](#code-conventions)
|
||||
- [POSIX shell](#posix-shell)
|
||||
- [Go](#go)
|
||||
- [Directory and file conventions](#directory-and-file-conventions)
|
||||
- [Testing conventions](#testing-conventions)
|
||||
|
||||
## POSIX shell
|
||||
|
||||
- [Style guide](https://google.github.io/styleguide/shell.xml)
|
||||
|
||||
## Go
|
||||
|
||||
- [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
|
||||
- [Effective Go](https://golang.org/doc/effective_go.html)
|
||||
- Know and avoid [Go landmines](https://gist.github.com/lavalamp/4bd23295a9f32706a48f)
|
||||
- Comment your code.
|
||||
- [Go's commenting conventions](http://blog.golang.org/godoc-documenting-go-code)
|
||||
- If reviewers ask questions about why the code is the way it is, that's a sign that comments might be helpful.
|
||||
- Command-line flags should use dashes, not underscores
|
||||
- Naming
|
||||
- Please consider package name when selecting an interface name, and avoid redundancy. For example, `storage.Interface` is better than `storage.StorageInterface`.
|
||||
- Do not use uppercase characters, underscores, or dashes in package names.
|
||||
- Please consider parent directory name when choosing a package name. For example, `pkg/controllers/autoscaler/foo.go` should say `package autoscaler` not `package autoscalercontroller`.
|
||||
- Unless there's a good reason, the `package foo` line should match the name of the directory in which the `.go` file exists.
|
||||
- Importers can use a different name if they need to disambiguate.
|
||||
|
||||
## Directory and file conventions
|
||||
|
||||
- Avoid general utility packages. Packages called "util" are suspect. Instead, derive a name that describes your desired function. For example, the utility functions dealing with waiting for operations are in the `wait` package and include functionality like `Poll`. The full name is `wait.Poll`.
|
||||
- All filenames should be lowercase.
|
||||
- All source files and directories should use underscores, not dashes.
|
||||
- Package directories should generally avoid using separators as much as possible. When package names are multiple words, they usually should be in nested subdirectories.
|
||||
|
||||
## Testing conventions
|
||||
|
||||
Please refer to [TESTING.md](../../tests/TESTING.md) document.
|
||||
80
docs/contrib/development.md
Normal file
80
docs/contrib/development.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Development Guide
|
||||
|
||||
Since OpenIM is written in Go, it is fair to assume that the Go tools are all one needs to contribute to this project. Unfortunately, there is a point where this no longer holds true when required to test or build local changes. This document elaborates on the required tooling for OpenIM development.
|
||||
|
||||
- [Development Guide](#development-guide)
|
||||
- [Non-Linux environment prerequisites](#non-linux-environment-prerequisites)
|
||||
- [Windows Setup](#windows-setup)
|
||||
- [macOS Setup](#macos-setup)
|
||||
- [Installing Required Software](#installing-required-software)
|
||||
- [Go](#go)
|
||||
- [Docker](#docker)
|
||||
- [Vagrant](#vagrant)
|
||||
- [Cloning, Building and Testing OpenIM](#cloning-building-and-testing-openim)
|
||||
- [Dependency management](#dependency-management)
|
||||
|
||||
## Non-Linux environment prerequisites
|
||||
|
||||
All the test and build scripts within this repository were created to be run on GNU Linux development environments. Due to this, it is suggested to use the virtual machine defined on this repository's [Vagrantfile](../../Vagrantfile) to use them.
|
||||
|
||||
Either way, if one still wants to build and test OpenIM on non-Linux environments, specific setups are to be followed.
|
||||
|
||||
### Windows Setup
|
||||
|
||||
To build OpenIM on Windows is only possible for versions that support Windows Subsystem for Linux (WSL). If the development environment in question has Windows 10, Version 2004, Build 19041 or higher, [follow these instructions to install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install-win10); otherwise, use a Linux Virtual machine instead.
|
||||
|
||||
### macOS Setup
|
||||
|
||||
The shell scripts in charge of the build and test processes rely on GNU utils (i.e. `sed`), [which slightly differ on macOS](https://unix.stackexchange.com/a/79357), meaning that one must make some adjustments before using them.
|
||||
|
||||
First, install the GNU utils:
|
||||
|
||||
```sh
|
||||
brew install coreutils findutils gawk gnu-sed gnu-tar grep make
|
||||
```
|
||||
|
||||
Then update the shell init script (i.e. `.bashrc`) to prepend the GNU Utils to the `$PATH` variable
|
||||
|
||||
```sh
|
||||
GNUBINS="$(find /usr/local/opt -type d -follow -name gnubin -print)"
|
||||
|
||||
for bindir in ${GNUBINS[@]}; do
|
||||
PATH=$bindir:$PATH
|
||||
done
|
||||
|
||||
export PATH
|
||||
```
|
||||
|
||||
## Installing Required Software
|
||||
|
||||
### Go
|
||||
|
||||
It is well known that OpenIM is written in [Go](http://golang.org). Please follow the [Go Getting Started guide](https://golang.org/doc/install) to install and set up the Go tools used to compile and run the test batteries.
|
||||
|
||||
| OpenIM | requires Go |
|
||||
|----------------|-------------|
|
||||
| 2.24 - 3.00 | 1.15 + |
|
||||
| 3.30 + | 1.18 + |
|
||||
|
||||
### Docker
|
||||
|
||||
OpenIM build and test processes development require Docker to run certain steps. [Follow the Docker website instructions to install Docker](https://docs.docker.com/get-docker/) in the development environment.
|
||||
|
||||
### Vagrant
|
||||
|
||||
As described in the [Testing documentation](../../tests/TESTING.md), all the smoke tests are run in virtual machines managed by Vagrant. To install Vagrant in the development environment, [follow the instructions from the Hashicorp website](https://www.vagrantup.com/downloads), alongside any of the following hypervisors:
|
||||
|
||||
- [VirtualBox](https://www.virtualbox.org/)
|
||||
- [libvirt](https://libvirt.org/) and the [vagrant-libvirt plugin](https://github.com/vagrant-libvirt/vagrant-libvirt#installation)
|
||||
|
||||
## Cloning, Building and Testing OpenIM
|
||||
|
||||
These topics already have been addressed on their respective documents:
|
||||
|
||||
- [Git Workflow](./git-workflow.md)
|
||||
- [Building](../../BUILDING.md)
|
||||
- [Testing](../../tests/TESTING.md)
|
||||
|
||||
## Dependency management
|
||||
|
||||
OpenIM uses [go modules](https://github.com/golang/go/wiki/Modules) to manage dependencies.
|
||||
102
docs/contrib/git_workflow.md
Normal file
102
docs/contrib/git_workflow.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Git workflows
|
||||
|
||||
This document is an overview of OpenIM git workflow. It includes conventions, tips, and how to maintain good repository hygiene.
|
||||
|
||||
- [Git workflows](#git-workflows)
|
||||
- [Branching model](#branching-model)
|
||||
- [Branch naming conventions](#branch-naming-conventions)
|
||||
- [Backport policy](#backport-policy)
|
||||
- [Git operations](#git-operations)
|
||||
- [Setting up](#setting-up)
|
||||
- [Branching out](#branching-out)
|
||||
- [Keeping local branches in sync](#keeping-local-branches-in-sync)
|
||||
- [Pushing changes](#pushing-changes)
|
||||
|
||||
## Branching model
|
||||
|
||||
OpenIM project uses the [GitHub flow](https://docs.github.com/en/get-started/quickstart/github-flow) as its branching model, where most of the changes come from repositories forks instead of branches within the same one.
|
||||
|
||||
### Branch naming conventions
|
||||
|
||||
Every forked repository works independently, meaning that any contributor can create branches with the name they see fit. However, it is worth noting that OpenIM mirrors [OpenIM version skew policy](https://github.com/openimsdk/Open-IM-Server/releases) by maintaining release branches for the most recent three minor releases. The only exception is that the main branch mirrors the latest OpenIM release (3.10) instead of using a `release-` prefixed one.
|
||||
|
||||
```text
|
||||
main -------------------------------------------. (OpenIM 3.10)
|
||||
release-3.0.0 \---------------|---------------. (OpenIM 3.00)
|
||||
release-2.4.0 \---------------. (OpenIM 2.40)
|
||||
```
|
||||
|
||||
|
||||
### Backport policy
|
||||
|
||||
All new work happens on the main branch, which means that for most cases, one should branch out from there and create the pull request against it. If the change involves adding a feature or patching OpenIM, the maintainers will backport it into the supported release branches.
|
||||
|
||||
## Git operations
|
||||
|
||||
There are everyday tasks related to git that every contributor needs to perform, and this section elaborates on them.
|
||||
|
||||
### Setting up
|
||||
|
||||
Creating a OpenIM fork, cloning it, and setting its upstream remote can be summarized on:
|
||||
|
||||
1. Visit <https://github.com/openimsdk/Open-IM-Server>
|
||||
2. Click the `Fork` button (top right) to establish a cloud-based fork
|
||||
3. Clone fork to local storage
|
||||
4. Add to your fork OpenIM remote as upstream
|
||||
|
||||
Once cloned, in code it would look this way:
|
||||
|
||||
```sh
|
||||
## Clone fork to local storage
|
||||
export user="your github profile name"
|
||||
git clone https://github.com/$user/OpenIM.git
|
||||
# or: git clone git@github.com:$user/OpenIM.git
|
||||
|
||||
## Add OpenIM as upstream to your fork
|
||||
cd OpenIM
|
||||
git remote add upstream https://github.com/openimsdk/Open-IM-Server.git
|
||||
# or: git remote add upstream git@github.com:OpenIMSDK/Open-IM-Server.git
|
||||
|
||||
## Ensure to never push to upstream directly
|
||||
git remote set-url --push upstream no_push
|
||||
|
||||
## Confirm that your remotes make sense:
|
||||
git remote -v
|
||||
```
|
||||
|
||||
### Branching out
|
||||
|
||||
Every time one wants to work on a new OpenIM feature, we do:
|
||||
|
||||
1. Get local main branch up to date
|
||||
2. Create a new branch from the main one (i.e.: myfeature branch )
|
||||
|
||||
In code it would look this way:
|
||||
|
||||
```sh
|
||||
## Get local main up to date
|
||||
# Assuming the OpenIM clone is the current working directory
|
||||
git fetch upstream
|
||||
git checkout main
|
||||
git rebase upstream/main
|
||||
|
||||
## Create a new branch from main
|
||||
git checkout -b myfeature
|
||||
```
|
||||
|
||||
### Keeping local branches in sync
|
||||
|
||||
Either when branching out from main or a release one, keep in mind it is worth checking if any change has been pushed upstream by doing:
|
||||
|
||||
```sh
|
||||
git fetch upstream
|
||||
git rebase upstream/main
|
||||
```
|
||||
|
||||
It is suggested to `fetch` then `rebase` instead of `pull` since the latter does a merge, which leaves merge commits. For this, one can consider changing the local repository configuration by doing `git config branch.autoSetupRebase always` to change the behavior of `git pull`, or another non-merge option such as `git pull --rebase`.
|
||||
|
||||
### Pushing changes
|
||||
|
||||
For commit messages and signatures please refer to the [CONTRIBUTING.md](../../CONTRIBUTING.md) document.
|
||||
|
||||
Nobody should push directly to upstream, even if one has such contributor access; instead, prefer [Github's pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) mechanism to contribute back into OpenIM. For expectations and guidelines about pull requests, consult the [CONTRIBUTING.md](../../CONTRIBUTING.md) document.
|
||||
9
docs/conversions/README.md
Normal file
9
docs/conversions/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## OpenIM Project Development Standards
|
||||
|
||||
- [Code Standards](https://chat.openai.com/go_code.md)
|
||||
- [Directory Standards](https://chat.openai.com/directory.md)
|
||||
- [Commit Standards](https://chat.openai.com/commit.md)
|
||||
- [Versioning Standards](https://chat.openai.com/version.md)
|
||||
- [Interface Standards](https://chat.openai.com/api.md)
|
||||
- [Log Standards](https://chat.openai.com/log.md)
|
||||
- [Error Code Standards](https://chat.openai.com/error_code.md)
|
||||
5
docs/conversions/api.md
Normal file
5
docs/conversions/api.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## Interface Standards
|
||||
|
||||
Our project, OpenIM, adheres to the [OpenAPI 3.0](https://spec.openapis.org/oas/latest.html) interface standards.
|
||||
|
||||
> Chinese translation: [OpenAPI Specification Chinese Translation](https://fishead.gitbook.io/openapi-specification-zhcn-translation/3.0.0.zhcn)
|
||||
9
docs/conversions/commit.md
Normal file
9
docs/conversions/commit.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## Commit Standards
|
||||
|
||||
Our project, OpenIM, follows the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) standards.
|
||||
|
||||
> Chinese translation: [Conventional Commits: A Specification Making Commit Logs More Human and Machine-friendly](https://tool.lu/en_US/article/2ac/preview)
|
||||
|
||||
In addition to adhering to these standards, we encourage all contributors to the OpenIM project to ensure that their commit messages are clear and descriptive. This helps in maintaining a clean and meaningful project history. Each commit message should succinctly describe the changes made and, where necessary, the reasoning behind those changes.
|
||||
|
||||
To facilitate a streamlined process, we also recommend using appropriate commit type based on Conventional Commits guidelines such as `fix:` for bug fixes, `feat:` for new features, and so forth. Understanding and using these conventions helps in generating automatic release notes, making versioning easier, and improving overall readability of commit history.
|
||||
3
docs/conversions/directory.md
Normal file
3
docs/conversions/directory.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## Catalog Service Interface Specification
|
||||
|
||||
+ [https://github.com/kubecub/go-project-layout](https://github.com/kubecub/go-project-layout)
|
||||
22
docs/conversions/error_code.md
Normal file
22
docs/conversions/error_code.md
Normal file
@@ -0,0 +1,22 @@
|
||||
## Error Code Standards
|
||||
|
||||
Error codes are one of the important means for users to locate and solve problems. When an application encounters an exception, users can quickly locate and resolve the problem based on the error code and the description and solution of the error code in the documentation.
|
||||
|
||||
### Error Code Naming Standards
|
||||
|
||||
- Follow CamelCase notation;
|
||||
- Error codes are divided into two levels. For example, `InvalidParameter.BindError`, separated by a `.`. The first-level error code is platform-level, and the second-level error code is resource-level, which can be customized according to the scenario;
|
||||
- The second-level error code can only use English letters or numbers ([a-zA-Z0-9]), and should use standard English word spelling, standard abbreviations, RFC term abbreviations, etc.;
|
||||
- The error code should avoid multiple definitions of the same semantics, for example: `InvalidParameter.ErrorBind`, `InvalidParameter.BindError`.
|
||||
|
||||
### First-Level Common Error Codes
|
||||
|
||||
| Error Code | Error Description | Error Type |
|
||||
| ---------------- | ------------------------------------------------------------ | ---------- |
|
||||
| InternalError | Internal error | 1 |
|
||||
| InvalidParameter | Parameter error (including errors in parameter type, format, value, etc.) | 0 |
|
||||
| AuthFailure | Authentication / Authorization error | 0 |
|
||||
| ResourceNotFound | Resource does not exist | 0 |
|
||||
| FailedOperation | Operation failed | 2 |
|
||||
|
||||
> Error Type: 0 represents the client, 1 represents the server, 2 represents both the client / server.
|
||||
901
docs/conversions/go_code.md
Normal file
901
docs/conversions/go_code.md
Normal file
@@ -0,0 +1,901 @@
|
||||
## Go 代码开发规范
|
||||
在Go 项目开发中,一个好的编码规范可以极大的提高代码质量。为了帮你节省时间和精力,这里我整理了一份清晰、可直接套用的 Go 编码规范,供你参考。
|
||||
|
||||
这份规范,是我参考了 Go 官方提供的编码规范,以及 Go 社区沉淀的一些比较合理的规范之后,加入自己的理解总结出的,它比很多公司内部的规范更全面,你掌握了,以后在面试大厂的时候,或者在大厂里写代码的时候,都会让人高看你一眼,觉得你code很专业。
|
||||
|
||||
这份编码规范中包含代码风格、命名规范、注释规范、类型、控制结构、函数、GOPATH 设置规范、依赖管理和最佳实践九类规范。如果你觉得这些规范内容太多了,看完一遍也记不住,这完全没关系。你可以多看几遍,也可以在用到时把它翻出来,在实际应用中掌握。这篇特别放送的内容,更多是作为写代码时候的一个参考手册。
|
||||
|
||||
## 1. 代码风格
|
||||
|
||||
### 1.1 代码格式
|
||||
|
||||
- 代码都必须用 `gofmt` 进行格式化。
|
||||
- 运算符和操作数之间要留空格。
|
||||
- 建议一行代码不超过120个字符,超过部分,请采用合适的换行方式换行。但也有些例外场景,例如import行、工具自动生成的代码、带tag的struct字段。
|
||||
- 文件长度不能超过800行。
|
||||
- 函数长度不能超过80行。
|
||||
- import规范
|
||||
- 代码都必须用`goimports`进行格式化(建议将代码Go代码编辑器设置为:保存时运行 `goimports`)。
|
||||
- 不要使用相对路径引入包,例如 `import ../util/net` 。
|
||||
- 包名称与导入路径的最后一个目录名不匹配时,或者多个相同包名冲突时,则必须使用导入别名。
|
||||
|
||||
```go
|
||||
// bad
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
|
||||
//good
|
||||
jwt "github.com/dgrijalva/jwt-go/v4"
|
||||
```
|
||||
- 导入的包建议进行分组,匿名包的引用使用一个新的分组,并对匿名包引用进行说明。
|
||||
|
||||
```go
|
||||
import (
|
||||
// go 标准包
|
||||
"fmt"
|
||||
|
||||
// 第三方包
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
// 匿名包单独分组,并对匿名包引用进行说明
|
||||
// import mysql driver
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
|
||||
// 内部包
|
||||
v1 "github.com/marmotedu/api/apiserver/v1"
|
||||
metav1 "github.com/marmotedu/apimachinery/pkg/meta/v1"
|
||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||
)
|
||||
```
|
||||
|
||||
### 1.2 声明、初始化和定义
|
||||
|
||||
当函数中需要使用到多个变量时,可以在函数开始处使用`var`声明。在函数外部声明必须使用 `var` ,不要采用 `:=` ,容易踩到变量的作用域的问题。
|
||||
|
||||
```go
|
||||
var (
|
||||
Width int
|
||||
Height int
|
||||
)
|
||||
```
|
||||
|
||||
- 在初始化结构引用时,请使用`&T{}`代替`new(T)`,以使其与结构体初始化一致。
|
||||
|
||||
```go
|
||||
// bad
|
||||
sptr := new(T)
|
||||
sptr.Name = "bar"
|
||||
|
||||
// good
|
||||
sptr := &T{Name: "bar"}
|
||||
```
|
||||
|
||||
- struct 声明和初始化格式采用多行,定义如下。
|
||||
|
||||
```go
|
||||
type User struct{
|
||||
Username string
|
||||
Email string
|
||||
}
|
||||
|
||||
user := User{
|
||||
Username: "belm",
|
||||
Email: "nosbelm@qq.com",
|
||||
}
|
||||
```
|
||||
|
||||
- 相似的声明放在一组,同样适用于常量、变量和类型声明。
|
||||
|
||||
```go
|
||||
// bad
|
||||
import "a"
|
||||
import "b"
|
||||
|
||||
// good
|
||||
import (
|
||||
"a"
|
||||
"b"
|
||||
)
|
||||
```
|
||||
|
||||
- 尽可能指定容器容量,以便为容器预先分配内存,例如:
|
||||
|
||||
```go
|
||||
v := make(map[int]string, 4)
|
||||
v := make([]string, 0, 4)
|
||||
```
|
||||
|
||||
- 在顶层,使用标准var关键字。请勿指定类型,除非它与表达式的类型不同。
|
||||
|
||||
```go
|
||||
// bad
|
||||
var _s string = F()
|
||||
|
||||
func F() string { return "A" }
|
||||
|
||||
// good
|
||||
var _s = F()
|
||||
// 由于 F 已经明确了返回一个字符串类型,因此我们没有必要显式指定_s 的类型
|
||||
// 还是那种类型
|
||||
|
||||
func F() string { return "A" }
|
||||
```
|
||||
|
||||
- 对于未导出的顶层常量和变量,使用`_`作为前缀。
|
||||
|
||||
```go
|
||||
// bad
|
||||
const (
|
||||
defaultHost = "127.0.0.1"
|
||||
defaultPort = 8080
|
||||
)
|
||||
|
||||
// good
|
||||
const (
|
||||
_defaultHost = "127.0.0.1"
|
||||
_defaultPort = 8080
|
||||
)
|
||||
```
|
||||
|
||||
- 嵌入式类型(例如 mutex)应位于结构体内的字段列表的顶部,并且必须有一个空行将嵌入式字段与常规字段分隔开。
|
||||
|
||||
```go
|
||||
// bad
|
||||
type Client struct {
|
||||
version int
|
||||
http.Client
|
||||
}
|
||||
|
||||
// good
|
||||
type Client struct {
|
||||
http.Client
|
||||
|
||||
version int
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 错误处理
|
||||
|
||||
- `error`作为函数的值返回,必须对`error`进行处理,或将返回值赋值给明确忽略。对于`defer xx.Close()`可以不用显式处理。
|
||||
|
||||
```go
|
||||
func load() error {
|
||||
// normal code
|
||||
}
|
||||
|
||||
// bad
|
||||
load()
|
||||
|
||||
// good
|
||||
_ = load()
|
||||
```
|
||||
|
||||
- `error`作为函数的值返回且有多个返回值的时候,`error`必须是最后一个参数。
|
||||
|
||||
```go
|
||||
// bad
|
||||
func load() (error, int) {
|
||||
// normal code
|
||||
}
|
||||
|
||||
// good
|
||||
func load() (int, error) {
|
||||
// normal code
|
||||
}
|
||||
```
|
||||
|
||||
- 尽早进行错误处理,并尽早返回,减少嵌套。
|
||||
|
||||
```go
|
||||
// bad
|
||||
if err != nil {
|
||||
// error code
|
||||
} else {
|
||||
// normal code
|
||||
}
|
||||
|
||||
// good
|
||||
if err != nil {
|
||||
// error handling
|
||||
return err
|
||||
}
|
||||
// normal code
|
||||
```
|
||||
|
||||
- 如果需要在 if 之外使用函数调用的结果,则应采用下面的方式。
|
||||
|
||||
```go
|
||||
// bad
|
||||
if v, err := foo(); err != nil {
|
||||
// error handling
|
||||
}
|
||||
|
||||
// good
|
||||
v, err := foo()
|
||||
if err != nil {
|
||||
// error handling
|
||||
}
|
||||
```
|
||||
|
||||
- 错误要单独判断,不与其他逻辑组合判断。
|
||||
|
||||
```go
|
||||
// bad
|
||||
v, err := foo()
|
||||
if err != nil || v == nil {
|
||||
// error handling
|
||||
return err
|
||||
}
|
||||
|
||||
// good
|
||||
v, err := foo()
|
||||
if err != nil {
|
||||
// error handling
|
||||
return err
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
// error handling
|
||||
return errors.New("invalid value v")
|
||||
}
|
||||
```
|
||||
|
||||
- 如果返回值需要初始化,则采用下面的方式。
|
||||
|
||||
```go
|
||||
v, err := f()
|
||||
if err != nil {
|
||||
// error handling
|
||||
return // or continue.
|
||||
}
|
||||
```
|
||||
|
||||
- 错误描述建议
|
||||
- 错误描述用小写字母开头,结尾不要加标点符号,例如:
|
||||
```go
|
||||
// bad
|
||||
errors.New("Redis connection failed")
|
||||
errors.New("redis connection failed.")
|
||||
|
||||
// good
|
||||
errors.New("redis connection failed")
|
||||
```
|
||||
- 告诉用户他们可以做什么,而不是告诉他们不能做什么。
|
||||
- 当声明一个需求时,用must 而不是should。例如,`must be greater than 0、must match regex '[a-z]+'`。
|
||||
- 当声明一个格式不对时,用must not。例如,`must not contain`。
|
||||
- 当声明一个动作时用may not。例如,`may not be specified when otherField is empty、only name may be specified`。
|
||||
- 引用文字字符串值时,请在单引号中指示文字。例如,`ust not contain '..'`。
|
||||
- 当引用另一个字段名称时,请在反引号中指定该名称。例如,must be greater than `request`。
|
||||
- 指定不等时,请使用单词而不是符号。例如,`must be less than 256、must be greater than or equal to 0 (不要用 larger than、bigger than、more than、higher than)`。
|
||||
- 指定数字范围时,请尽可能使用包含范围。
|
||||
- 建议 Go 1.13 以上,error 生成方式为 `fmt.Errorf("module xxx: %w", err)`。
|
||||
|
||||
### 1.4 panic处理
|
||||
|
||||
- 在业务逻辑处理中禁止使用panic。
|
||||
- 在main包中,只有当程序完全不可运行时使用panic,例如无法打开文件、无法连接数据库导致程序无法正常运行。
|
||||
- 在main包中,使用 `log.Fatal` 来记录错误,这样就可以由log来结束程序,或者将panic抛出的异常记录到日志文件中,方便排查问题。
|
||||
- 可导出的接口一定不能有panic。
|
||||
- 包内建议采用error而不是panic来传递错误。
|
||||
|
||||
### 1.5 单元测试
|
||||
|
||||
- 单元测试文件名命名规范为 `example_test.go`。
|
||||
- 每个重要的可导出函数都要编写测试用例。
|
||||
- 因为单元测试文件内的函数都是不对外的,所以可导出的结构体、函数等可以不带注释。
|
||||
- 如果存在 `func (b *Bar) Foo` ,单测函数可以为 `func TestBar_Foo`。
|
||||
|
||||
### 1.6 类型断言失败处理
|
||||
|
||||
- type assertion 的单个返回值针对不正确的类型将产生 panic。请始终使用 “comma ok”的惯用法。
|
||||
|
||||
```go
|
||||
// bad
|
||||
t := n.(int)
|
||||
|
||||
// good
|
||||
t, ok := n.(int)
|
||||
if !ok {
|
||||
// error handling
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 命名规范
|
||||
|
||||
命名规范是代码规范中非常重要的一部分,一个统一的、短小的、精确的命名规范可以大大提高代码的可读性,也可以借此规避一些不必要的Bug。
|
||||
|
||||
### 2.1 包命名
|
||||
|
||||
- 包名必须和目录名一致,尽量采取有意义、简短的包名,不要和标准库冲突。
|
||||
- 包名全部小写,没有大写或下划线,使用多级目录来划分层级。
|
||||
- 项目名可以通过中划线来连接多个单词。
|
||||
- 包名以及包所在的目录名,不要使用复数,例如,是`net/url`,而不是`net/urls`。
|
||||
- 不要用 common、util、shared 或者 lib 这类宽泛的、无意义的包名。
|
||||
- 包名要简单明了,例如 net、time、log。
|
||||
|
||||
### 2.2 函数命名
|
||||
|
||||
- 函数名采用驼峰式,首字母根据访问控制决定使用大写或小写,例如:`MixedCaps`或者`mixedCaps`。
|
||||
- 代码生成工具自动生成的代码(如`xxxx.pb.go`)和为了对相关测试用例进行分组,而采用的下划线(如`TestMyFunction_WhatIsBeingTested`)排除此规则。
|
||||
|
||||
### 2.3 文件命名
|
||||
|
||||
- 文件名要简短有意义。
|
||||
- 文件名应小写,并使用下划线分割单词。
|
||||
|
||||
### 2.4 结构体命名
|
||||
|
||||
- 采用驼峰命名方式,首字母根据访问控制决定使用大写或小写,例如`MixedCaps`或者`mixedCaps`。
|
||||
- 结构体名不应该是动词,应该是名词,比如 `Node`、`NodeSpec`。
|
||||
- 避免使用Data、Info这类无意义的结构体名。
|
||||
- 结构体的声明和初始化应采用多行,例如:
|
||||
|
||||
```go
|
||||
// User 多行声明
|
||||
type User struct {
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
|
||||
// 多行初始化
|
||||
u := User{
|
||||
UserName: "belm",
|
||||
Email: "nosbelm@qq.com",
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 接口命名
|
||||
|
||||
- 接口命名的规则,基本和结构体命名规则保持一致:
|
||||
- 单个函数的接口名以 “er"”作为后缀(例如Reader,Writer),有时候可能导致蹩脚的英文,但是没关系。
|
||||
- 两个函数的接口名以两个函数名命名,例如ReadWriter。
|
||||
- 三个以上函数的接口名,类似于结构体名。
|
||||
|
||||
例如:
|
||||
|
||||
```
|
||||
// Seeking to an offset before the start of the file is an error.
|
||||
// Seeking to any positive offset is legal, but the behavior of subsequent
|
||||
// I/O operations on the underlying object is implementation-dependent.
|
||||
type Seeker interface {
|
||||
Seek(offset int64, whence int) (int64, error)
|
||||
}
|
||||
|
||||
// ReadWriter is the interface that groups the basic Read and Write methods.
|
||||
type ReadWriter interface {
|
||||
Reader
|
||||
Writer
|
||||
}
|
||||
```
|
||||
|
||||
### 2.6 变量命名
|
||||
|
||||
- 变量名必须遵循驼峰式,首字母根据访问控制决定使用大写或小写。
|
||||
- 在相对简单(对象数量少、针对性强)的环境中,可以将一些名称由完整单词简写为单个字母,例如:
|
||||
- user 可以简写为 u;
|
||||
- userID 可以简写 uid。
|
||||
- 特有名词时,需要遵循以下规则:
|
||||
- 如果变量为私有,且特有名词为首个单词,则使用小写,如 apiClient。
|
||||
- 其他情况都应当使用该名词原有的写法,如 APIClient、repoID、UserID。
|
||||
|
||||
下面列举了一些常见的特有名词。
|
||||
|
||||
```
|
||||
// A GonicMapper that contains a list of common initialisms taken from golang/lint
|
||||
var LintGonicMapper = GonicMapper{
|
||||
"API": true,
|
||||
"ASCII": true,
|
||||
"CPU": true,
|
||||
"CSS": true,
|
||||
"DNS": true,
|
||||
"EOF": true,
|
||||
"GUID": true,
|
||||
"HTML": true,
|
||||
"HTTP": true,
|
||||
"HTTPS": true,
|
||||
"ID": true,
|
||||
"IP": true,
|
||||
"JSON": true,
|
||||
"LHS": true,
|
||||
"QPS": true,
|
||||
"RAM": true,
|
||||
"RHS": true,
|
||||
"RPC": true,
|
||||
"SLA": true,
|
||||
"SMTP": true,
|
||||
"SSH": true,
|
||||
"TLS": true,
|
||||
"TTL": true,
|
||||
"UI": true,
|
||||
"UID": true,
|
||||
"UUID": true,
|
||||
"URI": true,
|
||||
"URL": true,
|
||||
"UTF8": true,
|
||||
"VM": true,
|
||||
"XML": true,
|
||||
"XSRF": true,
|
||||
"XSS": true,
|
||||
}
|
||||
```
|
||||
|
||||
- 若变量类型为bool类型,则名称应以Has,Is,Can或Allow开头,例如:
|
||||
|
||||
```go
|
||||
var hasConflict bool
|
||||
var isExist bool
|
||||
var canManage bool
|
||||
var allowGitHook bool
|
||||
```
|
||||
|
||||
- 局部变量应当尽可能短小,比如使用buf指代buffer,使用i指代index。
|
||||
- 代码生成工具自动生成的代码可排除此规则(如`xxx.pb.go`里面的Id)
|
||||
|
||||
### 2.7 常量命名
|
||||
|
||||
- 常量名必须遵循驼峰式,首字母根据访问控制决定使用大写或小写。
|
||||
- 如果是枚举类型的常量,需要先创建相应类型:
|
||||
|
||||
```go
|
||||
// Code defines an error code type.
|
||||
type Code int
|
||||
|
||||
// Internal errors.
|
||||
const (
|
||||
// ErrUnknown - 0: An unknown error occurred.
|
||||
ErrUnknown Code = iota
|
||||
// ErrFatal - 1: An fatal error occurred.
|
||||
ErrFatal
|
||||
)
|
||||
```
|
||||
|
||||
### 2.8 Error的命名
|
||||
|
||||
- Error类型应该写成FooError的形式。
|
||||
|
||||
```go
|
||||
type ExitError struct {
|
||||
// ....
|
||||
}
|
||||
```
|
||||
|
||||
- Error变量写成ErrFoo的形式。
|
||||
|
||||
```go
|
||||
var ErrFormat = errors.New("unknown format")
|
||||
```
|
||||
|
||||
## 3. 注释规范
|
||||
|
||||
- 每个可导出的名字都要有注释,该注释对导出的变量、函数、结构体、接口等进行简要介绍。
|
||||
- 全部使用单行注释,禁止使用多行注释。
|
||||
- 和代码的规范一样,单行注释不要过长,禁止超过 120 字符,超过的请使用换行展示,尽量保持格式优雅。
|
||||
- 注释必须是完整的句子,以需要注释的内容作为开头,句点作为结尾,`格式为 // 名称 描述.`。例如:
|
||||
|
||||
```go
|
||||
// bad
|
||||
// logs the flags in the flagset.
|
||||
func PrintFlags(flags *pflag.FlagSet) {
|
||||
// normal code
|
||||
}
|
||||
|
||||
// good
|
||||
// PrintFlags logs the flags in the flagset.
|
||||
func PrintFlags(flags *pflag.FlagSet) {
|
||||
// normal code
|
||||
}
|
||||
```
|
||||
|
||||
- 所有注释掉的代码在提交code review前都应该被删除,否则应该说明为什么不删除,并给出后续处理建议。
|
||||
|
||||
- 在多段注释之间可以使用空行分隔加以区分,如下所示:
|
||||
|
||||
```go
|
||||
// Package superman implements methods for saving the world.
|
||||
//
|
||||
// Experience has shown that a small number of procedures can prove
|
||||
// helpful when attempting to save the world.
|
||||
package superman
|
||||
```
|
||||
|
||||
### 3.1 包注释
|
||||
|
||||
- 每个包都有且仅有一个包级别的注释。
|
||||
- 包注释统一用 // 进行注释,格式为 `// Package 包名 包描述`,例如:
|
||||
|
||||
```go
|
||||
// Package genericclioptions contains flags which can be added to you command, bound, completed, and produce
|
||||
// useful helper functions.
|
||||
package genericclioptions
|
||||
```
|
||||
|
||||
### 3.2 变量/常量注释
|
||||
|
||||
- 每个可导出的变量/常量都必须有注释说明,`格式为// 变量名 变量描述`,例如:
|
||||
|
||||
```go
|
||||
// ErrSigningMethod defines invalid signing method error.
|
||||
var ErrSigningMethod = errors.New("Invalid signing method")
|
||||
```
|
||||
- 出现大块常量或变量定义时,可在前面注释一个总的说明,然后在每一行常量的前一行或末尾详细注释该常量的定义,例如:
|
||||
```go
|
||||
// Code must start with 1xxxxx.
|
||||
const (
|
||||
// ErrSuccess - 200: OK.
|
||||
ErrSuccess int = iota + 100001
|
||||
|
||||
// ErrUnknown - 500: Internal server error.
|
||||
ErrUnknown
|
||||
|
||||
// ErrBind - 400: Error occurred while binding the request body to the struct.
|
||||
ErrBind
|
||||
|
||||
// ErrValidation - 400: Validation failed.
|
||||
ErrValidation
|
||||
)
|
||||
```
|
||||
### 3.3 结构体注释
|
||||
|
||||
- 每个需要导出的结构体或者接口都必须有注释说明,格式为 `// 结构体名 结构体描述.`。
|
||||
- 结构体内的可导出成员变量名,如果意义不明确,必须要给出注释,放在成员变量的前一行或同一行的末尾。例如:
|
||||
|
||||
```go
|
||||
// User represents a user restful resource. It is also used as gorm model.
|
||||
type User struct {
|
||||
// Standard object's metadata.
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Nickname string `json:"nickname" gorm:"column:nickname"`
|
||||
Password string `json:"password" gorm:"column:password"`
|
||||
Email string `json:"email" gorm:"column:email"`
|
||||
Phone string `json:"phone" gorm:"column:phone"`
|
||||
IsAdmin int `json:"isAdmin,omitempty" gorm:"column:isAdmin"`
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 方法注释
|
||||
|
||||
每个需要导出的函数或者方法都必须有注释,格式为// 函数名 函数描述.,例如:
|
||||
|
||||
```go
|
||||
// BeforeUpdate run before update database record.
|
||||
func (p *Policy) BeforeUpdate() (err error) {
|
||||
// normal code
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 类型注释
|
||||
|
||||
- 每个需要导出的类型定义和类型别名都必须有注释说明,格式为 `// 类型名 类型描述.`,例如:
|
||||
|
||||
```go
|
||||
// Code defines an error code type.
|
||||
type Code int
|
||||
```
|
||||
|
||||
## 4. 类型
|
||||
|
||||
### 4.1 字符串
|
||||
|
||||
- 空字符串判断。
|
||||
|
||||
```go
|
||||
// bad
|
||||
if s == "" {
|
||||
// normal code
|
||||
}
|
||||
|
||||
// good
|
||||
if len(s) == 0 {
|
||||
// normal code
|
||||
}
|
||||
```
|
||||
|
||||
- `[]byte`/`string`相等比较。
|
||||
|
||||
```go
|
||||
// bad
|
||||
var s1 []byte
|
||||
var s2 []byte
|
||||
...
|
||||
bytes.Equal(s1, s2) == 0
|
||||
bytes.Equal(s1, s2) != 0
|
||||
|
||||
// good
|
||||
var s1 []byte
|
||||
var s2 []byte
|
||||
...
|
||||
bytes.Compare(s1, s2) == 0
|
||||
bytes.Compare(s1, s2) != 0
|
||||
```
|
||||
|
||||
- 复杂字符串使用raw字符串避免字符转义。
|
||||
|
||||
```go
|
||||
// bad
|
||||
regexp.MustCompile("\\.")
|
||||
|
||||
// good
|
||||
regexp.MustCompile(`\.`)
|
||||
```
|
||||
|
||||
### 4.2 切片
|
||||
|
||||
- 空slice判断。
|
||||
|
||||
```go
|
||||
// bad
|
||||
if len(slice) = 0 {
|
||||
// normal code
|
||||
}
|
||||
|
||||
// good
|
||||
if slice != nil && len(slice) == 0 {
|
||||
// normal code
|
||||
}
|
||||
```
|
||||
|
||||
上面判断同样适用于map、channel。
|
||||
|
||||
- 声明slice。
|
||||
|
||||
```go
|
||||
// bad
|
||||
s := []string{}
|
||||
s := make([]string, 0)
|
||||
|
||||
// good
|
||||
var s []string
|
||||
```
|
||||
|
||||
- slice复制。
|
||||
|
||||
```go
|
||||
// bad
|
||||
var b1, b2 []byte
|
||||
for i, v := range b1 {
|
||||
b2[i] = v
|
||||
}
|
||||
for i := range b1 {
|
||||
b2[i] = b1[i]
|
||||
}
|
||||
|
||||
// good
|
||||
copy(b2, b1)
|
||||
```
|
||||
|
||||
- slice新增。
|
||||
|
||||
```go
|
||||
// bad
|
||||
var a, b []int
|
||||
for _, v := range a {
|
||||
b = append(b, v)
|
||||
}
|
||||
|
||||
// good
|
||||
var a, b []int
|
||||
b = append(b, a...)
|
||||
```
|
||||
|
||||
### 4.3 结构体
|
||||
|
||||
- struct初始化。
|
||||
|
||||
struct以多行格式初始化。
|
||||
|
||||
```go
|
||||
type user struct {
|
||||
Id int64
|
||||
Name string
|
||||
}
|
||||
|
||||
u1 := user{100, "Colin"}
|
||||
|
||||
u2 := user{
|
||||
Id: 200,
|
||||
Name: "Lex",
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 控制结构
|
||||
|
||||
### 5.1 if
|
||||
|
||||
- if 接受初始化语句,约定如下方式建立局部变量。
|
||||
|
||||
```go
|
||||
if err := loadConfig(); err != nil {
|
||||
// error handling
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
- if 对于bool类型的变量,应直接进行真假判断。
|
||||
|
||||
```go
|
||||
var isAllow bool
|
||||
if isAllow {
|
||||
// normal code
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 for
|
||||
|
||||
- 采用短声明建立局部变量。
|
||||
|
||||
```go
|
||||
sum := 0
|
||||
for i := 0; i < 10; i++ {
|
||||
sum += 1
|
||||
}
|
||||
```
|
||||
|
||||
- 不要在 for 循环里面使用 defer,defer只有在函数退出时才会执行。
|
||||
|
||||
```go
|
||||
// bad
|
||||
for file := range files {
|
||||
fd, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
// normal code
|
||||
}
|
||||
|
||||
// good
|
||||
for file := range files {
|
||||
func() {
|
||||
fd, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
// normal code
|
||||
}()
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 range
|
||||
|
||||
- 如果只需要第一项(key),就丢弃第二个。
|
||||
|
||||
```go
|
||||
for key := range keys {
|
||||
// normal code
|
||||
}
|
||||
```
|
||||
|
||||
- 如果只需要第二项,则把第一项置为下划线。
|
||||
|
||||
```go
|
||||
sum := 0
|
||||
for _, value := range array {
|
||||
sum += value
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 switch
|
||||
|
||||
- 必须要有default。
|
||||
|
||||
```go
|
||||
switch os := runtime.GOOS; os {
|
||||
case "linux":
|
||||
fmt.Println("Linux.")
|
||||
case "darwin":
|
||||
fmt.Println("OS X.")
|
||||
default:
|
||||
fmt.Printf("%s.\n", os)
|
||||
}
|
||||
```
|
||||
|
||||
### 5.5 goto
|
||||
- 业务代码禁止使用 goto 。
|
||||
- 框架或其他底层源码尽量不用。
|
||||
|
||||
## 6. 函数
|
||||
|
||||
- 传入变量和返回变量以小写字母开头。
|
||||
- 函数参数个数不能超过5个。
|
||||
- 函数分组与顺序
|
||||
- 函数应按粗略的调用顺序排序。
|
||||
- 同一文件中的函数应按接收者分组。
|
||||
- 尽量采用值传递,而非指针传递。
|
||||
- 传入参数是 map、slice、chan、interface ,不要传递指针。
|
||||
|
||||
### 6.1 函数参数
|
||||
|
||||
- 如果函数返回相同类型的两个或三个参数,或者如果从上下文中不清楚结果的含义,使用命名返回,其他情况不建议使用命名返回,例如:
|
||||
|
||||
```go
|
||||
func coordinate() (x, y float64, err error) {
|
||||
// normal code
|
||||
}
|
||||
```
|
||||
- 传入变量和返回变量都以小写字母开头。
|
||||
- 尽量用值传递,非指针传递。
|
||||
- 参数数量均不能超过5个。
|
||||
- 多返回值最多返回三个,超过三个请使用 struct。
|
||||
|
||||
### 6.2 defer
|
||||
|
||||
- 当存在资源创建时,应紧跟defer释放资源(可以大胆使用defer,defer在Go1.14版本中,性能大幅提升,defer的性能损耗即使在性能敏感型的业务中,也可以忽略)。
|
||||
- 先判断是否错误,再defer释放资源,例如:
|
||||
|
||||
```go
|
||||
rep, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
```
|
||||
|
||||
### 6.3 方法的接收器
|
||||
|
||||
- 推荐以类名第一个英文首字母的小写作为接收器的命名。
|
||||
- 接收器的命名在函数超过20行的时候不要用单字符。
|
||||
- 接收器的命名不能采用me、this、self这类易混淆名称。
|
||||
|
||||
### 6.4 嵌套
|
||||
- 嵌套深度不能超过4层。
|
||||
|
||||
### 6.5 变量命名
|
||||
- 变量声明尽量放在变量第一次使用的前面,遵循就近原则。
|
||||
- 如果魔法数字出现超过两次,则禁止使用,改用一个常量代替,例如:
|
||||
|
||||
```go
|
||||
// PI ...
|
||||
const Prise = 3.14
|
||||
|
||||
func getAppleCost(n float64) float64 {
|
||||
return Prise * n
|
||||
}
|
||||
|
||||
func getOrangeCost(n float64) float64 {
|
||||
return Prise * n
|
||||
}
|
||||
```
|
||||
|
||||
## 7. GOPATH 设置规范
|
||||
- Go 1.11 之后,弱化了 GOPATH 规则,已有代码(很多库肯定是在1.11之前建立的)肯定符合这个规则,建议保留 GOPATH 规则,便于维护代码。
|
||||
- 建议只使用一个 GOPATH,不建议使用多个 GOPATH。如果使用多个GOPATH,编译生效的 bin 目录是在第一个 GOPATH 下。
|
||||
|
||||
## 8. 依赖管理
|
||||
|
||||
- Go 1.11 以上必须使用 Go Modules。
|
||||
- 使用Go Modules作为依赖管理的项目时,不建议提交vendor目录。
|
||||
- 使用Go Modules作为依赖管理的项目时,必须提交go.sum文件。
|
||||
|
||||
### 9. 最佳实践
|
||||
|
||||
- 尽量少用全局变量,而是通过参数传递,使每个函数都是“无状态”的。这样可以减少耦合,也方便分工和单元测试。
|
||||
- 在编译时验证接口的符合性,例如:
|
||||
|
||||
```go
|
||||
type LogHandler struct {
|
||||
h http.Handler
|
||||
log *zap.Logger
|
||||
}
|
||||
var _ http.Handler = LogHandler{}
|
||||
```
|
||||
- 服务器处理请求时,应该创建一个context,保存该请求的相关信息(如requestID),并在函数调用链中传递。
|
||||
|
||||
### 9.1 性能
|
||||
- string 表示的是不可变的字符串变量,对 string 的修改是比较重的操作,基本上都需要重新申请内存。所以,如果没有特殊需要,需要修改时多使用 []byte。
|
||||
- 优先使用 strconv 而不是 fmt。
|
||||
|
||||
### 9.2 注意事项
|
||||
|
||||
- append 要小心自动分配内存,append 返回的可能是新分配的地址。
|
||||
- 如果要直接修改 map 的 value 值,则 value 只能是指针,否则要覆盖原来的值。
|
||||
- map 在并发中需要加锁。
|
||||
- 编译过程无法检查 interface{} 的转换,只能在运行时检查,小心引起 panic。
|
||||
|
||||
## 总结
|
||||
|
||||
这里向你介绍了九类常用的编码规范。但今天的最后,我要在这里提醒你一句:规范是人定的,你也可以根据需要,制定符合你项目的规范,但同时我也建议你采纳这些业界沉淀下来的规范,并通过工具来确保规范的执行。
|
||||
69
docs/conversions/images.md
Normal file
69
docs/conversions/images.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# OpenIM Image Management Strategy and Pulling Guide
|
||||
|
||||
OpenIM is an efficient, stable, and scalable instant messaging framework that provides convenient deployment methods through Docker images. OpenIM manages multiple image sources, hosted respectively on GitHub (ghcr), Alibaba Cloud, and Docker Hub. This document is aimed at detailing the image management strategy of OpenIM and providing the steps for pulling these images.
|
||||
|
||||
## Image Management Strategy
|
||||
|
||||
OpenIM's versions correspond to GitHub's tag versions. Each time we release a new version and tag it on GitHub, an automated process is triggered that pushes the new Docker image version to the following three platforms:
|
||||
|
||||
1. **GitHub (ghcr.io):** We use GitHub Container Registry (ghcr.io) to host OpenIM's Docker images. This allows us to better integrate with the GitHub source code repository, providing better version control and continuous integration/deployment (CI/CD) features. You can view all GitHub images [here](https://github.com/orgs/OpenIMSDK/packages).
|
||||
2. **Alibaba Cloud (registry.cn-hangzhou.aliyuncs.com):** For users in Mainland China, we also host OpenIM's Docker images on Alibaba Cloud to provide faster pull speeds. You can view all Alibaba Cloud images on this [page](https://cr.console.aliyun.com/cn-hangzhou/instances/repositories) of Alibaba Cloud Image Service (note that you need to log in to your Alibaba Cloud account first).
|
||||
3. **Docker Hub (docker.io):** Docker Hub is the most commonly used Docker image hosting platform, and we also host OpenIM's images there to facilitate developers worldwide. You can view all Docker Hub images on the [OpenIM's Docker Hub page](https://hub.docker.com/r/openim).
|
||||
|
||||
## Methods and Steps for Pulling Images
|
||||
|
||||
When pulling OpenIM's Docker images, you can choose the most suitable source based on your geographic location and network conditions. Here are the steps to pull OpenIM images from each source:
|
||||
|
||||
1. First, make sure Docker is installed on your machine. If not, you can refer to the [Docker official documentation](https://docs.docker.com/get-docker/) for installation.
|
||||
|
||||
2. Open the terminal and run the following commands to pull the images:
|
||||
|
||||
For OpenIM Server:
|
||||
|
||||
- Pull from GitHub:
|
||||
|
||||
```
|
||||
bashCopy code
|
||||
docker pull ghcr.io/openimsdk/openim-server:latest
|
||||
```
|
||||
|
||||
- Pull from Alibaba Cloud:
|
||||
|
||||
```
|
||||
bashCopy code
|
||||
docker pull registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server:latest
|
||||
```
|
||||
|
||||
- Pull from Docker Hub:
|
||||
|
||||
```
|
||||
bashCopy code
|
||||
docker pull docker.io/openim/openim-server:latest
|
||||
```
|
||||
|
||||
For OpenIM Chat:
|
||||
|
||||
- Pull from GitHub:
|
||||
|
||||
```
|
||||
bashCopy code
|
||||
docker pull ghcr.io/openimsdk/openim-chat:latest
|
||||
```
|
||||
|
||||
- Pull from Alibaba Cloud:
|
||||
|
||||
```
|
||||
bashCopy code
|
||||
docker pull registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-chat:latest
|
||||
```
|
||||
|
||||
- Pull from Docker Hub:
|
||||
|
||||
```
|
||||
bashCopy code
|
||||
docker pull docker.io/openim/openim-chat:latest
|
||||
```
|
||||
|
||||
3. Run the `docker images` command to confirm that the image has been successfully pulled.
|
||||
|
||||
This concludes OpenIM's image management strategy and the steps for pulling images. If you have any questions, please feel free to ask.
|
||||
20
docs/conversions/logging.md
Normal file
20
docs/conversions/logging.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## Log Standards
|
||||
|
||||
### Log Standards
|
||||
|
||||
- The unified log package `github.com/openimsdk/Open-IM-Server/internal/pkg/log` should be used for all logging;
|
||||
- Use structured logging formats: `log.Infow`, `log.Warnw`, `log.Errorw`, etc. For example: `log.Infow("Update post function called")`;
|
||||
- All logs should start with an uppercase letter and should not end with a `.`. For example: `log.Infow("Update post function called")`;
|
||||
- Use past tense. For example, use `Could not delete B` instead of `Cannot delete B`;
|
||||
- Adhere to log level standards:
|
||||
- Debug level logs use `log.Debugw`;
|
||||
- Info level logs use `log.Infow`;
|
||||
- Warning level logs use `log.Warnw`;
|
||||
- Error level logs use `log.Errorw`;
|
||||
- Panic level logs use `log.Panicw`;
|
||||
- Fatal level logs use `log.Fatalw`.
|
||||
- Log settings:
|
||||
- Development and test environments: The log level is set to `debug`, the log format can be set to `console` / `json` as needed, and caller is enabled;
|
||||
- Production environment: The log level is set to `info`, the log format is set to `json`, and caller is enabled. (**Note**: In the early stages of going online, to facilitate troubleshooting, the log level can be set to `debug`)
|
||||
- When logging, avoid outputting sensitive information, such as passwords, keys, etc.
|
||||
- If you are calling a logging function in a function/method with a `context.Context` parameter, it is recommended to use `log.L(ctx).Infow()` for logging.
|
||||
57
docs/conversions/version.md
Normal file
57
docs/conversions/version.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# OpenIM Branch Management and Versioning
|
||||
|
||||
Our project, OpenIM, follows the [Semantic Versioning 2.0.0](https://semver.org/lang/zh-CN/) standards.
|
||||
|
||||
## OpenIM version
|
||||
|
||||
OpenIM manages two primary branches: `main` and `release`. The project uses Semantic Versioning 2.0.0 to tag different versions of the software, each indicating a significant milestone in the software's development.
|
||||
|
||||
In the OpenIM repository, the versioning adheres to the `MAJOR.MINOR.PATCH` format, where:
|
||||
|
||||
- `MAJOR` version changes when there are incompatible changes to the API,
|
||||
- `MINOR` version changes when features are added in a backward-compatible manner, and
|
||||
- `PATCH` version changes when backward-compatible bugs are fixed.
|
||||
|
||||
## Milestones and Branching
|
||||
|
||||
When a significant milestone like v3.1.0 is achieved, a new branch `release-v3.1` is created. This branch contains all the code pertaining to this stable release. All bug fixes and features intended for the next version, v3.2.0, are merged into this branch.
|
||||
|
||||
The release of `PATCH` versions (Z in `X.Y.Z`) are driven by bug fixes, and these can be rolled out depending on the bug's priority or over a scheduled time. On the other hand, `MINOR` versions (Y in `X.Y.Z`) are released based on the project's roadmap, milestone completion, or on a scheduled timeline. Importantly, the API of minor versions is always backward-compatible.
|
||||
|
||||
## Dealing with Major Bugs
|
||||
|
||||
In the event of a major bug discovery, the fix would selectively be merged into the previous version (e.g., v3.1 or the `release-v3.1` branch), as well as into the `main` branch. This is to ensure that users relying on the older version can still receive important bug fixes, while also keeping the main branch updated.
|
||||
|
||||
It's worth noting that a robust testing regime should be in place to ensure the integrity of all branches at any given time. Automated tests and code review sessions are crucial components of maintaining a healthy codebase.
|
||||
|
||||
To summarize, OpenIM's approach to branch management and versioning ensures a balance between introducing new features, fixing bugs, and maintaining backward compatibility. This strategy is vital for managing user expectations, supporting older versions, and paving the way for the project's continuous growth.
|
||||
|
||||
## Git Workflow Example
|
||||
|
||||
To put the above principles into practice, here's a Git workflow example that you might follow when working on a bug fix:
|
||||
|
||||
```
|
||||
bashCopy code# Checkout the branch for the version that needs the bug fix
|
||||
git checkout release-v3.1
|
||||
|
||||
# Create a new branch for the bug fix
|
||||
git checkout -b bug/bug-name
|
||||
|
||||
# ... Make changes, commit your work ...
|
||||
|
||||
# Push the branch to your remote repository
|
||||
git push origin bug/bug-name
|
||||
|
||||
# After the pull request is merged into the release-v3.1 branch,
|
||||
# checkout and update your main branch
|
||||
git checkout main
|
||||
git pull origin main
|
||||
|
||||
# Merge or rebase the changes from release-v3.1 into main
|
||||
git merge release-v3.1
|
||||
|
||||
# Push the updates to the main branch
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Remember, communication with your team is key throughout this process, keeping everyone up-to-date with the changes being made.
|
||||
34
docs/统计接口调用example.md
Normal file
34
docs/统计接口调用example.md
Normal file
@@ -0,0 +1,34 @@
|
||||
- chat 管理端 在线人数统计
|
||||
curl -sS -X POST "http://127.0.0.1:10009/statistic/online_user_count" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiJpbUFkbWluIiwiVXNlclR5cGUiOjIsIlBsYXRmb3JtSUQiOjAsImV4cCI6MTc3NTI5NTUzMSwibmJmIjoxNzY3NTE5NDcxLCJpYXQiOjE3Njc1MTk1MzF9.m-7AuhZS9Xd_5YMBdxNn7-QQ865PrC2SgKZjByDc3XE" \
|
||||
-H "operationID: op_chat_online_001" \
|
||||
-d "{}"
|
||||
|
||||
- chat 管理端 在线人数走势统计
|
||||
curl -sS -X POST "http://127.0.0.1:10009/statistic/online_user_count_trend" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiJpbUFkbWluIiwiVXNlclR5cGUiOjIsIlBsYXRmb3JtSUQiOjAsImV4cCI6MTc3NTI5NTUzMSwibmJmIjoxNzY3NTE5NDcxLCJpYXQiOjE3Njc1MTk1MzF9.m-7AuhZS9Xd_5YMBdxNn7-QQ865PrC2SgKZjByDc3XE" \
|
||||
-H "operationID: op_chat_online_trend_001" \
|
||||
-d '{"startTime":1700000000000,"endTime":1700086400000,"intervalMinutes":15}'
|
||||
|
||||
- chat 管理端 用户发送消息总数统计
|
||||
curl -sS -X POST "http://127.0.0.1:10009/statistic/user_send_msg_count" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiJpbUFkbWluIiwiVXNlclR5cGUiOjIsIlBsYXRmb3JtSUQiOjAsImV4cCI6MTc3NTI5NTUzMSwibmJmIjoxNzY3NTE5NDcxLCJpYXQiOjE3Njc1MTk1MzF9.m-7AuhZS9Xd_5YMBdxNn7-QQ865PrC2SgKZjByDc3XE" \
|
||||
-H "operationID: op_chat_msg_count_001" \
|
||||
-d "{}"
|
||||
|
||||
- chat 管理端 用户发送消息总数走势统计
|
||||
curl -sS -X POST "http://127.0.0.1:10009/statistic/user_send_msg_count_trend" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiJpbUFkbWluIiwiVXNlclR5cGUiOjIsIlBsYXRmb3JtSUQiOjAsImV4cCI6MTc3NTI5NTUzMSwibmJmIjoxNzY3NTE5NDcxLCJpYXQiOjE3Njc1MTk1MzF9.m-7AuhZS9Xd_5YMBdxNn7-QQ865PrC2SgKZjByDc3XE" \
|
||||
-H "operationID: op_chat_msg_trend_001" \
|
||||
-d '{"userID":"u001","chatType":1,"startTime":1700000000000,"endTime":1700086400000,"intervalMinutes":30}'
|
||||
|
||||
- chat 管理端 用户发送消息筛选与查询
|
||||
curl -sS -X POST "http://127.0.0.1:10009/statistic/user_send_msg_query" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySUQiOiJpbUFkbWluIiwiVXNlclR5cGUiOjIsIlBsYXRmb3JtSUQiOjAsImV4cCI6MTc3NTI5NTUzMSwibmJmIjoxNzY3NTE5NDcxLCJpYXQiOjE3Njc1MTk1MzF9.m-7AuhZS9Xd_5YMBdxNn7-QQ865PrC2SgKZjByDc3XE" \
|
||||
-H "operationID: op_chat_msg_query_001" \
|
||||
-d '{"userID":"u001","startTime":1700000000000,"endTime":1700086400000,"content":"关键词","pageNumber":1,"showNumber":50}'
|
||||
Reference in New Issue
Block a user