feat: migrate workbench services
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,230 +0,0 @@
|
||||
-- IM 工作台 AI 建表 SQL
|
||||
-- Run on the shared meeting_im database.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wb_ai_capability_config (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
tenant_code VARCHAR(64) NOT NULL DEFAULT '',
|
||||
feature VARCHAR(64) NOT NULL,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 0,
|
||||
model VARCHAR(64) NOT NULL DEFAULT 'deepseek-flash',
|
||||
daily_limit INT NOT NULL DEFAULT 0,
|
||||
concurrency_limit INT NOT NULL DEFAULT 1,
|
||||
max_input_chars INT NOT NULL DEFAULT 0,
|
||||
max_file_size_mb INT NOT NULL DEFAULT 0,
|
||||
config_json JSON NULL,
|
||||
created_at DATETIME(3) NOT NULL,
|
||||
updated_at DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_tenant_feature (tenant_id, feature)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wb_ai_task (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
task_id VARCHAR(64) NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
tenant_code VARCHAR(64) NOT NULL,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
feature VARCHAR(64) NOT NULL,
|
||||
status VARCHAR(32) NOT NULL,
|
||||
progress INT NOT NULL DEFAULT 0,
|
||||
source_type VARCHAR(32) NOT NULL DEFAULT '',
|
||||
source_ref_id VARCHAR(128) NOT NULL DEFAULT '',
|
||||
input_hash VARCHAR(128) NOT NULL DEFAULT '',
|
||||
input_snapshot JSON NULL,
|
||||
prompt_version VARCHAR(64) NOT NULL DEFAULT '',
|
||||
model VARCHAR(64) NOT NULL DEFAULT '',
|
||||
result_ref_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
result_json JSON NULL,
|
||||
error_reason VARCHAR(64) NOT NULL DEFAULT '',
|
||||
error_message VARCHAR(255) NOT NULL DEFAULT '',
|
||||
retry_count INT NOT NULL DEFAULT 0,
|
||||
started_at DATETIME(3) NULL,
|
||||
finished_at DATETIME(3) NULL,
|
||||
created_at DATETIME(3) NOT NULL,
|
||||
updated_at DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_task_id (task_id),
|
||||
KEY idx_user_feature_time (tenant_id, user_id, feature, created_at),
|
||||
KEY idx_status_time (status, created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wb_ai_task_step (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
task_id VARCHAR(64) NOT NULL,
|
||||
step VARCHAR(64) NOT NULL,
|
||||
title VARCHAR(128) NOT NULL,
|
||||
status VARCHAR(32) NOT NULL,
|
||||
progress INT NOT NULL DEFAULT 0,
|
||||
error_reason VARCHAR(64) NOT NULL DEFAULT '',
|
||||
started_at DATETIME(3) NULL,
|
||||
finished_at DATETIME(3) NULL,
|
||||
created_at DATETIME(3) NOT NULL,
|
||||
updated_at DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_task_step (task_id, step)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wb_ai_record (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
record_id VARCHAR(64) NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
tenant_code VARCHAR(64) NOT NULL,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
record_type VARCHAR(64) NOT NULL,
|
||||
title VARCHAR(128) NOT NULL,
|
||||
summary VARCHAR(512) NOT NULL DEFAULT '',
|
||||
status VARCHAR(32) NOT NULL,
|
||||
detail_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
task_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
created_at DATETIME(3) NOT NULL,
|
||||
updated_at DATETIME(3) NOT NULL,
|
||||
deleted_at DATETIME(3) NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_record_id (record_id),
|
||||
KEY idx_user_type_time (tenant_id, user_id, record_type, created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wb_ai_document (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
record_id VARCHAR(64) NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
document_type VARCHAR(64) NOT NULL,
|
||||
title VARCHAR(128) NOT NULL,
|
||||
content_markdown MEDIUMTEXT NOT NULL,
|
||||
content_plain MEDIUMTEXT NULL,
|
||||
sections_json JSON NULL,
|
||||
source_excerpt VARCHAR(1024) NOT NULL DEFAULT '',
|
||||
prompt_version VARCHAR(64) NOT NULL DEFAULT '',
|
||||
model VARCHAR(64) NOT NULL DEFAULT '',
|
||||
created_at DATETIME(3) NOT NULL,
|
||||
updated_at DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_record_id (record_id),
|
||||
KEY idx_tenant_user_time (tenant_id, user_id, created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wb_ai_table_analysis (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
record_id VARCHAR(64) NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
file_id VARCHAR(128) NOT NULL,
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
file_type VARCHAR(16) NOT NULL,
|
||||
file_size BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
topic VARCHAR(128) NOT NULL,
|
||||
metrics_json JSON NULL,
|
||||
preview_json JSON NULL,
|
||||
alerts_json JSON NULL,
|
||||
interpretation MEDIUMTEXT NULL,
|
||||
export_file_id VARCHAR(128) NOT NULL DEFAULT '',
|
||||
prompt_version VARCHAR(64) NOT NULL DEFAULT '',
|
||||
model VARCHAR(64) NOT NULL DEFAULT '',
|
||||
created_at DATETIME(3) NOT NULL,
|
||||
updated_at DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_record_id (record_id),
|
||||
KEY idx_file_id (tenant_id, file_id),
|
||||
KEY idx_tenant_user_time (tenant_id, user_id, created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wb_ai_meeting_summary (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
summary_id VARCHAR(64) NOT NULL,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
tenant_code VARCHAR(64) NOT NULL,
|
||||
meeting_id VARCHAR(128) NOT NULL,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
task_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
conclusions_json JSON NULL,
|
||||
decisions_json JSON NULL,
|
||||
action_items_json JSON NULL,
|
||||
source_file_ids_json JSON NULL,
|
||||
prompt_version VARCHAR(64) NOT NULL DEFAULT '',
|
||||
model VARCHAR(64) NOT NULL DEFAULT '',
|
||||
edited_by VARCHAR(64) NOT NULL DEFAULT '',
|
||||
edited_at DATETIME(3) NULL,
|
||||
version INT NOT NULL DEFAULT 1,
|
||||
created_at DATETIME(3) NOT NULL,
|
||||
updated_at DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_summary_id (summary_id),
|
||||
UNIQUE KEY uk_tenant_meeting (tenant_id, meeting_id),
|
||||
KEY idx_user_time (tenant_id, user_id, updated_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wb_ai_usage_log (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
tenant_code VARCHAR(64) NOT NULL,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
feature VARCHAR(64) NOT NULL,
|
||||
task_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
provider VARCHAR(32) NOT NULL,
|
||||
model VARCHAR(64) NOT NULL,
|
||||
provider_request_id VARCHAR(128) NOT NULL DEFAULT '',
|
||||
prompt_tokens INT NOT NULL DEFAULT 0,
|
||||
completion_tokens INT NOT NULL DEFAULT 0,
|
||||
total_tokens INT NOT NULL DEFAULT 0,
|
||||
latency_ms INT NOT NULL DEFAULT 0,
|
||||
status VARCHAR(32) NOT NULL,
|
||||
error_reason VARCHAR(64) NOT NULL DEFAULT '',
|
||||
input_hash VARCHAR(128) NOT NULL DEFAULT '',
|
||||
output_hash VARCHAR(128) NOT NULL DEFAULT '',
|
||||
created_at DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_user_feature_day (tenant_id, user_id, feature, created_at),
|
||||
KEY idx_provider_time (provider, model, created_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wb_ai_idempotency_record (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
idempotency_key VARCHAR(128) NOT NULL,
|
||||
request_hash VARCHAR(128) NOT NULL,
|
||||
response_code INT NOT NULL DEFAULT 0,
|
||||
response_body JSON NULL,
|
||||
status VARCHAR(32) NOT NULL,
|
||||
created_at DATETIME(3) NOT NULL,
|
||||
updated_at DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_idempotency (tenant_id, user_id, idempotency_key)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wb_ai_prompt_template (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
template_key VARCHAR(64) NOT NULL,
|
||||
version VARCHAR(64) NOT NULL,
|
||||
provider VARCHAR(32) NOT NULL DEFAULT 'deepseek',
|
||||
model VARCHAR(64) NOT NULL DEFAULT 'deepseek-flash',
|
||||
system_prompt MEDIUMTEXT NOT NULL,
|
||||
user_prompt_template MEDIUMTEXT NOT NULL,
|
||||
response_schema JSON NULL,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
created_at DATETIME(3) NOT NULL,
|
||||
updated_at DATETIME(3) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_template_version (template_key, version),
|
||||
KEY idx_template_enabled (template_key, enabled)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
INSERT INTO wb_ai_capability_config
|
||||
(tenant_id, tenant_code, feature, enabled, model, daily_limit, concurrency_limit, max_input_chars, max_file_size_mb, config_json, created_at, updated_at)
|
||||
VALUES
|
||||
(0, '', 'smart_document', 0, 'deepseek-flash', 20, 1, 1000, 0, JSON_OBJECT('entry_title', '智能文档'), NOW(3), NOW(3)),
|
||||
(0, '', 'smart_table', 0, 'deepseek-v4-pro', 10, 1, 100, 20, JSON_OBJECT('entry_title', '智能表格', 'allowed_file_types', JSON_ARRAY('xlsx', 'xls', 'csv')), NOW(3), NOW(3)),
|
||||
(0, '', 'text_polish', 0, 'deepseek-flash', 50, 3, 300, 0, JSON_OBJECT('entry_title', 'AI美文'), NOW(3), NOW(3)),
|
||||
(0, '', 'daily_report_polish', 0, 'deepseek-flash', 50, 3, 300, 0, JSON_OBJECT('entry_title', 'AI美文'), NOW(3), NOW(3)),
|
||||
(0, '', 'monthly_report_polish', 0, 'deepseek-flash', 30, 3, 1000, 0, JSON_OBJECT('entry_title', 'AI美文'), NOW(3), NOW(3)),
|
||||
(0, '', 'meeting_summary', 0, 'deepseek-v4-pro', 10, 1, 0, 0, JSON_OBJECT('entry_title', 'AI总结'), NOW(3), NOW(3))
|
||||
ON DUPLICATE KEY UPDATE
|
||||
model = VALUES(model),
|
||||
daily_limit = VALUES(daily_limit),
|
||||
concurrency_limit = VALUES(concurrency_limit),
|
||||
max_input_chars = VALUES(max_input_chars),
|
||||
max_file_size_mb = VALUES(max_file_size_mb),
|
||||
config_json = VALUES(config_json),
|
||||
updated_at = NOW(3);
|
||||
@@ -1,442 +0,0 @@
|
||||
# 工作台与 Manager 内部配置运维说明
|
||||
|
||||
更新时间:2026-09-17
|
||||
|
||||
## 1. 背景
|
||||
|
||||
`im_workbench` 需要从 `im_manager` 读取系统配置,例如:
|
||||
|
||||
- `im.uid_encryption_key`
|
||||
|
||||
读取链路如下:
|
||||
|
||||
```text
|
||||
im_workbench
|
||||
-> GET https://admin.118im.com/internal/im-config/third-party/im?keys=uid_encryption_key,UID_ENCRYPTION_KEY,uid_key
|
||||
-> im_manager 校验 HMAC 签名
|
||||
-> 返回系统配置
|
||||
```
|
||||
|
||||
这个接口是服务间内部接口,不是普通前端接口。调用方必须带 HMAC 请求头:
|
||||
|
||||
```http
|
||||
X-Service-Name: im-workbench
|
||||
X-Timestamp: <unix seconds>
|
||||
X-Nonce: <random nonce>
|
||||
X-Signature: <hmac-sha256 hex>
|
||||
```
|
||||
|
||||
签名使用的密钥来自 `im_manager` 测试环境 Secret 中的:
|
||||
|
||||
```text
|
||||
manager-system / im-manager-secrets / IM_CONFIG_API_SECRET
|
||||
```
|
||||
|
||||
注意:不要把真实密钥写进 Git 文档或代码。
|
||||
|
||||
## 2. 为什么 .env 不生效
|
||||
|
||||
测试环境运行在 Kubernetes Pod 中,服务启动时不会自动读取仓库里的 `.env` 文件。
|
||||
|
||||
当前代码和启动方式是:
|
||||
|
||||
- `im_workbench` 只读取进程环境变量和 `IM_WORKBENCH_CONFIG` 指向的 JSON 配置文件。
|
||||
- `im_manager` 的 entrypoint 使用 Pod 环境变量做 `envsubst` 生成 `config.yaml`。
|
||||
- 两个服务都没有 `godotenv.Load()`、`source .env` 或 docker compose `env_file` 逻辑。
|
||||
|
||||
所以测试环境真正生效的是:
|
||||
|
||||
```text
|
||||
Kubernetes Secret / Deployment 环境变量
|
||||
```
|
||||
|
||||
不是仓库中的 `.env`。
|
||||
|
||||
## 3. 当前 Kubernetes 配置关系
|
||||
|
||||
### im_workbench
|
||||
|
||||
命名空间:
|
||||
|
||||
```text
|
||||
workbench-system
|
||||
```
|
||||
|
||||
Deployment:
|
||||
|
||||
```text
|
||||
im-workbench
|
||||
```
|
||||
|
||||
当前 Pod 里只显式注入:
|
||||
|
||||
```text
|
||||
IM_WORKBENCH_CONFIG=/etc/im-workbench/workbench.json
|
||||
```
|
||||
|
||||
`/etc/im-workbench/workbench.json` 来自:
|
||||
|
||||
```text
|
||||
workbench-system / im-workbench-secrets / workbench.json
|
||||
```
|
||||
|
||||
因此 workbench 的运行时配置要改这个 Secret 里的 `workbench.json`,或在 Deployment 里显式加环境变量。
|
||||
|
||||
### im_manager
|
||||
|
||||
命名空间:
|
||||
|
||||
```text
|
||||
manager-system
|
||||
```
|
||||
|
||||
Deployment:
|
||||
|
||||
```text
|
||||
im-manager
|
||||
```
|
||||
|
||||
Secret:
|
||||
|
||||
```text
|
||||
manager-system / im-manager-secrets
|
||||
```
|
||||
|
||||
其中和内部配置接口相关的 key:
|
||||
|
||||
```text
|
||||
IM_CONFIG_API_SERVICE_NAME
|
||||
IM_CONFIG_API_SECRET
|
||||
```
|
||||
|
||||
## 4. 必须配置什么
|
||||
|
||||
### 4.1 im_manager 需要允许 im_workbench 调配置接口
|
||||
|
||||
`im_manager` 的 `IM_CONFIG_API_SERVICE_NAME` 需要支持两个调用方:
|
||||
|
||||
```text
|
||||
im-server,im-workbench
|
||||
```
|
||||
|
||||
原因:
|
||||
|
||||
- `im_server` 也需要读取 manager 配置。
|
||||
- `im_workbench` 也需要读取 manager 配置。
|
||||
- 如果只配置 `im-workbench`,会影响 `im_server`。
|
||||
- 如果只配置 `im-server`,`im_workbench` 用正确 service name 调用会 401。
|
||||
|
||||
目标配置:
|
||||
|
||||
```text
|
||||
IM_CONFIG_API_SERVICE_NAME=im-server,im-workbench
|
||||
```
|
||||
|
||||
`IM_CONFIG_API_SECRET` 保持现有值,不要改成空,也不要泄露到日志或文档。
|
||||
|
||||
### 4.2 im_workbench 需要配置 manager 连接信息
|
||||
|
||||
`workbench.json` 中需要有:
|
||||
|
||||
```json
|
||||
{
|
||||
"manager": {
|
||||
"base_url": "https://admin.118im.com",
|
||||
"service_name": "im-workbench",
|
||||
"secret": "<填 manager-system/im-manager-secrets 中的 IM_CONFIG_API_SECRET>",
|
||||
"uid_key_group": "im",
|
||||
"uid_key_keys": [
|
||||
"uid_encryption_key",
|
||||
"UID_ENCRYPTION_KEY",
|
||||
"uid_key"
|
||||
],
|
||||
"timeout_ms": 3000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
也可以不用改 JSON,改成 Deployment 环境变量:
|
||||
|
||||
```text
|
||||
IM_WORKBENCH_MANAGER_BASE_URL=https://admin.118im.com
|
||||
IM_WORKBENCH_MANAGER_SERVICE_NAME=im-workbench
|
||||
IM_WORKBENCH_MANAGER_SECRET=<填 manager-system/im-manager-secrets 中的 IM_CONFIG_API_SECRET>
|
||||
```
|
||||
|
||||
建议优先统一放到 `workbench.json`,因为当前 workbench 已经通过 Secret 挂载 JSON 配置。
|
||||
|
||||
## 5. 推荐操作流程
|
||||
|
||||
### 步骤 1:更新 manager Secret
|
||||
|
||||
将:
|
||||
|
||||
```text
|
||||
manager-system / im-manager-secrets / IM_CONFIG_API_SERVICE_NAME
|
||||
```
|
||||
|
||||
改为:
|
||||
|
||||
```text
|
||||
im-server,im-workbench
|
||||
```
|
||||
|
||||
`IM_CONFIG_API_SECRET` 保持现有值。
|
||||
|
||||
### 步骤 2:更新 workbench Secret
|
||||
|
||||
从:
|
||||
|
||||
```text
|
||||
manager-system / im-manager-secrets / IM_CONFIG_API_SECRET
|
||||
```
|
||||
|
||||
取值,写入:
|
||||
|
||||
```text
|
||||
workbench-system / im-workbench-secrets / workbench.json
|
||||
```
|
||||
|
||||
目标字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"manager": {
|
||||
"base_url": "https://admin.118im.com",
|
||||
"service_name": "im-workbench",
|
||||
"secret": "<IM_CONFIG_API_SECRET>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤 3:重启服务
|
||||
|
||||
更新 Secret 后需要重启 Pod,确保进程重新读取配置。
|
||||
|
||||
建议顺序:
|
||||
|
||||
```text
|
||||
1. 重启 im-manager
|
||||
2. 重启 im-workbench
|
||||
```
|
||||
|
||||
原因:
|
||||
|
||||
- 先让 manager 支持 `im-workbench` service name。
|
||||
- 再让 workbench 使用 `im-workbench` service name 去调用 manager。
|
||||
|
||||
## 6. 验证方法
|
||||
|
||||
### 6.1 验证 manager 是否接受 im-workbench
|
||||
|
||||
用 `IM_CONFIG_API_SECRET` 对请求签名,请求:
|
||||
|
||||
```text
|
||||
GET https://admin.118im.com/internal/im-config/third-party/im?keys=uid_encryption_key,UID_ENCRYPTION_KEY,uid_key
|
||||
X-Service-Name: im-workbench
|
||||
```
|
||||
|
||||
期望结果:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"values": {
|
||||
"uid_encryption_key": "..."
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果返回 401,重点检查:
|
||||
|
||||
- `IM_CONFIG_API_SERVICE_NAME` 是否包含 `im-workbench`
|
||||
- 请求里的 `X-Service-Name` 是否是 `im-workbench`
|
||||
- `IM_CONFIG_API_SECRET` 是否和 manager 当前 Secret 一致
|
||||
- 签名文本是否为:
|
||||
|
||||
```text
|
||||
METHOD
|
||||
RequestURI
|
||||
BodySHA256
|
||||
Timestamp
|
||||
Nonce
|
||||
```
|
||||
|
||||
### 6.2 验证 workbench 是否加载到 manager 配置
|
||||
|
||||
进入 workbench Pod 看环境和配置摘要,注意不要打印真实 secret:
|
||||
|
||||
```bash
|
||||
kubectl -n workbench-system exec <im-workbench-pod> -- sh -c 'env | grep IM_WORKBENCH_MANAGER'
|
||||
```
|
||||
|
||||
或查看挂载配置:
|
||||
|
||||
```bash
|
||||
kubectl -n workbench-system exec <im-workbench-pod> -- sh -c 'cat /etc/im-workbench/workbench.json'
|
||||
```
|
||||
|
||||
检查:
|
||||
|
||||
```text
|
||||
manager.base_url = https://admin.118im.com
|
||||
manager.service_name = im-workbench
|
||||
manager.secret 非空
|
||||
```
|
||||
|
||||
### 6.3 验证业务接口
|
||||
|
||||
从 IM 登录获取 token 后,请求 workbench 接口:
|
||||
|
||||
```http
|
||||
GET https://api.118im.com/v1/workbench
|
||||
Authorization: Bearer <IM token>
|
||||
X-MeetingIM-APIKey: <api key>
|
||||
X-IM-Schema: 1
|
||||
X-Tenant-Code: <tenant code>
|
||||
```
|
||||
|
||||
期望:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0
|
||||
}
|
||||
```
|
||||
|
||||
再创建会议,确认 user id 编解码和展示名正常:
|
||||
|
||||
```http
|
||||
POST https://api.118im.com/v1/workbench/meetings
|
||||
Idempotency-Key: <unique id>
|
||||
Authorization: Bearer <IM token>
|
||||
X-MeetingIM-APIKey: <api key>
|
||||
X-IM-Schema: 1
|
||||
X-Tenant-Code: <tenant code>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
请求体示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "运维验证会议",
|
||||
"start_at": "2026-09-18T08:00:00Z",
|
||||
"end_at": "2026-09-18T09:00:00Z",
|
||||
"location": "",
|
||||
"recorder_user_id": "usr...",
|
||||
"attendee_user_ids": ["usr..."],
|
||||
"reminder_minutes": []
|
||||
}
|
||||
```
|
||||
|
||||
期望:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"state": "scheduled",
|
||||
"organizer": {
|
||||
"user_id": "usr...",
|
||||
"display_name": "..."
|
||||
},
|
||||
"participant_names": ["..."]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 常见问题
|
||||
|
||||
### 7.1 为什么不能从 manager 读取 manager secret
|
||||
|
||||
因为 `IM_CONFIG_API_SECRET` 是调用 manager 配置接口的 HMAC 引导凭证。
|
||||
|
||||
如果要读取 manager 配置接口,必须先有这个 secret 才能签名。
|
||||
所以它不能从同一个 manager 配置接口中获取,只能来自部署环境,例如 Kubernetes Secret。
|
||||
|
||||
### 7.2 为什么 service name 不是 im-server
|
||||
|
||||
`X-Service-Name` 表示调用方服务名。
|
||||
|
||||
- `im_server` 调 manager 时,应使用 `im-server`
|
||||
- `im_workbench` 调 manager 时,应使用 `im-workbench`
|
||||
|
||||
manager 侧应该允许多个合法调用方:
|
||||
|
||||
```text
|
||||
im-server,im-workbench
|
||||
```
|
||||
|
||||
### 7.3 为什么配置了 .env 还是没用
|
||||
|
||||
因为 Kubernetes 不会自动读取仓库 `.env`。
|
||||
|
||||
`.env` 只有在以下情况才会生效:
|
||||
|
||||
- 程序代码主动 `godotenv.Load()`
|
||||
- 启动脚本 `source .env`
|
||||
- docker compose 配了 `env_file`
|
||||
- CI/CD 将 `.env` 内容写进 Deployment 或 Secret
|
||||
|
||||
当前测试环境都不是这些方式。
|
||||
|
||||
### 7.4 只改 workbench 会怎样
|
||||
|
||||
如果只把 workbench 改成:
|
||||
|
||||
```text
|
||||
X-Service-Name: im-workbench
|
||||
```
|
||||
|
||||
但 manager 仍然只允许:
|
||||
|
||||
```text
|
||||
IM_CONFIG_API_SERVICE_NAME=im-server
|
||||
```
|
||||
|
||||
那么 workbench 调 manager 会返回 401。
|
||||
|
||||
所以需要 manager 和 workbench 一起配置。
|
||||
|
||||
## 8. 最终配置清单
|
||||
|
||||
manager:
|
||||
|
||||
```text
|
||||
Namespace: manager-system
|
||||
Secret: im-manager-secrets
|
||||
|
||||
IM_CONFIG_API_SERVICE_NAME=im-server,im-workbench
|
||||
IM_CONFIG_API_SECRET=<保持现有值>
|
||||
```
|
||||
|
||||
workbench:
|
||||
|
||||
```text
|
||||
Namespace: workbench-system
|
||||
Secret: im-workbench-secrets
|
||||
Key: workbench.json
|
||||
```
|
||||
|
||||
`workbench.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"manager": {
|
||||
"base_url": "https://admin.118im.com",
|
||||
"service_name": "im-workbench",
|
||||
"secret": "<manager 的 IM_CONFIG_API_SECRET>",
|
||||
"uid_key_group": "im",
|
||||
"uid_key_keys": [
|
||||
"uid_encryption_key",
|
||||
"UID_ENCRYPTION_KEY",
|
||||
"uid_key"
|
||||
],
|
||||
"timeout_ms": 3000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,351 +0,0 @@
|
||||
# 工作台迁移对接总结文档
|
||||
|
||||
> 更新时间:2026-09-14。本文用于后续开启新的 `im_workbench` 工作树时快速接手,覆盖当前迁移边界、接口、鉴权、中间件配置、Kafka 事件、数据库现状和验收步骤。
|
||||
|
||||
## 1. 当前结论
|
||||
|
||||
工作台已从 `im_server` 拆到独立仓库 `im_workbench`,当前阶段只拆服务和接口边界,数据库暂不迁移。工作台接口统一走 `/v1/workbench/*`,由网关分流到 `im_workbench`;登录仍由 `im_server` 负责,登录成功后把 Token 会话写入 Redis,`im_workbench` 从 Redis 校验登录态。
|
||||
|
||||
当前已完成:
|
||||
|
||||
| 项目 | 状态 |
|
||||
|---|---|
|
||||
| 独立 Go 工程 | 已完成,仓库:`/Users/youshunlai/go_workspace/im_workbench` |
|
||||
| 独立启动 | 已完成,默认监听 `:6061` |
|
||||
| 新路由 `/v1/workbench/*` | 已完成 |
|
||||
| Redis Token 鉴权 | 已完成 |
|
||||
| 工作台 Kafka 事件生产 | 已完成 |
|
||||
| 测试环境中间件配置 | 已更新 |
|
||||
| 数据库迁移 | 暂不迁移,仍使用租户库 `tinode` |
|
||||
| `im_server` 旧工作台代码 | 暂保留,等新服务稳定后再删 |
|
||||
|
||||
最近关键提交:
|
||||
|
||||
| 仓库 | Commit | 说明 |
|
||||
|---|---|---|
|
||||
| `im_workbench` | `8b5c5e3` | 兼容当前工作台表结构 |
|
||||
| `im_workbench` | `c1c1156` | 更新新测试环境中间件配置,修复 Kafka 发布 |
|
||||
|
||||
## 2. 服务边界
|
||||
|
||||
### 2.1 im_workbench 负责
|
||||
|
||||
| 模块 | 接口范围 |
|
||||
|---|---|
|
||||
| 工作台首页 | `GET /v1/workbench` |
|
||||
| 组织架构 | `/v1/workbench/organization/*` |
|
||||
| 考勤 | `/v1/workbench/attendance/*` |
|
||||
| 会议 | `/v1/workbench/meetings*` |
|
||||
| 会议笔记 | `/v1/workbench/meeting-notes`、`/v1/workbench/meetings/{id}/notes*` |
|
||||
| 日报 | `/v1/workbench/reports/daily*` |
|
||||
| 月报 | `/v1/workbench/reports/monthly*` |
|
||||
| 总经理信箱 | `/v1/workbench/executive-mailbox/*` |
|
||||
| 工作台业务事件 | 生产 Kafka 事件到 `im.workbench.event` |
|
||||
|
||||
### 2.2 im_server 继续负责
|
||||
|
||||
| 能力 | 说明 |
|
||||
|---|---|
|
||||
| 登录、注册、Token 签发 | C 端登录仍走 `im_server` |
|
||||
| Redis 登录态写入 | 登录成功后写 `im:session:token:{sha256(token)}` |
|
||||
| 文件上传 | 暂由 `/v0/file/u/` 处理,工作台只保存 `file_id` |
|
||||
| WebSocket 实时下发 | 工作台事件由 `im_server` 消费后转 C 端通知 |
|
||||
| IM 消息、群、好友、在线状态 | 仍全部留在 `im_server` |
|
||||
|
||||
## 3. 网关路由
|
||||
|
||||
客户端对外仍访问统一域名,由 Nginx/API Gateway 按路径分流:
|
||||
|
||||
```text
|
||||
/v0/channels -> im_server
|
||||
/v0/file/u/ -> im_server
|
||||
/v1/im/* -> im_server
|
||||
/v1/workbench/* -> im_workbench
|
||||
```
|
||||
|
||||
测试环境域名:
|
||||
|
||||
| 域名 | 用途 |
|
||||
|---|---|
|
||||
| `https://api.118im.com` | 后端 API,包含 `/v1/workbench/*` |
|
||||
| `https://api-test.118im.com` | 测试 API 备用域名 |
|
||||
| `https://web.118im.com` | Web 前端同源代理 |
|
||||
|
||||
注意:不要再让 `im_server` 代理工作台新接口。新接口应直接进入 `im_workbench`。
|
||||
|
||||
## 4. 请求鉴权
|
||||
|
||||
工作台接口和 IM 产品 HTTP 保持同一套公共 Header:
|
||||
|
||||
| Header | 必填 | 说明 |
|
||||
|---|---|---|
|
||||
| `Authorization` | 是 | `Bearer {token}` |
|
||||
| `X-MeetingIM-APIKey` | 是 | 客户端 API Key |
|
||||
| `X-Tenant-Code` | 是 | 当前租户编码,例如 `default` |
|
||||
| `X-IM-Schema` | 是 | 当前固定 `1` |
|
||||
| `X-Request-ID` | 建议必填 | 请求链路 ID |
|
||||
| `Idempotency-Key` | 写接口必填 | 写操作幂等键 |
|
||||
|
||||
登录态 Redis Key:
|
||||
|
||||
```text
|
||||
im:session:token:{sha256(token)}
|
||||
```
|
||||
|
||||
Redis Value:
|
||||
|
||||
```json
|
||||
{
|
||||
"user_id": 1572927143048908800,
|
||||
"tenant_id": 1,
|
||||
"tenant_code": "default",
|
||||
"device_id": "ios-device-1",
|
||||
"auth_level": "auth",
|
||||
"state": "active",
|
||||
"issued_at": "2026-09-14T10:00:00Z",
|
||||
"expires_at": "2026-09-14T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
鉴权流程:
|
||||
|
||||
1. `im_workbench` 校验 `X-MeetingIM-APIKey` 非空或命中配置白名单。
|
||||
2. 从 `Authorization` 读取 Bearer Token。
|
||||
3. 对 Token 做 `sha256`,查 Redis 登录态。
|
||||
4. 校验 `state=active`、未过期。
|
||||
5. 校验 Header 的 `X-Tenant-Code` 与登录态 `tenant_code` 一致。
|
||||
|
||||
测试环境当前可用 API Key 以部署文档为准,最近验证可用的是新测试环境文档中的 `TINODE_API_KEY`。
|
||||
|
||||
## 5. 测试环境配置
|
||||
|
||||
配置文件:
|
||||
|
||||
```text
|
||||
/Users/youshunlai/go_workspace/im_workbench/configs/workbench.test.json
|
||||
```
|
||||
|
||||
当前关键配置:
|
||||
|
||||
| 配置 | 值 |
|
||||
|---|---|
|
||||
| 服务监听 | `:6061` |
|
||||
| MySQL | `mysql.118im.com:30306` |
|
||||
| MySQL 业务库 | `tinode` |
|
||||
| Redis 数据端口 | `redis.118im.com:30637` |
|
||||
| Redis Sentinel | `redis.118im.com:30638` |
|
||||
| Kafka Bootstrap | `kafka.118im.com:30835` |
|
||||
| Kafka 协议 | `PLAINTEXT` |
|
||||
| Kafka Topic | `im.workbench.event` |
|
||||
|
||||
说明:
|
||||
|
||||
1. Kafka 测试环境是明文直连,不配置 SASL/TLS。
|
||||
2. `im.workbench.event` 已在新 Kafka 集群创建。
|
||||
3. Redis 数据 NodePort 可能落到主节点或副本。服务正常读登录态没问题;如果本地要写临时测试 session,可能遇到 `READONLY`,需要通过真正主库入口或先走真实登录链路。
|
||||
4. MySQL、Redis 密码不写入本文,按 `im_server/docs/problems/新测试环境部署与中间件连接信息.md` 或部署 Secret 获取。
|
||||
|
||||
## 6. 本地启动
|
||||
|
||||
本地开发配置:
|
||||
|
||||
```bash
|
||||
IM_WORKBENCH_CONFIG=configs/workbench.local.json go run ./cmd/workbench
|
||||
```
|
||||
|
||||
连接测试环境配置:
|
||||
|
||||
```bash
|
||||
IM_WORKBENCH_CONFIG=configs/workbench.test.json go run ./cmd/workbench
|
||||
```
|
||||
|
||||
如果只做本地接口调试,可以临时打开开发 Header 鉴权:
|
||||
|
||||
```bash
|
||||
IM_WORKBENCH_CONFIG=configs/workbench.test.json \
|
||||
IM_WORKBENCH_ALLOW_DEV_HEADER_AUTH=true \
|
||||
go run ./cmd/workbench
|
||||
```
|
||||
|
||||
开发 Header:
|
||||
|
||||
```text
|
||||
X-User-ID: 1572927143048908800
|
||||
X-Tenant-ID: 1
|
||||
X-Tenant-Code: default
|
||||
X-MeetingIM-APIKey: x
|
||||
X-IM-Schema: 1
|
||||
X-Request-ID: {uuid}
|
||||
```
|
||||
|
||||
注意:`IM_WORKBENCH_ALLOW_DEV_HEADER_AUTH=true` 只能本地调试使用,测试/生产部署不要开启。
|
||||
|
||||
## 7. Kafka 事件
|
||||
|
||||
工作台写操作统一投递:
|
||||
|
||||
```text
|
||||
Topic: im.workbench.event
|
||||
```
|
||||
|
||||
事件体:
|
||||
|
||||
```json
|
||||
{
|
||||
"event_id": "wbe_xxx",
|
||||
"trace_id": "request-id",
|
||||
"tenant_id": 1,
|
||||
"tenant_code": "default",
|
||||
"actor_user_id": 42,
|
||||
"module": "meeting",
|
||||
"action": "meeting.created",
|
||||
"resource_id": "mtg_xxx",
|
||||
"target_user_ids": [42, 43],
|
||||
"title": "会议通知",
|
||||
"summary": "项目周会",
|
||||
"occurred_at": "2026-09-14T06:00:00Z",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
当前事件范围:
|
||||
|
||||
| 模块 | Action | 触发场景 |
|
||||
|---|---|---|
|
||||
| 考勤 | `attendance.record.updated` | 打卡成功 |
|
||||
| 考勤 | `attendance.appeal.created` | 提交考勤申诉 |
|
||||
| 会议 | `meeting.created` | 创建会议 |
|
||||
| 会议 | `meeting.updated` | 修改会议 |
|
||||
| 会议 | `meeting.canceled` | 取消会议 |
|
||||
| 会议 | `meeting.response.updated` | 更新参会回执 |
|
||||
| 会议笔记 | `meeting_note.file.created` | 新增会议笔记文件 |
|
||||
| 会议笔记 | `meeting_note.file.replaced` | 替换会议笔记文件 |
|
||||
| 会议笔记 | `meeting_note.file.deleted` | 删除会议笔记文件 |
|
||||
| 日报 | `daily_report.draft.created` | 创建日报草稿 |
|
||||
| 日报 | `daily_report.draft.saved` | 保存日报草稿 |
|
||||
| 日报 | `daily_report.submitted` | 提交日报 |
|
||||
| 日报 | `daily_report.reminded` | 日报提醒 |
|
||||
| 月报 | `monthly_report.draft.created` | 创建月报草稿 |
|
||||
| 月报 | `monthly_report.draft.saved` | 保存月报草稿 |
|
||||
| 月报 | `monthly_report.submitted` | 提交月报 |
|
||||
| 月报 | `monthly_report.reopened` | 重开月报 |
|
||||
| 信箱 | `mailbox.submitted` | 提交总经理信箱 |
|
||||
|
||||
已修复问题:`kafka.Writer` 设置了默认 Topic 后,单条 `kafka.Message` 不再重复设置 Topic,否则 `kafka-go` 会报 `Topic must not be specified for both Writer and Message`。
|
||||
|
||||
## 8. 数据库现状
|
||||
|
||||
当前数据库不迁移,`im_workbench` 仍连接租户库 `tinode`。
|
||||
|
||||
主要表:
|
||||
|
||||
| 表 | 用途 |
|
||||
|---|---|
|
||||
| `im_workbench_apps` | 工作台模块 |
|
||||
| `im_org_departments` | 组织部门 |
|
||||
| `im_org_members` | 组织成员 |
|
||||
| `im_org_visibility_overrides` | 组织可见范围 |
|
||||
| `im_attendance_rules` | 考勤规则 |
|
||||
| `im_attendance_shifts` | 考勤班次 |
|
||||
| `im_attendance_records` | 考勤记录 |
|
||||
| `im_attendance_checkins` | 打卡流水 |
|
||||
| `im_attendance_appeals` | 考勤申诉 |
|
||||
| `im_meetings` | 会议 |
|
||||
| `im_meeting_participants` | 会议参与人 |
|
||||
| `im_meeting_note_files` | 会议笔记文件引用 |
|
||||
| `im_daily_reports` | 日报 |
|
||||
| `im_monthly_reports` | 月报 |
|
||||
| `im_mailbox_config` | 总经理信箱配置 |
|
||||
| `im_mailbox_submissions` | 总经理信箱提交 |
|
||||
|
||||
已兼容当前表结构:
|
||||
|
||||
| 表 | 当前结构注意点 | 代码处理 |
|
||||
|---|---|---|
|
||||
| `im_org_members` | 成员状态字段是 `employment_state` | 查询时返回为 `status` |
|
||||
| `im_attendance_rules` | 没有 `appeal_deadline` 字段 | 查询返回 `NULL AS appeal_deadline` |
|
||||
| `im_mailbox_config` | 没有 `version` 字段 | 用 `notice_updated_at` 派生版本 |
|
||||
|
||||
服务启动不自动建库建表,不执行 DDL。所有建表、改字段、初始化数据都走人工 SQL 审核和发布。
|
||||
|
||||
## 9. 已验证接口
|
||||
|
||||
使用新测试环境配置启动本地 `im_workbench`,并开启本地开发 Header 鉴权后,已验证:
|
||||
|
||||
| 接口 | 结果 |
|
||||
|---|---|
|
||||
| `GET /v1/workbench` | 200 |
|
||||
| `GET /v1/workbench/organization/departments` | 200 |
|
||||
| `GET /v1/workbench/attendance/config` | 200 |
|
||||
| `GET /v1/workbench/executive-mailbox/config` | 200 |
|
||||
| `POST /v1/workbench/executive-mailbox/submissions` | 200,Kafka 发布成功 |
|
||||
|
||||
同时执行:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
结果通过。
|
||||
|
||||
## 10. 新工作树接手步骤
|
||||
|
||||
1. 拉取 `im_workbench` 的 `dev` 分支。
|
||||
2. 确认包含提交 `c1c1156` 或更新的提交。
|
||||
3. 查看 `configs/workbench.test.json`,确认中间件地址是新测试环境域名,不是旧 VM `64.118.137.182`。
|
||||
4. 执行 `go test ./...`。
|
||||
5. 本地启动:
|
||||
|
||||
```bash
|
||||
IM_WORKBENCH_CONFIG=configs/workbench.test.json \
|
||||
IM_WORKBENCH_ALLOW_DEV_HEADER_AUTH=true \
|
||||
go run ./cmd/workbench
|
||||
```
|
||||
|
||||
6. 用开发 Header 调 `GET /v1/workbench`、组织、考勤、信箱配置。
|
||||
7. 调一个写接口,例如信箱提交,确认返回 200,并在数据库清理测试数据。
|
||||
8. 关闭开发 Header 鉴权,走真实登录 Token 验证网关链路。
|
||||
9. 验证公网 `/v1/workbench/*` 路由已经进入 `im_workbench`,并确认网关透传 `Authorization`、`X-MeetingIM-APIKey`、`X-Tenant-Code`、`X-IM-Schema`、`X-Request-ID`。
|
||||
|
||||
## 11. 待后续处理
|
||||
|
||||
| 事项 | 说明 |
|
||||
|---|---|
|
||||
| 真实登录态联调 | 需要通过 `im_server` 登录后生成 Redis session,再从公网 `/v1/workbench/*` 验证 |
|
||||
| 网关 Header 透传 | 必须确认 `Authorization` 没被 Nginx/API Gateway 清掉 |
|
||||
| Redis 写入口 | 数据 NodePort 可能落副本,本地造 session 可能遇到 `READONLY` |
|
||||
| im_server 消费工作台事件 | `im_server` 消费 `im.workbench.event` 后转系统通知/WS 下发需要继续联调 |
|
||||
| 工作台数据库迁移 | 现阶段不迁,接口稳定后单独做工作台库迁移 |
|
||||
| 删除 im_server 旧工作台代码 | 等新服务、网关、前端、通知链路全部稳定后再删 |
|
||||
|
||||
## 12. 常见问题
|
||||
|
||||
### 12.1 `/v1/workbench` 返回 `AUTH_REQUIRED`
|
||||
|
||||
优先检查:
|
||||
|
||||
1. 是否带 `Authorization: Bearer {token}`。
|
||||
2. Redis 是否有 `im:session:token:{sha256(token)}`。
|
||||
3. session 是否 `state=active` 且未过期。
|
||||
4. `X-Tenant-Code` 是否和 session 中的 `tenant_code` 一致。
|
||||
5. 网关是否透传 `Authorization`。
|
||||
6. `im_workbench` 是否连到了和 `im_server` 相同的 Redis。
|
||||
|
||||
### 12.2 写接口返回 `workbench event publish failed`
|
||||
|
||||
优先检查:
|
||||
|
||||
1. Kafka broker 是否为 `kafka.118im.com:30835`。
|
||||
2. Kafka 是否为 `PLAINTEXT`,不要配置 SASL。
|
||||
3. Topic `im.workbench.event` 是否存在。
|
||||
4. 服务代码是否已经包含 Kafka 双 Topic 修复。
|
||||
|
||||
### 12.3 组织或考勤接口 500
|
||||
|
||||
优先检查当前表结构是否和代码兼容:
|
||||
|
||||
1. `im_org_members` 使用 `employment_state`,不是 `status`。
|
||||
2. `im_attendance_rules` 当前没有 `appeal_deadline`。
|
||||
3. `im_mailbox_config` 当前没有 `version`。
|
||||
|
||||
这些兼容已在当前代码中处理;如果新工作树仍 500,先确认是否拉到最新提交。
|
||||
@@ -0,0 +1,101 @@
|
||||
# 注册登录对接文档
|
||||
|
||||
接口公共前缀为 `/v1`,旧的应用名路径前缀不再提供兼容路由。
|
||||
|
||||
## 1 数据模型
|
||||
|
||||
账户以 `olons_admin.hg_app_member` 为主档:
|
||||
|
||||
- 手机号写入 `mobile_cipher`、`mobile_hash` 和 `mobile_mask`。
|
||||
- 登录密码使用 bcrypt 写入 `password_hash`。
|
||||
- 推荐码通过上级会员的 `invite_code` 建立 `parent_id`、`level` 和 `tree`。
|
||||
- 注册事务同时创建 `hg_app_member_profile` 和 CNY `hg_wallet_account`。
|
||||
|
||||
## 2 公共 Header
|
||||
|
||||
```http
|
||||
X-API-Key: <api key>
|
||||
X-Request-ID: <request id>
|
||||
X-Device-ID: <optional device id>
|
||||
```
|
||||
|
||||
## 3 注册
|
||||
|
||||
```http
|
||||
POST /v1/auth/register
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "alice01",
|
||||
"phone": "13800138000",
|
||||
"password": "Secret123",
|
||||
"referral_code": "ABCD2345",
|
||||
"privacy_accepted": true
|
||||
}
|
||||
```
|
||||
|
||||
`referral_code` 可选;传入时必须匹配当前租户下正常会员的 `invite_code`。
|
||||
|
||||
## 4 登录
|
||||
|
||||
```http
|
||||
POST /v1/auth/login
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"phone": "13800138000",
|
||||
"password": "Secret123"
|
||||
}
|
||||
```
|
||||
|
||||
界面的“记住密码”由客户端安全存储,不作为接口字段上传。
|
||||
|
||||
## 5 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"member_id": 9,
|
||||
"member_no": "MABCDEFGHIJKLMNOPQRST",
|
||||
"username": "alice01",
|
||||
"phone_mask": "138****8000",
|
||||
"invite_code": "EFGH6789",
|
||||
"token": "...",
|
||||
"token_type": "Bearer",
|
||||
"expires_at": "2026-10-23T10:00:00Z"
|
||||
},
|
||||
"request_id": "uuid",
|
||||
"server_time": "2026-10-09T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
Token 是 32 字节随机会话凭证的 Base64URL 编码。Redis 只使用 Token SHA-256 摘要作为 Key,不存储原始 Token。
|
||||
|
||||
## 6 退出登录
|
||||
|
||||
```http
|
||||
POST /v1/auth/logout
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
退出成功后删除当前 Redis 会话,`data` 为 `null`。缺少、格式错误或已失效的 Token 返回 HTTP 401 和 `AUTH_REQUIRED`。
|
||||
|
||||
## 7 错误 reason
|
||||
|
||||
| reason | HTTP | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `USERNAME_INVALID_FORMAT` | 400 | 账户名格式错误 |
|
||||
| `PHONE_INVALID_FORMAT` | 400 | 手机号格式错误 |
|
||||
| `PASSWORD_INVALID_FORMAT` | 400 | 密码格式错误 |
|
||||
| `REFERRAL_CODE_INVALID` | 400 | 推荐码无效 |
|
||||
| `PRIVACY_POLICY_NOT_ACCEPTED` | 400 | 未同意隐私政策 |
|
||||
| `ACCOUNT_ALREADY_REGISTERED` | 409 | 账户名或手机号已注册 |
|
||||
| `INVALID_CREDENTIALS` | 401 | 手机号或密码错误 |
|
||||
| `AUTH_REQUIRED` | 401 | 缺少或无效的登录会话 |
|
||||
| `RATE_LIMITED` | 429 | 15 分钟内登录失败次数过多 |
|
||||
| `SERVICE_UNAVAILABLE` | 503 | Redis 或敏感数据密钥未正确配置 |
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 91 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
@@ -0,0 +1,368 @@
|
||||
# 移动端任务与账户平台需求文档
|
||||
|
||||
**参考站点功能拆解与重构建议**
|
||||
|
||||
目标:在不复制品牌素材、敏感数据与高风险业务表述的前提下,复刻参考站点的信息架构和交互骨架,并形成可开发、可验收、可审计的产品需求基线。
|
||||
|
||||
主要读者:产品负责人、交互设计师、前端开发、后端开发、测试、风控与运营。
|
||||
|
||||
观察日期:2026-10-07 参考环境:移动端网页 文档版本:V1.0
|
||||
|
||||
结论:参考站点由账户、任务订单、余额台账、储入取出、会员等级、内容展示和在线客服七个域构成。建议复刻页面结构与业务闭环,但重写资金、隐私、安全和合规机制。
|
||||
|
||||
# 1 文档范围与方法
|
||||
|
||||
本需求基于登录后的页面逐项浏览、按钮与导航行为检查、页面状态观察,以及浏览器中可见网络请求和前端资源分析。本文把结论分为三类:实测行为、前端可见实现、建议需求。敏感手机号、邀请码、银行卡等信息已脱敏,不作为原型内容。
|
||||
|
||||
## 1.1 已覆盖范围
|
||||
|
||||
- 登录页现状、注册页字段、会话与路由保护。
|
||||
- 首页、荣誉榜、会员等级、平台简介、规则说明、代理合作、公司资质。
|
||||
- 订单记录筛选、领取中心、订单资金变化、手工商城。
|
||||
- 储入、取出、收款信息绑定、储入记录、取出记录、账户明细。
|
||||
- 个人信息、头像上传、登录密码、交易密码、通知记录、在线客服。
|
||||
- 可观察接口、响应结构、关键数据字段与前端安全实现。
|
||||
|
||||
## 1.2 未执行操作
|
||||
|
||||
为避免生成真实订单、变更资金、上传个人文件、修改密码或退出当前会话,本文未点击自动领取、订单提交、储入提交、取出确认、收款信息提交、头像上传、密码提交和退出登录。对应需求由页面字段、前端接口定义与上下游数据状态推导,并标记为待联调确认。
|
||||
|
||||
# 2 产品定位与角色
|
||||
|
||||
产品可定位为移动端会员任务与佣金账户平台。用户通过注册、储入余额、领取任务订单、完成任务、获得本金返还与佣金,并可查看台账或发起取出。平台通过等级、任务数量和费率管理用户权益,通过客服处理储入、异常订单和账户问题。
|
||||
|
||||
| **角色** | **主要目标** | **关键权限** |
|
||||
| --- | --- | --- |
|
||||
| 游客 | 了解平台并注册 | 登录、注册、查看隐私政策 |
|
||||
| 会员 | 领取任务并查看收益 | 首页、任务、订单、余额、客服、个人设置 |
|
||||
| 高级会员 | 按等级获得更多任务和更高费率 | 等级权益、额度与次数受配置控制 |
|
||||
| 客服 | 处理储入、取出、异常任务 | 会话、工单、用户与交易查询 |
|
||||
| 运营 | 管理内容、等级、任务和公告 | 配置、发布、审核、数据看板 |
|
||||
| 财务与风控 | 审核资金和异常行为 | 充值审核、提现审核、台账、风控规则 |
|
||||
| 管理员 | 维护权限与系统参数 | RBAC、审计日志、系统配置 |
|
||||
|
||||
# 3 信息架构与页面路由
|
||||
|
||||
| **一级模块** | **页面或路由** | **核心内容** | **入口** |
|
||||
| --- | --- | --- | --- |
|
||||
| 认证 | /login /register | 手机号登录、账户名、推荐码、验证码 | 未登录访问 |
|
||||
| 首页 | /home | 活动图、快捷入口、余额、荣誉榜、会员等级、公司介绍 | 底部首页 |
|
||||
| 任务 | /receive /orderDetail | 领取、等待、任务详情、提交完成 | 底部领取 |
|
||||
| 记录 | 记录页 | 全部、待处理、已完成订单 | 底部记录 |
|
||||
| 手工 | /mall | 手工任务卡片并引导客服 | 首页快捷入口 |
|
||||
| 资金 | /recharge /withdrawal | 储入、取出、余额、交易密码 | 首页与个人中心 |
|
||||
| 账户 | /mine /personalInfo | 用户资料、信用分、邀请码、设置 | 底部我的 |
|
||||
| 明细 | /depositRecord /withdrawalRecord /bill | 储入、取出、余额台账 | 我的 |
|
||||
| 客服 | /customerService /iframeCustom | 客服列表与第三方会话 | 底部客服 |
|
||||
| 内容 | /html/{id} | 平台简介、规则、代理、资质 | 首页公司简介 |
|
||||
|
||||
## 3.1 建议导航原则
|
||||
|
||||
- 底部五栏固定为首页、记录、领取、客服、我的;详情页隐藏底部栏并保留明确返回。
|
||||
- 资金页面必须显示币种、可用余额、冻结余额、预计到账和手续费,不使用含义不明的“人民币编码”。
|
||||
- 所有金额变动都能跳转至同一业务单据,并在台账中保持可解释、可追溯。
|
||||
- 客服 iframe 需明确第三方来源、隐私边界和会话状态,失败时提供备用联系方式。
|
||||
|
||||
# 4 核心业务闭环
|
||||
|
||||
## 4.1 任务订单闭环
|
||||
|
||||
1. 用户进入领取中心,系统校验登录态、营业时间、会员等级、最低可用余额、未完成任务与风控状态。
|
||||
1. 用户点击领取后,系统创建或匹配一笔任务订单,返回任务编号、商品、金额、佣金、截止时间与状态。
|
||||
1. 任务确认时,台账先记录交易扣款;完成后记录本金返还和佣金入账。参考数据中同一业务单号出现 -298、+298、+14.9 三条流水。
|
||||
1. 订单必须支持待领取、匹配中、待处理、处理中、已完成、超时、冻结、取消与失败,并记录每次状态变化。
|
||||
1. 任何补差额、冻结或信用分变化都必须有明确原因、复核入口和申诉渠道;不得依赖口头客服指令。
|
||||
|
||||
## 4.2 储入与取出闭环
|
||||
|
||||
储入页面按等级展示金额、赠送额、每日单数和佣金比例,用户填写金额与姓名后提交。参考站点还要求联系客服获取收款方式。新系统应改为平台内生成可核验的支付订单,禁止客服通过聊天发送临时个人账户。
|
||||
|
||||
取出前需绑定姓名、银行名、银行账户与收款码,随后输入金额和交易密码。新系统应只保留与实际支付通道必要的字段,敏感数据加密存储,收款码上传须做病毒扫描、内容校验与访问控制。
|
||||
|
||||
## 4.3 会员与代理闭环
|
||||
|
||||
参考站点设置普通、白银、黄金、铂金、钻石、星耀、至尊七档,充值门槛、赠送额、每日单数和佣金比例逐级上升,并存在三级推荐奖励。建议将等级规则做成版本化配置,禁止前端写死;代理奖励必须在合规评估通过后再启用。
|
||||
|
||||
# 5 页面级功能需求
|
||||
|
||||
## 5.1 登录与注册
|
||||
|
||||
| **页面** | **字段与控件** | **校验与行为** | **验收要点** |
|
||||
| --- | --- | --- | --- |
|
||||
| 登录 | 手机号、密码、记住密码、登录、去注册、隐私政策 | 手机号格式;密码非空;错误次数限制;登录成功进入首页 | 不在本地明文保存密码;支持会话过期提示 |
|
||||
| 注册 | 账户名、11 位手机号、密码、推荐码、图片验证码、隐私政策 | 账户名必填;手机号唯一;验证码校验;推荐码可选或按运营配置 | 提交前明确同意协议;防批量注册;注册成功自动登录或返回登录 |
|
||||
| 会话 | Authorization 令牌 | 401 清除会话并返回登录;403 显示可解释错误 | 支持刷新、撤销、设备管理与退出所有设备 |
|
||||
|
||||
## 5.2 首页
|
||||
|
||||
- 展示轮播图或单图运营位,支持后台排序、上下线、跳转类型和有效期。
|
||||
- 快捷入口为领取、储入、取出、手工;按后台开关显示,关闭时给出原因而不是静默失效。
|
||||
- 账户卡展示昵称、可用余额与币种;点击可进入账户明细。
|
||||
- 荣誉榜为脱敏昵称、积分和结果状态,要求数据真实性标识、刷新频率与隐私策略。
|
||||
- 会员等级展示当前等级、费率和下一等级条件;等级图不得只是一张不可访问的图片。
|
||||
- 公司简介包括平台简介、规则说明、代理合作和公司资质,内容由 CMS 管理并保留版本。
|
||||
|
||||
## 5.3 记录与订单详情
|
||||
|
||||
记录页显示可用资产,提供全部、待处理、已完成三种筛选。列表项包括创建时间、商品图、商品名、商品价格、佣金与状态。分页默认 10 条,支持下拉刷新、空状态、加载失败重试和详情跳转。
|
||||
|
||||
实测发现先切换待处理、再切换已完成时曾短暂出现空列表,而重新加载全部后可见已完成记录。验收时需覆盖筛选参数、缓存状态和并发请求覆盖问题。
|
||||
|
||||
| **状态** | **用户可见文案** | **允许操作** | **系统约束** |
|
||||
| --- | --- | --- | --- |
|
||||
| 匹配中 | 正在匹配任务 | 取消等待 | 只允许一个匹配请求 |
|
||||
| 待处理 | 请在截止时间前完成 | 查看详情、提交 | 倒计时与服务端时间一致 |
|
||||
| 处理中 | 已提交,等待确认 | 查看详情、联系客服 | 防重复提交 |
|
||||
| 已完成 | 本金与佣金已入账 | 查看流水 | 三笔或组合流水必须可核对 |
|
||||
| 超时或冻结 | 显示原因与处理路径 | 申诉、联系客服 | 需要人工复核和审计 |
|
||||
| 失败或取消 | 显示失败原因 | 重新领取 | 不得产生不一致余额 |
|
||||
|
||||
## 5.4 领取中心
|
||||
|
||||
领取中心包含会员等级、佣金费率、主领取按钮、匹配进度、今日战果和规则说明。点击主按钮前需完成资格预检;按钮点击后立即进入不可重复提交的 loading 状态;匹配成功跳转订单详情,失败显示可恢复原因。
|
||||
|
||||
- 预检项:营业时间、账户状态、信用或风控状态、最低余额、当日次数、未完成订单、等级商品范围。
|
||||
- 统计项:总资产、昨日佣金、今日佣金、今日领取数;所有统计口径需要后台统一定义。
|
||||
- 规则文案需要 CMS 管理,禁止把风控与资金规则硬编码在页面。
|
||||
|
||||
## 5.5 手工商城
|
||||
|
||||
页面以双列卡片展示图片、任务名称、份数、工资与已领取数;点击卡片实测直接进入客服页。建议增加任务详情、适用人群、交付方式、预计周期、库存、审核标准和风险提示,再由用户主动发起咨询。
|
||||
|
||||
参考站点一条商品名称中可见脚本标签文本,说明内容过滤不足。新系统必须对运营内容做 HTML 转义、富文本白名单、图片域名白名单和持久型 XSS 检测。
|
||||
|
||||
## 5.6 客服
|
||||
|
||||
客服页展示多个客服入口与工作时间,点击后在站内 iframe 打开第三方聊天。聊天页含历史会话、系统消息、文本框、图片与视频上传。需求上应增加服务状态、排队人数、会话编号、隐私声明、附件大小与类型限制、敏感信息提醒和会话评价。
|
||||
|
||||
## 5.7 我的与个人信息
|
||||
|
||||
| **功能** | **需求** | **边界或风险** |
|
||||
| --- | --- | --- |
|
||||
| 资料概览 | 头像、昵称、邀请码、会员等级、信用状态、余额 | 手机号等敏感字段默认脱敏 |
|
||||
| 储入与取出 | 提供余额入口与状态提示 | 资金操作必须二次确认与风控 |
|
||||
| 收款信息 | 姓名、银行、账户、可选支付通道 | 加密、最小化收集、修改留痕 |
|
||||
| 头像 | 上传、裁剪、预览、替换 | 文件扫描、大小和格式校验 |
|
||||
| 密码 | 旧密码、新密码、确认密码 | 强度、频控、成功后令牌失效 |
|
||||
| 交易密码 | 独立于登录密码 | 不得用无盐 MD5;失败次数锁定 |
|
||||
| 通知 | 公告列表、未读、已读、详情 | 重要规则变更需留存阅读证据 |
|
||||
| 退出 | 清除令牌和本地用户数据 | 保留确认,避免误触 |
|
||||
|
||||
## 5.8 储入
|
||||
|
||||
页面展示等级规则表和金额、姓名输入框。参考规则为普通 500、白银 1888、黄金 10000、铂金 30000、钻石 50000、星耀 88888、至尊 188888;费率从 1.8% 到 5%。规则说明和表格存在个别口径差异,必须以后台版本化配置为唯一事实源。
|
||||
|
||||
- 提交前展示收款主体、订单号、有效期、金额、手续费和风险提示。
|
||||
- 储入订单状态:待支付、待审核、成功、失败、过期、取消;失败必须提供原因。
|
||||
- 若需要上传付款凭证,必须经过用户明确选择,并限制为图片或 PDF。
|
||||
|
||||
## 5.9 取出与收款绑定
|
||||
|
||||
取出页面展示余额、金额、已绑定收款信息摘要和交易密码。若未绑定,弹窗提示并引导至绑定页。绑定页实测字段为姓名、银行名、银行账户、人民币编码、支付宝收款码和微信收款码。建议删除含义不清或非必要字段,并根据支付渠道动态展示。
|
||||
|
||||
- 金额校验:大于 0、不超过可用余额、符合单笔最小值和最大值,并显示手续费与到账金额。
|
||||
- 状态:待审核、审核中、处理中、成功、失败、驳回、取消;用户可查看原因和预计时间。
|
||||
- 交易密码只在提交时使用;服务端采用强哈希校验并配合频率限制、设备风控和二次验证。
|
||||
|
||||
## 5.10 账户明细与记录
|
||||
|
||||
账户明细需按业务单号聚合展示变动类型、变动金额、变动前余额、变动后余额、时间、状态和关联单据。储入记录显示订单号、状态、金额、创建时间;取出记录字段同构,并增加手续费、到账金额与失败原因。
|
||||
|
||||
# 6 数据模型与状态机
|
||||
|
||||
| **实体** | **关键字段** | **说明** |
|
||||
| --- | --- | --- |
|
||||
| User | id, username, mobile, avatar, status, inviteCode | 用户主档,手机号单独加密与索引 |
|
||||
| Membership | levelId, rate, dailyLimit, withdrawLimits, effectiveAt | 版本化等级权益 |
|
||||
| Wallet | available, frozen, currency, version | 使用乐观锁或账户序列保证一致性 |
|
||||
| LedgerEntry | bizNo, type, direction, amount, before, after | 不可变台账,支持幂等与冲正 |
|
||||
| TaskOrder | orderNo, productId, amount, commission, status, deadline | 任务生命周期 |
|
||||
| DepositOrder | depositNo, amount, channel, status, proofUrl | 储入生命周期 |
|
||||
| WithdrawalOrder | withdrawNo, amount, fee, netAmount, status | 取出生命周期 |
|
||||
| PayoutProfile | realName, bankName, maskedAccount, qrAssetIds | 敏感字段加密与脱敏 |
|
||||
| Content | type, title, body, version, status, publishedAt | 公告、规则与公司资料 |
|
||||
| SupportSession | sessionNo, provider, status, openedAt, closedAt | 第三方客服映射 |
|
||||
| AuditLog | actor, action, target, before, after, ip, device | 资金与权限操作强制留痕 |
|
||||
|
||||
## 6.1 幂等与一致性
|
||||
|
||||
- 领取、提交订单、储入回调、取出申请和密码修改都必须使用幂等键。
|
||||
- 钱包余额只能由服务端台账事务修改;前端显示值不参与结算。
|
||||
- 订单状态转换由服务端状态机控制,不允许客户端任意设置。
|
||||
- 重复回调、网络重试和并发点击不得重复扣款、返款或发佣金。
|
||||
- 所有涉及金额的接口返回业务单号和最新余额版本。
|
||||
|
||||
# 7 接口需求
|
||||
|
||||
下表分为实测请求与前端资源中可见接口。实测接口在浏览器中返回 HTTP 200,响应外层为 Base64 字符串,解码后为 JSON,统一包含 success、code、data、msg。建议新系统直接使用标准 JSON,不把 Base64 当作安全措施。
|
||||
|
||||
| **方法** | **接口** | **用途** | **主要请求或返回字段** | **证据** |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| POST | /wap-api/doLogin | 登录 | username, password;token, users | 前端实现与已发生请求 |
|
||||
| POST | /wap-api/doRegister | 注册 | nickName, username, password, inviteCode, code, uuid | 前端实现 |
|
||||
| GET | /wap-api/captchaImage | 图片验证码 | uuid, img, captchaBol | 前端实现 |
|
||||
| GET | /wap-api/profile | 用户概要 | tel, username, headpic, inviteCode, level, levelRate, balance, xyf | 实测 |
|
||||
| GET | /wap-api/banner/list | 首页横幅 | 图片地址数组 | 实测 |
|
||||
| GET | /wap-api/home | 首页开关与内容编号 | SHOP_STATUS, TASK, POP_MESSAGE, 内容 ID | 实测 |
|
||||
| GET | /wap-api/user/data/home | 荣誉榜 | name, avatar, balance | 实测 |
|
||||
| GET | /wap-api/ctrl/level-list | 等级配置 | name, orderNum, num, giveNum, bili, 提现限制 | 实测 |
|
||||
| GET | /wap-api/support | 客服列表 | qq, url, workTime, 分页 | 实测 |
|
||||
| GET | /wap-api/convey/page | 订单分页 | pageNo, pageSize, status;订单与商品字段 | 实测 |
|
||||
| POST | /wap-api/convey/order | 领取或匹配订单 | 无显式参数;返回订单 | 前端实现,未执行 |
|
||||
| POST | /wap-api/convey/order/submit/{id} | 提交任务 | 订单 id | 前端实现,未执行 |
|
||||
| GET | /wap-api/convey/count | 领取统计 | balance, countToday, countNumToday, countNumYesterday, content | 实测 |
|
||||
| GET | /wap-api/convey/unpay | 未完成订单检查 | djs 等状态 | 实测 |
|
||||
| GET | /wap-api/product/page | 手工商品分页 | 分页条件;商品卡片 | 前端实现 |
|
||||
| GET | /wap-api/platform-config/trade-wait-time | 任务等待时间 | 秒数或分钟数 | 实测 |
|
||||
| GET | /wap-api/bank/current/info | 当前收款信息 | 收款资料摘要 | 前端实现 |
|
||||
| POST | /wap-api/bank/save | 保存收款信息 | 姓名、银行、账户、收款码 | 前端实现,未执行 |
|
||||
| POST | /wap-api/recharge/recharge | 创建储入申请 | amount, name 等 | 前端实现,未执行 |
|
||||
| GET | /wap-api/recharge/page | 储入记录分页 | 分页与状态 | 前端实现 |
|
||||
| POST | /wap-api/deposit/do_deposit | 创建取出申请 | amount, paypassword 等 | 前端实现,未执行 |
|
||||
| GET | /wap-api/deposit/page | 取出记录分页 | 分页与状态 | 前端实现 |
|
||||
| GET | /wap-api/balance-log/page | 账户流水 | 分页;类型、金额、业务单号 | 前端实现与页面实测 |
|
||||
| POST | /wap-api/users/setPwd | 修改登录密码 | oldPwd, newPwd | 前端实现,未执行 |
|
||||
| POST | /wap-api/users/setPwd2 | 修改交易密码 | oldPwd, newPwd | 前端实现,未执行 |
|
||||
| POST | /wap-api/local/upload | 文件上传 | multipart file | 前端实现,未执行 |
|
||||
| GET/POST | /wap-api/user-tc/select /read/{id} /page | 弹窗通知 | 公告、已读、分页 | 实测与前端实现 |
|
||||
|
||||
## 7.1 新系统接口规范
|
||||
|
||||
- 统一使用 HTTPS、JSON、明确的 HTTP 状态码、traceId、错误码和可本地化 message。
|
||||
- Authorization 使用短期访问令牌与可撤销刷新令牌;敏感操作增加一次性挑战或二次验证。
|
||||
- 列表接口统一 page、pageSize、sort、status,返回 items、total、hasNext。
|
||||
- 金额使用最小货币单位整数或高精度 Decimal,禁止浮点结算。
|
||||
- 上传接口先申请受限上传凭证,服务端扫描后再绑定业务。
|
||||
- 所有写接口要求 Idempotency-Key、设备信息与审计上下文。
|
||||
|
||||
# 8 管理后台需求
|
||||
|
||||
| **模块** | **主要能力** | **审批与审计** |
|
||||
| --- | --- | --- |
|
||||
| 用户管理 | 查询、状态、等级、信用、设备、邀请关系 | 冻结与解冻需理由和双人复核 |
|
||||
| 等级配置 | 门槛、赠送、次数、费率、提现限制、商品范围 | 版本化发布,可回滚 |
|
||||
| 任务商品 | 商品、图片、金额、佣金、库存、上下线 | 内容过滤和审核流 |
|
||||
| 任务订单 | 状态、倒计时、资金流水、异常处理 | 人工改状态必须双人审批 |
|
||||
| 储入管理 | 支付订单、回调、凭证、审核、对账 | 每日对账与差错处理 |
|
||||
| 取出管理 | 审核、通道、手续费、失败重试 | 大额与异常规则 |
|
||||
| 内容管理 | 横幅、规则、简介、资质、公告 | 预览、定时、版本与发布人 |
|
||||
| 客服管理 | 客服入口、工作时间、会话映射、工单 | 第三方访问最小权限 |
|
||||
| 风控 | 设备、IP、频次、关联账户、异常金额 | 命中规则可解释并可申诉 |
|
||||
| 权限审计 | RBAC、操作日志、导出日志 | 关键权限定期复核 |
|
||||
|
||||
# 9 非功能需求
|
||||
|
||||
## 9.1 安全
|
||||
|
||||
- 密码使用 Argon2id 或 bcrypt 加盐存储。参考站点在浏览器端对密码做 MD5,这不能替代服务端强哈希。
|
||||
- 输入与富文本做上下文相关转义;运营内容采用白名单;CSP 禁止任意外部脚本。
|
||||
- 银行卡和手机号加密存储,日志与埋点禁止记录完整值;展示统一脱敏。
|
||||
- 接口启用 CSRF 或同站策略、速率限制、风控挑战、签名回调和重放保护。
|
||||
- 第三方客服 iframe 设置最小 sandbox 权限、allowlist 域名和清晰的数据传输告知。
|
||||
- 上传文件限制 MIME、扩展名、体积与像素,执行恶意内容扫描,使用隔离存储和短期访问 URL。
|
||||
|
||||
## 9.2 性能与可用性
|
||||
|
||||
- 首屏 LCP 目标小于 2.5 秒,核心 API P95 小于 800 毫秒,写操作 P95 小于 1.5 秒。
|
||||
- 按钮防抖、请求取消、重试与离线提示;长列表分页和图片懒加载。
|
||||
- 资金写入接口可用性目标 99.95%,客服与内容接口目标 99.9%。
|
||||
- 客户端时间仅用于展示,截止时间与状态以服务端为准。
|
||||
|
||||
## 9.3 可访问性与国际化
|
||||
|
||||
按钮和导航必须有语义标签与至少 44×44 像素触控区域;颜色不作为唯一状态标识;支持字体放大。简体、繁体和术语需统一,避免同一页面混用“储入/充值”“取出/提现”“领取/抢单”。
|
||||
|
||||
# 10 合规与业务风险
|
||||
|
||||
参考站点涉及先储入、任务扣款、返本付佣、推荐奖励、信用分和提现限制。此类模式在不同司法辖区可能触及支付、消费者保护、反洗钱、网络营销或其他监管要求。上线前需由法务和持牌支付机构评估,不应直接复制其资金规则、免责条款或客服收款模式。
|
||||
|
||||
| **风险** | **参考站点表现** | **新系统要求** | **优先级** |
|
||||
| --- | --- | --- | --- |
|
||||
| 资金真实性 | 客服提供收款方式,失败责任不清 | 使用合规支付通道、订单化收款与自动对账 | P0 |
|
||||
| 诱导与不透明 | 高等级对应更高储入和收益 | 明确费用、风险、无保证收益,禁止误导 | P0 |
|
||||
| 推荐奖励 | 三级佣金 16% 8% 4% | 法务审查后决定是否启用 | P0 |
|
||||
| 账户冻结 | 规则含冻结、补差额、信用分 | 冻结需合法依据、通知、申诉和复核 | P0 |
|
||||
| 内容安全 | 商品名出现脚本标签文本 | CMS 白名单与持久型 XSS 防护 | P0 |
|
||||
| 密码安全 | 前端无盐 MD5 | 服务端强哈希、MFA、频控 | P0 |
|
||||
| 隐私 | 收集银行账户和收款码 | 最小化、加密、授权、删除与访问审计 | P0 |
|
||||
| 数据一致性 | 筛选短暂空列表、规则口径差异 | 单一配置源、请求竞态处理、自动化测试 | P1 |
|
||||
|
||||
# 11 埋点与运营指标
|
||||
|
||||
| **事件** | **触发时机** | **关键属性** |
|
||||
| --- | --- | --- |
|
||||
| auth_login_result | 登录成功或失败 | result, errorCode, deviceRisk |
|
||||
| home_shortcut_click | 点击首页快捷入口 | target, userLevel |
|
||||
| order_claim_click | 点击领取 | eligibilityResult, balanceBucket, level |
|
||||
| order_state_change | 订单状态变化 | orderNo, from, to, reason |
|
||||
| deposit_submit | 提交储入申请 | amountBucket, channel, result |
|
||||
| withdraw_submit | 提交取出申请 | amountBucket, fee, result |
|
||||
| support_open | 打开客服会话 | provider, entry, workStatus |
|
||||
| content_view | 查看规则或公告 | contentId, version, readDuration |
|
||||
| security_challenge | 触发安全验证 | scene, result, ruleId |
|
||||
|
||||
核心指标包括注册转化、首个任务完成率、领取成功率、订单超时率、储入成功率、取出成功率与处理时长、客服转人工率、异常账户率、账务差错率和投诉率。涉及金额与身份的数据只做区间化或匿名化统计。
|
||||
|
||||
# 12 验收标准
|
||||
|
||||
1. 未登录用户只能访问登录、注册和公开协议;受保护路由统一拦截。
|
||||
|
||||
2. 同一用户重复点击领取或提交订单,不产生重复业务单和重复资金流水。
|
||||
|
||||
3. 订单完成后,订单状态、钱包余额、三类流水和统计数据在一次事务或可验证最终一致性中完成。
|
||||
|
||||
4. 储入与取出所有状态均可在记录页查询,失败和驳回有明确原因。
|
||||
|
||||
5. 等级规则由后台发布后前台一致生效,表格、领取校验与提现限制不存在口径差异。
|
||||
|
||||
6. 手机号、银行卡、邀请码和交易密码不出现在客户端日志、服务端普通日志或分析平台。
|
||||
|
||||
7. 富文本、商品名、公告和客服消息中的脚本或危险 HTML 不能执行。
|
||||
|
||||
8. 接口 401、403、429、5xx、超时和断网场景均有清晰恢复路径。
|
||||
|
||||
9. 移动端 320 至 430 像素宽度可用,底部导航不遮挡内容,键盘弹出时表单按钮可达。
|
||||
|
||||
10. 关键资金和权限操作在后台可按用户、业务单号和时间完整审计。
|
||||
|
||||
# 13 交付优先级与里程碑
|
||||
|
||||
| **阶段** | **范围** | **退出条件** |
|
||||
| --- | --- | --- |
|
||||
| MVP 认证与账户 | 登录注册、资料、钱包、台账、内容、客服 | 权限、安全、审计基线通过 |
|
||||
| MVP 任务闭环 | 任务列表、领取、详情、提交、状态机、佣金 | 幂等和账务一致性测试通过 |
|
||||
| 资金闭环 | 储入、取出、收款绑定、支付通道、对账 | 支付与法务合规评审通过 |
|
||||
| 运营能力 | 等级、公告、横幅、商品、通知、后台 | 配置版本和发布审核可用 |
|
||||
| 增长与优化 | 邀请关系、数据看板、体验优化 | 合规批准且核心指标稳定 |
|
||||
|
||||
# 14 低保真原型
|
||||
|
||||
以下原型根据实测页面重新绘制,用于确认页面层级、组件位置和交互入口,不复制原站品牌素材、头像或敏感数据。视觉设计阶段应重新建立色彩、字体、图标和组件规范。
|
||||
|
||||

|
||||
|
||||
图 1 参考站点功能复刻原型
|
||||
|
||||

|
||||
|
||||
图 2 参考站点功能复刻原型
|
||||
|
||||

|
||||
|
||||
图 3 参考站点功能复刻原型
|
||||
|
||||

|
||||
|
||||
图 4 参考站点功能复刻原型
|
||||
|
||||
# 15 待确认事项
|
||||
|
||||
- 目标产品是否真实涉及用户资金储入与取出,还是仅做演示积分。
|
||||
- 任务来源、商品真实性、结算主体和佣金承担方。
|
||||
- 目标市场与适用法律、支付通道和实名要求。
|
||||
- 是否保留会员等级、推荐奖励、信用分和冻结机制。
|
||||
- 客服使用自建系统还是第三方服务,允许传输哪些用户数据。
|
||||
- 管理后台的组织结构、审批层级和财务对账责任人。
|
||||
- 品牌名、语言版本、视觉方向与是否需要 App 壳或 PWA。
|
||||
|
||||
在上述事项明确前,可以先完成无资金风险的认证、内容、任务展示、客服、账户明细和后台配置原型;资金接口只做沙箱与模拟数据。
|
||||
@@ -1,56 +0,0 @@
|
||||
# 工作台服务迁移说明
|
||||
|
||||
本仓库承接 `im_server` 中的工作台接口,第一阶段目标是让工作台服务独立编译、独立启动、独立鉴权,并由网关把 `/v1/workbench/*` 分流到本服务。
|
||||
|
||||
## 已迁移接口
|
||||
|
||||
| 模块 | 新路径 |
|
||||
|---|---|
|
||||
| 工作台入口 | `GET /v1/workbench` |
|
||||
| 组织架构 | `/v1/workbench/organization/*` |
|
||||
| 考勤 | `/v1/workbench/attendance/*` |
|
||||
| 会议 | `/v1/workbench/meetings*` |
|
||||
| 会议笔记 | `/v1/workbench/meeting-notes`、`/v1/workbench/meetings/{id}/notes*` |
|
||||
| 日报 | `/v1/workbench/reports/daily*` |
|
||||
| 月报 | `/v1/workbench/reports/monthly*` |
|
||||
| 总经理信箱 | `/v1/workbench/executive-mailbox/*` |
|
||||
|
||||
## 当前边界
|
||||
|
||||
1. 数据库暂不迁移,仍访问现有租户库工作台表。
|
||||
2. 文件上传仍由 `im_server` 的 `/v0/file/u/` 处理,工作台只保存 `file_id` 引用。
|
||||
3. C 端实时通知仍由 `im_server` 下发;工作台后续只投 Kafka 业务事件。
|
||||
4. `im_server` 原工作台代码暂不删除,待本服务联调稳定后再清理。
|
||||
5. 服务启动不自动创建或修改表结构。
|
||||
|
||||
## 待联调项
|
||||
|
||||
1. `im_server` 登录后写入 Redis session:`im:session:token:{sha256(token)}`。
|
||||
2. Nginx/API Gateway 路由:`/v1/workbench/* -> im_workbench:6061`。
|
||||
3. 工作台业务事件 Kafka 化:会议、考勤、日报、月报、信箱事件投递给 `im_server`。
|
||||
4. 数据库专项迁移:接口稳定后再从租户库拆到工作台库。
|
||||
|
||||
## Kafka 事件范围
|
||||
|
||||
当前工作台服务生产端统一投递到 `im.workbench.event`,由 `im_server` 后续接入消费组并转为系统通知、未读数或 WS 实时下发。
|
||||
|
||||
| 模块 | Action | 触发场景 |
|
||||
|---|---|---|
|
||||
| 考勤 | `attendance.record.updated` | 打卡成功 |
|
||||
| 考勤 | `attendance.appeal.created` | 提交考勤申诉 |
|
||||
| 会议 | `meeting.created` | 创建会议 |
|
||||
| 会议 | `meeting.updated` | 修改会议 |
|
||||
| 会议 | `meeting.canceled` | 取消会议 |
|
||||
| 会议 | `meeting.response.updated` | 参会人更新回执 |
|
||||
| 会议笔记 | `meeting_note.file.created` | 新增会议笔记文件 |
|
||||
| 会议笔记 | `meeting_note.file.replaced` | 替换会议笔记文件 |
|
||||
| 会议笔记 | `meeting_note.file.deleted` | 删除会议笔记文件 |
|
||||
| 日报 | `daily_report.draft.created` | 创建日报草稿 |
|
||||
| 日报 | `daily_report.draft.saved` | 保存日报草稿 |
|
||||
| 日报 | `daily_report.submitted` | 提交日报 |
|
||||
| 日报 | `daily_report.reminded` | 日报提醒 |
|
||||
| 月报 | `monthly_report.draft.created` | 创建月报草稿 |
|
||||
| 月报 | `monthly_report.draft.saved` | 保存月报草稿 |
|
||||
| 月报 | `monthly_report.submitted` | 提交月报 |
|
||||
| 月报 | `monthly_report.reopened` | 重开月报 |
|
||||
| 信箱 | `mailbox.submitted` | 提交总经理信箱 |
|
||||
Reference in New Issue
Block a user