交易信号管理平台后端服务
openmask/
├── cmd/server/main.go # 入口文件
├── go.mod # Go模块配置
├── internal/
│ ├── config/ # 配置模块
│ │ ├── config.go # 配置加载
│ │ └── config_test.go # 配置测试
│ ├── models/ # 数据库模型
│ │ └── models.go # 17个数据表模型
│ ├── repository/ # 数据访问层
│ │ └── repository.go # 数据库操作
│ ├── services/ # 业务逻辑层
│ │ ├── user_service.go # 用户/鉴权服务
│ │ ├── signal_service.go # 信号中心服务
│ │ ├── trade_service.go # 交易回报服务
│ │ ├── stats_service.go # 统计分析服务
│ │ ├── content_service.go # 内容消息服务
│ │ └── *_test.go # 服务层测试
│ ├── handlers/ # API处理器
│ │ └── handlers.go # 所有HTTP接口
│ └── middleware/ # 中间件
│ └── middleware.go # 认证/日志/限流等
└── pkg/
├── utils/ # 工具函数
│ ├── crypto.go # 加密/脱敏
│ ├── response.go # 响应封装
│ └── *_test.go # 工具测试
└── errors/ # 错误处理
├── errors.go # 错误定义
└── errors_test.go # 错误测试
graph TB
subgraph Client ["客户端"]
A[iOS App]
B[Android App]
C[Web App]
D[交易机器人]
end
subgraph Gateway ["API 网关 / Load Balancer"]
E[ginx / Envoy]
end
subgraph Auth ["用户鉴权模块"]
F[用户注册/登录]
G[SDK Session]
H[交易账户绑定]
end
subgraph Signal ["信号中心模块"]
I[信号录入]
J[信号分发]
K[ACK确认]
end
subgraph Trade ["交易回报接收模块"]
L[开单/平单上报]
M[持仓快照<br/>10秒增量]
N[资产快照]
O[幂等性保证]
end
subgraph Stats ["统计分析模块"]
P[盈亏统计<br/>日/累计]
Q[全局排行榜]
R[策略统计]
end
subgraph Content ["内容消息模块"]
S[公告管理]
T[策略历史]
U[AI对话历史]
end
subgraph Database ["数据存储"]
V[(MySQL)]
W[(Redis)]
end
subgraph External ["外部服务"]
X[交易所 API<br/>Binance/OKX]
end
A --> E
B --> E
C --> E
D --> E
E --> F
E --> G
E --> H
E --> I
E --> L
E --> P
E --> S
F --> V
G --> V
H --> V
I --> V
L --> V
M --> V
N --> V
P --> V
Q --> V
S --> V
T --> V
U --> V
V <--> W
H --> X
sequenceDiagram
participant Client as 客户端
participant Server as 服务端
participant DB as MySQL
participant Redis as Redis
Note over Client,Server: 认证流程
Client->>Server: POST /auth/register
Server->>DB: 创建用户
Server->>Client: {user_id, username}
Client->>Server: POST /auth/login
Server->>DB: 验证用户
Server->>DB: 创建Session
Server->>Client: {token, expires_at}
Note over Client,Server: 交易回报上报
Client->>Server: POST /trade/open (request_id)
Server->>DB: 检查request_id是否已存在
alt 幂等检查
Server->>DB: 插入订单记录
Server->>Client: {order_id}
else 已存在
Server->>Client: 返回已有记录
end
loop 每10秒增量同步
Client->>Server: POST /trade/sync
Server->>DB: 更新持仓快照
Server->>DB: 更新资产快照
Server->>Client: {message}
end
erDiagram
User ||--o{ UserExchangeAccount : has
User ||--o{ UserSDKSession : has
User ||--o{ Signal : creates
User ||--o{ SignalDispatchLog : receives
User ||--o{ TradeOrder : places
User ||--o{ UserPositionSnapshot : holds
User ||--o{ UserAssetSnapshot : owns
User ||--o{ UserPnlDaily : generates
User ||--o{ UserPnlTotal : accumulates
User ||--o{ GlobalRankSnapshot : appears_in
User ||--o{ AIChatHistory : chats
Signal ||--o{ SignalDispatchLog : dispatched_to
Signal ||--o{ SignalFollowRelation : followed_by
TradeOrder ||--o{ TradeFill : contains
- 用户注册/登录
- SDK会话管理
- 交易账户绑定(API Key加密存储)
- 手工信号录入
- AI信号录入(预留)
- 信号分发与ACK确认
- 开单/平单上报
- 持仓快照上报(每10秒增量)
- 资产快照上报
- 幂等性保证
- 用户盈亏统计(日/累计)
- 全局排行榜(按盈亏/收益率)
- 排名更新
- 公告管理
- 策略信号历史
- AI对话历史
- 仪表盘数据
- 订单/持仓/资产历史
- 排行榜查询
- User、UserExchangeAccount、UserSDKSession
- Signal、SignalDispatchLog、SignalFollowRelation
- TradeOrder、TradeFill、UserPositionSnapshot、UserAssetSnapshot
- UserPnlDaily、UserPnlTotal、GlobalRankSnapshot
- Announcement、StrategySignalHistory、AIChatHistory
- 基础URL:
http://localhost:8080/api/v1 - 协议: HTTP JSON
- 认证方式: Bearer Token (Session Token)
// 成功响应
{
"code": 0,
"message": "success",
"data": {}
}
// 分页响应
{
"code": 0,
"message": "success",
"data": [],
"total": 100,
"page": 1,
"page_size": 20
}
// 错误响应
{
"code": 1001,
"message": "错误信息"
}| 错误码 | 说明 |
|---|---|
| 0 | 成功 |
| 1001 | 参数错误 |
| 1002 | 未授权 |
| 1003 | 禁止访问 |
| 1004 | 资源不存在 |
| 1005 | 资源冲突 |
| 1006 | 用户不存在 |
| 1007 | 用户已被禁用 |
| 1008 | 用户名或密码错误 |
| 1009 | Token无效 |
| 1010 | Token已过期 |
| 5000 | 服务器内部错误 |
Content-Type: application/json
Authorization: Bearer <session_token>
X-Device-ID: <设备ID>
POST /auth/register
Request:
{
"username": "string", // 必填,用户名
"password": "string", // 必填,密码
"email": "string", // 选填,邮箱
"nickname": "string" // 选填,昵称
}Response:
{
"code": 0,
"message": "success",
"data": {
"user_id": 1,
"username": "testuser"
}
}POST /auth/login
Request:
{
"username": "string", // 必填
"password": "string" // 必填
}Response:
{
"code": 0,
"message": "success",
"data": {
"user_id": 1,
"username": "testuser",
"token": "uuid-token",
"expires_at": "2024-12-31T23:59:59Z"
}
}POST /auth/sdk/login
Request:
{
"token": "string" // 必填,session token
}Response:
{
"code": 0,
"message": "success",
"data": {
"user_id": 1,
"expires_at": "2024-12-31T23:59:59Z"
}
}GET /user/info
Headers: Authorization: Bearer <token>
Response:
{
"code": 0,
"message": "success",
"data": {
"id": 1,
"username": "testuser",
"nickname": "昵称",
"email": "email@example.com",
"avatar": "avatar_url",
"total_pnl": 1000.50,
"total_pnl_rate": 0.25,
"rank": 10,
"created_at": "2024-01-01T00:00:00Z"
}
}POST /user/exchange/account
Headers: Authorization: Bearer <token>
Request:
{
"exchange": "binance", // 必填,交易所名称
"api_key": "string", // 必填,API Key
"api_secret": "string", // 必填,API Secret
"account_type": "合约", // 必填,现货/合约
"margin_type": "全仓", // 选填,全仓/逐仓
"position_type": "单向持仓" // 选填,单向/双向
}Response:
{
"code": 0,
"message": "success",
"data": {
"id": 1,
"exchange": "binance",
"api_key_masked": "abcd****1234",
"account_type": 2,
"status": 1
}
}GET /user/exchange/accounts
Headers: Authorization: Bearer <token>
Response:
{
"code": 0,
"message": "success",
"data": [
{
"id": 1,
"exchange": "binance",
"api_key_masked": "abcd****1234",
"account_type": 2,
"margin_type": 1,
"position_type": 1,
"status": 1,
"bind_time": "2024-01-01T00:00:00Z"
}
]
}POST /signals
Headers: Authorization: Bearer <token>
Request:
{
"strategy_id": "str_001",
"strategy_name": "趋势策略",
"symbol": "BTCUSDT",
"side": "BUY", // BUY/SELL
"position_side": "LONG", // LONG/SHORT
"order_type": "MARKET", // MARKET/LIMIT
"quantity": 0.01,
"price": 0, // 市价单填0
"stop_price": 0, // 止损价
"remark": "策略备注"
}Response:
{
"code": 0,
"message": "success",
"data": {
"id": "sig_xxx",
"strategy_id": "str_001",
"symbol": "BTCUSDT",
"side": "BUY",
"status": 1,
"created_at": "2024-01-01T00:00:00Z"
}
}GET /signals
Query Parameters:
page: 页码,默认1page_size: 每页数量,默认20
Response:
{
"code": 0,
"message": "success",
"data": [],
"total": 100,
"page": 1,
"page_size": 20
}GET /signals/pending
Query Parameters:
limit: 数量限制,默认20
GET /signals/:id
POST /signals/:id/ack
Headers: Authorization: Bearer <token>
Response:
{
"code": 0,
"message": "success",
"data": {
"message": "信号已确认"
}
}POST /trade/open
Headers: Authorization: Bearer <token>
Request:
{
"request_id": "unique_req_id", // 必填,幂等ID
"order_id": "order_123",
"symbol": "BTCUSDT",
"side": "BUY",
"position_side": "LONG",
"order_type": "MARKET",
"quantity": 0.01,
"price": 50000.00,
"filled_quantity": 0.01,
"filled_price": 50000.00,
"commission": 0.00001,
"remark": "开仓备注"
}POST /trade/close
Headers: Authorization: Bearer <token>
Request:
{
"request_id": "unique_req_id", // 必填,幂等ID
"order_id": "order_456",
"symbol": "BTCUSDT",
"side": "SELL",
"position_side": "LONG",
"order_type": "MARKET",
"quantity": 0.01,
"price": 51000.00,
"filled_quantity": 0.01,
"filled_price": 51000.00,
"commission": 0.00001,
"realized_pnl": 10.00,
"remark": "平仓备注"
}POST /trade/fail
Headers: Authorization: Bearer <token>
Request:
{
"request_id": "unique_req_id",
"order_id": "order_789",
"symbol": "BTCUSDT",
"error_code": "INSUFFICIENT_BALANCE",
"error_msg": "余额不足"
}POST /trade/position/snapshot
Headers: Authorization: Bearer <token>
Request:
{
"request_id": "unique_req_id",
"symbol": "BTCUSDT",
"position_side": "LONG",
"quantity": 0.01,
"entry_price": 50000.00,
"mark_price": 51000.00,
"unrealized_pnl": 10.00,
"leverage": 10
}POST /trade/asset/snapshot
Headers: Authorization: Bearer <token>
Request:
{
"request_id": "unique_req_id",
"total_assets": 10000.00,
"total_pnl": 100.00,
"available_balance": 5000.00,
"frozen_balance": 4900.00,
"margin_balance": 100.00
}POST /trade/sync
Headers: Authorization: Bearer <token>
Request:
{
"positions": [
{
"request_id": "pos_001",
"symbol": "BTCUSDT",
"position_side": "LONG",
"quantity": 0.01,
"entry_price": 50000.00,
"mark_price": 51000.00,
"unrealized_pnl": 10.00,
"leverage": 10
}
],
"assets": {
"request_id": "asset_001",
"total_assets": 10000.00,
"total_pnl": 100.00,
"available_balance": 5000.00,
"frozen_balance": 4900.00,
"margin_balance": 100.00
}
}GET /trade/positions
Headers: Authorization: Bearer <token>
GET /trade/orders
Headers: Authorization: Bearer <token>
Query Parameters:
page: 页码page_size: 每页数量
GET /trade/asset/latest
Headers: Authorization: Bearer <token>
GET /trade/asset/history
Headers: Authorization: Bearer <token>
Query Parameters:
page: 页码page_size: 每页数量
GET /trade/fills
Headers: Authorization: Bearer <token>
Query Parameters:
page: 页码page_size: 每页数量
GET /stats/pnl
Headers: Authorization: Bearer <token>
GET /stats/rank
Query Parameters:
type: 排序类型 (pnl/rate)limit: 数量限制,默认20
GET /stats/rank/me
Headers: Authorization: Bearer <token>
POST /stats/rank/update
GET /announcements/active
GET /announcements
Query Parameters:
page: 页码page_size: 每页数量
GET /announcements/:id
POST /announcements
POST /announcements/:id/publish
GET /strategy/history
Query Parameters:
strategy_id: 策略IDpage: 页码page_size: 每页数量
GET /chat/history
Headers: Authorization: Bearer <token>
Query Parameters:
page: 页码page_size: 每页数量
POST /chat/message
Headers: Authorization: Bearer <token>
Request:
{
"session_id": "session_001",
"content": "用户消息内容"
}GET /dashboard
Headers: Authorization: Bearer <token>
Response:
{
"code": 0,
"message": "success",
"data": {
"user": {},
"asset": {},
"positions": [],
"pnl": {},
"rank": {},
"timestamp": "2024-01-01T00:00:00Z"
}
}GET /health
确保服务端已配置以下环境变量或配置文件:
# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=password
DB_NAME=openmask
# Redis配置(可选)
REDIS_HOST=localhost
REDIS_PORT=6379
# 服务端口
SERVER_PORT=8080┌─────────────┐ POST /api/v1/auth/register ┌─────────────┐
│ │ ─────────────────────────────────► │ │
│ 客户端 │ │ 服务端 │
│ │ ◄───────────────────────────────── │ │
└─────────────┘ {user_id, username} └─────────────┘
│ POST /api/v1/auth/login
│ ─────────────────────────────────►
│ ┌─────────────┐
│ ◄───────────────────────────────── │ 服务端 │
│ {token, expires_at} └─────────────┘
│
▼
后续请求携带 Token
Authorization: Bearer <token>
客户端(交易机器人) 服务端
│ │
│──── POST /trade/open ────────────►│
│ (request_id: "uuid") │
│◄───── {order_id, ...} ────────────│
│ │
│ (交易进行中) │
│ │
│──── POST /trade/sync ────────────►│
│ (positions + assets) │
│◄───── {message} ──────────────────│
│ │
│ (每10秒增量同步) │
│ │
│──── POST /trade/close ───────────►│
│ (request_id: "uuid") │
│◄───── {order_id, realized_pnl} ───│
│ │
所有交易回报接口支持幂等性,通过 request_id 防止重复提交:
// 客户端生成唯一ID
requestID := uuid.New().String()
// 请求体包含 request_id
{
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"order_id": "order_123",
...
}
// 服务端会根据 request_id 判断是否已处理
// 相同 request_id 的请求会返回已存在的结果,不会重复处理- 客户端提交 API Key 时使用明文
- 服务端使用 AES-256-CFB 加密存储
- 展示时返回脱敏后的 Key(如
abcd****1234)
// Go 示例
resp, err := http.Post(url, "application/json", body)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
if result["code"].(float64) != 0 {
// 处理错误
log.Printf("Error: %v", result["message"])
}// Swift 示例
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default)
.responseJSON { response in
if let json = response.result.value as? [String: Any] {
if json["code"] as! Int != 0 {
print("Error: \(json["message"]!)")
}
}
}// Kotlin 示例
RetrofitClient.api.login(request)
.enqueue(object : Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
if (response.body()?.code != 0) {
println("Error: ${response.body()?.message}")
}
}
})- 调用
/auth/register注册 - 调用
/auth/login登录获取 Token - 调用
/user/exchange/account绑定交易所账户
- 启动时调用
/auth/sdk/login验证 Token - 开仓调用
/trade/open - 定时(每10秒)调用
/trade/sync同步持仓和资产 - 平仓调用
/trade/close - 失败调用
/trade/fail
- 登录获取 Token
- 调用
/dashboard获取概览 - 调用
/trade/positions查看持仓 - 调用
/stats/rank查看排行榜
# 编译
go build -o openmask-server ./cmd/server
# 测试
go test ./...所有测试已通过。启动服务前需要配置MySQL数据库连接。