docs(api): 新增kami_gateway系统支付、代付、回调API文档
- 创建支付API文档,详细说明扫码支付及订单创建接口流程 - 新建代付API文档,描述代付功能架构、组件及流程设计 - 编写回调API文档,覆盖多第三方支付渠道回调处理机制 - 说明API参数结构、签名验证规则及错误码 - 补充调用示例及常见错误码处理方案 - 设计消息队列重试机制保证回调通知可靠传递
This commit is contained in:
334
.qoder/repowiki/zh/content/API参考/API参考.md
Normal file
334
.qoder/repowiki/zh/content/API参考/API参考.md
Normal file
@@ -0,0 +1,334 @@
|
||||
# API参考
|
||||
|
||||
<cite>
|
||||
**本文档中引用的文件**
|
||||
- [router.go](file://internal/routers/router.go)
|
||||
- [sign_verify.go](file://internal/utils/sign_verify.go)
|
||||
- [order.go](file://internal/schema/request/order.go)
|
||||
- [pay_resp.go](file://internal/schema/response/pay_resp.go)
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go)
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [API认证与签名机制](#api认证与签名机制)
|
||||
3. [支付下单接口](#支付下单接口)
|
||||
4. [订单查询接口](#订单查询接口)
|
||||
5. [回调通知接口](#回调通知接口)
|
||||
6. [代付接口](#代付接口)
|
||||
7. [客户端调用示例](#客户端调用示例)
|
||||
8. [常见错误码与解决方案](#常见错误码与解决方案)
|
||||
|
||||
## 简介
|
||||
本文档为kami_gateway系统的公开RESTful API提供完整的技术参考。文档基于`router.go`中的路由定义,系统性地描述了支付下单、订单查询和回调通知三大核心流程的接口规范。所有请求和响应结构均引用`internal/schema/request`和`internal/schema/response`中的定义。本文档还详细说明了API的签名验证机制、安全要求,并提供实际使用示例。
|
||||
|
||||
## API认证与签名机制
|
||||
|
||||
所有API请求必须通过签名验证以确保安全性和完整性。签名算法基于MD5,具体实现位于`sign_verify.go`中。不同接口可能使用不同的签名方法,主要分为标准MD5签名和MF格式签名。
|
||||
|
||||
### 签名生成规则
|
||||
1. **标准MD5签名(GetMD5Sign)**:
|
||||
- 将所有非空参数按键名的字母顺序排序
|
||||
- 拼接格式为:`key1=value1&key2=value2&...&paySecret=商户密钥`
|
||||
- 对拼接后的字符串进行MD5加密,并转换为大写
|
||||
|
||||
2. **MF格式签名(GetMD5SignMF)**:
|
||||
- 将所有非空参数按键名的字母顺序排序
|
||||
- 拼接格式为:`key1value1key2value2...paySecret`
|
||||
- 对拼接后的字符串进行MD5加密,并转换为小写
|
||||
|
||||
### 签名验证流程
|
||||
1. 服务端接收到请求后,提取`sign`参数
|
||||
2. 根据接口类型选择相应的签名算法
|
||||
3. 使用商户密钥重新计算签名
|
||||
4. 比较客户端签名与服务端计算的签名是否一致
|
||||
5. 签名验证失败将返回错误响应
|
||||
|
||||
**Section sources**
|
||||
- [sign_verify.go](file://internal/utils/sign_verify.go#L1-L104)
|
||||
|
||||
## 支付下单接口
|
||||
|
||||
支付下单接口用于创建新的支付订单,是支付流程的起点。
|
||||
|
||||
### 接口定义
|
||||
- **HTTP方法**:POST
|
||||
- **URL模式**:`/gateway/scan`
|
||||
- **认证方式**:MD5签名
|
||||
|
||||
### 请求头
|
||||
| 头部字段 | 是否必需 | 描述 |
|
||||
|---------|--------|------|
|
||||
| Content-Type | 是 | 必须为 `application/json` |
|
||||
|
||||
### 请求体(Schema: CreatedOrder)
|
||||
请求体结构定义在`internal/schema/request/order.go`中,具体字段如下:
|
||||
|
||||
| 字段名 | 类型 | 是否必需 | 描述 |
|
||||
|-------|------|--------|------|
|
||||
| payKey | string | 是 | 商户唯一标识key,由系统分配 |
|
||||
| orderNo | string | 是 | 商户侧订单号,需保证唯一性 |
|
||||
| orderPrice | float64 | 是 | 订单金额,单位为元 |
|
||||
| orderPeriod | int | 否 | 订单周期(天) |
|
||||
| notifyUrl | string | 是 | 支付成功后的回调通知地址 |
|
||||
| sign | string | 是 | 签名字符串,用于验证请求合法性 |
|
||||
| productCode | string | 是 | 产品编码,标识支付产品类型 |
|
||||
| timestamp | int64 | 是 | 时间戳,单位为毫秒 |
|
||||
|
||||
### 响应体
|
||||
成功响应返回`ScanSuccessData`结构,失败返回`ScanFailData`结构。
|
||||
|
||||
#### 成功响应示例
|
||||
```json
|
||||
{
|
||||
"orderNo": "ORD20231101001",
|
||||
"sign": "A1B2C3D4E5F6...",
|
||||
"orderPrice": "100.00",
|
||||
"statusCode": "00",
|
||||
"msg": "订单创建成功",
|
||||
"code": 200,
|
||||
"payUrl": "https://pay.example.com/order/ORD20231101001"
|
||||
}
|
||||
```
|
||||
|
||||
#### 失败响应示例
|
||||
```json
|
||||
{
|
||||
"payKey": "MCH123456",
|
||||
"statusCode": "99",
|
||||
"msg": "参数验证失败",
|
||||
"code": 400
|
||||
}
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [router.go](file://internal/routers/router.go#L1-L75)
|
||||
- [order.go](file://internal/schema/request/order.go#L1-L35)
|
||||
- [pay_resp.go](file://internal/schema/response/pay_resp.go#L1-L38)
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go)
|
||||
|
||||
## 订单查询接口
|
||||
|
||||
订单查询接口允许商户查询已创建订单的状态和详细信息。
|
||||
|
||||
### 接口定义
|
||||
- **HTTP方法**:GET
|
||||
- **URL模式**:`/gateway/merchant/query`
|
||||
- **认证方式**:MF格式MD5签名
|
||||
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 是否必需 | 描述 |
|
||||
|-------|------|--------|------|
|
||||
| appKey | string | 是 | 商户应用key,用于身份识别 |
|
||||
| orderNo | string | 是 | 商户侧订单号 |
|
||||
| timestamp | string | 是 | 请求时间戳,格式为Unix时间戳字符串 |
|
||||
| sign | string | 是 | MF格式签名 |
|
||||
|
||||
### 响应体(Schema: OrderQueryResp)
|
||||
响应体结构定义在`internal/schema/response`中,包含订单的完整状态信息。
|
||||
|
||||
| 字段名 | 类型 | 描述 |
|
||||
|-------|------|------|
|
||||
| orderNo | string | 系统生成的订单号 |
|
||||
| cardNo | string | 卡号(如适用) |
|
||||
| cardPwd | string | 卡密(如适用) |
|
||||
| status | string | 订单状态:`wait`(等待)、`success`(成功)、`fail`(失败) |
|
||||
| faceVal | float64 | 实际面值金额 |
|
||||
| cardReturnData | string | 通道返回的原始数据 |
|
||||
| amount | string | 显示金额,格式化为字符串 |
|
||||
|
||||
### 成功响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "订单获取成功",
|
||||
"data": {
|
||||
"orderNo": "ORD20231101001",
|
||||
"cardNo": "1234567890",
|
||||
"cardPwd": "ABCDEF123456",
|
||||
"status": "success",
|
||||
"faceVal": 100.0,
|
||||
"cardReturnData": "{\"result\":\"success\"}",
|
||||
"amount": "100.00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"code": -1,
|
||||
"msg": "签名错误"
|
||||
}
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [router.go](file://internal/routers/router.go#L1-L75)
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L1-L226)
|
||||
|
||||
## 回调通知接口
|
||||
|
||||
回调通知接口用于接收第三方支付平台的支付结果通知,是支付流程的终点。
|
||||
|
||||
### 接口定义
|
||||
- **HTTP方法**:POST
|
||||
- **URL模式**:`/gateway/supplier/order/query`
|
||||
- **认证方式**:无(由第三方系统调用)
|
||||
|
||||
### 请求参数
|
||||
| 参数名 | 类型 | 是否必需 | 描述 |
|
||||
|-------|------|--------|------|
|
||||
| bankOrderId | string | 是 | 系统内部订单号 |
|
||||
|
||||
### 响应格式
|
||||
接口返回纯文本响应,用于确认接收状态。
|
||||
|
||||
| 状态 | 响应内容 |
|
||||
|------|----------|
|
||||
| 成功 | "success" |
|
||||
| 失败 | 错误描述信息 |
|
||||
|
||||
### 处理流程
|
||||
1. 系统接收到第三方通知
|
||||
2. 根据`bankOrderId`查询订单信息
|
||||
3. 更新订单状态为成功
|
||||
4. 触发后续业务逻辑(如通知商户)
|
||||
5. 返回确认响应
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant ThirdParty as 第三方支付平台
|
||||
participant Gateway as kami_gateway
|
||||
participant Database as 数据库
|
||||
ThirdParty->>Gateway : POST /gateway/supplier/order/query
|
||||
Gateway->>Database : 查询订单(bankOrderId)
|
||||
Database-->>Gateway : 返回订单信息
|
||||
Gateway->>Database : 更新订单状态为"success"
|
||||
Gateway-->>ThirdParty : "success"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [router.go](file://internal/routers/router.go#L1-L75)
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L1-L226)
|
||||
|
||||
## 代付接口
|
||||
|
||||
代付接口用于处理商户的代付请求,包括代付下单、余额查询和结果查询。
|
||||
|
||||
### 接口列表
|
||||
- **代付下单**:`/gateway/payfor` (当前注释状态)
|
||||
- **代付查询**:`/gateway/payfor/query` (当前注释状态)
|
||||
- **余额查询**:`/gateway/balance` (当前注释状态)
|
||||
- **供应商代付结果查询**:`/gateway/supplier/payfor/query` (当前注释状态)
|
||||
- **代付结果处理**:`/solve/payfor/result` (当前注释状态)
|
||||
|
||||
> **注意**:根据`router.go`中的代码,大部分代付接口当前处于注释状态,表示功能尚未启用或正在开发中。
|
||||
|
||||
**Section sources**
|
||||
- [router.go](file://internal/routers/router.go#L1-L75)
|
||||
|
||||
## 客户端调用示例
|
||||
|
||||
### Go语言调用示例
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CreatedOrder struct {
|
||||
PayKey string `json:"payKey"`
|
||||
OrderNo string `json:"orderNo"`
|
||||
OrderPrice float64 `json:"orderPrice"`
|
||||
NotifyUrl string `json:"notifyUrl"`
|
||||
Sign string `json:"sign"`
|
||||
ProductCode string `json:"productCode"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
func generateSign(params map[string]string, paySecret string) string {
|
||||
var keys []string
|
||||
for k := range params {
|
||||
if k != "sign" && params[k] != "" {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var str string
|
||||
for _, k := range keys {
|
||||
str += k + "=" + params[k] + "&"
|
||||
}
|
||||
str += "paySecret=" + paySecret
|
||||
|
||||
hash := md5.Sum([]byte(str))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
func createOrder() {
|
||||
order := CreatedOrder{
|
||||
PayKey: "MCH123456",
|
||||
OrderNo: "ORD" + strconv.FormatInt(time.Now().Unix(), 10),
|
||||
OrderPrice: 100.00,
|
||||
NotifyUrl: "https://yourdomain.com/notify",
|
||||
ProductCode: "CARD_100",
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
// 生成签名
|
||||
params := map[string]string{
|
||||
"payKey": order.PayKey,
|
||||
"orderNo": order.OrderNo,
|
||||
"orderPrice": fmt.Sprintf("%.2f", order.OrderPrice),
|
||||
"notifyUrl": order.NotifyUrl,
|
||||
"productCode": order.ProductCode,
|
||||
"timestamp": strconv.FormatInt(order.Timestamp, 10),
|
||||
}
|
||||
order.Sign = generateSign(params, "your_merchant_secret")
|
||||
|
||||
// 发送请求
|
||||
jsonData, _ := json.Marshal(order)
|
||||
resp, err := http.Post("http://kami-gateway/gateway/scan", "application/json", bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
fmt.Printf("请求失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Printf("响应: %s\n", string(body))
|
||||
}
|
||||
|
||||
func main() {
|
||||
createOrder()
|
||||
}
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [order.go](file://internal/schema/request/order.go#L1-L35)
|
||||
|
||||
## 常见错误码与解决方案
|
||||
|
||||
| 错误码 | 错误信息 | 可能原因 | 解决方案 |
|
||||
|-------|--------|--------|--------|
|
||||
| -1 | 参数错误 | 必填参数缺失或格式不正确 | 检查所有必需参数是否完整且格式正确 |
|
||||
| -1 | key错误 | appKey或payKey无效 | 确认使用的key是否正确且已激活 |
|
||||
| -1 | 签名错误 | 签名计算不正确 | 严格按照签名规则重新计算签名,注意参数排序和拼接方式 |
|
||||
| -1 | 订单不存在 | 查询的订单号不存在 | 确认订单号是否正确,或检查是否已成功创建订单 |
|
||||
| 400 | 参数验证失败 | 请求参数不符合验证规则 | 检查参数类型、长度和格式是否符合API文档要求 |
|
||||
| 500 | 内部错误 | 服务器内部处理异常 | 联系技术支持,提供请求时间、订单号等信息 |
|
||||
|
||||
**Section sources**
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L1-L226)
|
||||
- [pay_resp.go](file://internal/schema/response/pay_resp.go#L1-L38)
|
||||
288
.qoder/repowiki/zh/content/API参考/代付API.md
Normal file
288
.qoder/repowiki/zh/content/API参考/代付API.md
Normal file
@@ -0,0 +1,288 @@
|
||||
# 代付API
|
||||
|
||||
<cite>
|
||||
**本文档中引用的文件**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go)
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
- [payfor_info.go](file://internal/models/payfor/payfor_info.go)
|
||||
- [payfor_resp.go](file://internal/schema/response/payfor_resp.go)
|
||||
- [router.go](file://internal/routers/router.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概述](#架构概述)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
本文档旨在为kami_gateway系统的代付功能创建全面的API文档。尽管相关控制器代码被注释,但基于现有代码结构和命名约定,详细说明了代付API的设计意图和预期功能。文档涵盖了代付请求、结果查询、余额查询以及手动处理代付结果等关键端点,解释了代付流程中的核心参数、签名验证机制和参数校验逻辑。
|
||||
|
||||
## 项目结构
|
||||
kami_gateway项目采用分层架构设计,主要分为conf、deploy、internal三大目录。代付功能的核心代码位于internal目录下,包括controllers、service、models、schema等子模块。这种结构实现了关注点分离,便于维护和扩展。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "配置"
|
||||
conf[conf]
|
||||
end
|
||||
subgraph "部署"
|
||||
deploy[deploy]
|
||||
end
|
||||
subgraph "核心逻辑"
|
||||
internal[internal]
|
||||
internal --> controllers[controllers]
|
||||
internal --> service[service]
|
||||
internal --> models[models]
|
||||
internal --> schema[schema]
|
||||
internal --> routers[routers]
|
||||
end
|
||||
conf --> internal
|
||||
deploy --> internal
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go)
|
||||
- [router.go](file://internal/routers/router.go)
|
||||
|
||||
**本节来源**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go)
|
||||
- [router.go](file://internal/routers/router.go)
|
||||
|
||||
## 核心组件
|
||||
代付功能的核心组件包括PayForGateway控制器、pay_for服务、PayforInfo模型和相关响应结构体。这些组件协同工作,处理代付请求的接收、验证、处理和结果返回。控制器负责接收HTTP请求,服务层处理业务逻辑,模型定义数据结构,响应结构体规范API输出格式。
|
||||
|
||||
**本节来源**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go)
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_info.go](file://internal/models/payfor/payfor_info.go)
|
||||
- [payfor_resp.go](file://internal/schema/response/payfor_resp.go)
|
||||
|
||||
## 架构概述
|
||||
代付功能的架构采用典型的MVC模式,通过控制器接收请求,调用服务层处理业务逻辑,操作模型层的数据,并返回标准化的响应。整个流程涉及签名验证、参数校验、数据库操作和事务管理,确保了代付操作的安全性和一致性。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Client[客户端] --> Controller[PayForGateway控制器]
|
||||
Controller --> Service[pay_for服务]
|
||||
Service --> Model[PayforInfo模型]
|
||||
Model --> Database[(数据库)]
|
||||
Service --> Utils[工具模块]
|
||||
Utils --> Sign[签名验证]
|
||||
Service --> Response[响应生成]
|
||||
Response --> Controller
|
||||
Controller --> Client
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go#L7-L7)
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_info.go](file://internal/models/payfor/payfor_info.go)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 代付网关控制器分析
|
||||
PayForGateway控制器是代付功能的入口点,继承自web.Controller,提供了多个代付相关的API端点。尽管这些方法被注释,但其设计意图清晰,涵盖了代付请求、结果查询、余额查询和手动结果处理等完整流程。
|
||||
|
||||
#### 控制器结构
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PayForGateway {
|
||||
+PayFor()
|
||||
+PayForQuery()
|
||||
+QuerySupplierPayForResult()
|
||||
+SolvePayForResult()
|
||||
+Balance()
|
||||
+checkParams()
|
||||
}
|
||||
PayForGateway --> web.Controller : "继承"
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go#L7-L7)
|
||||
|
||||
#### API端点流程
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([接收请求]) --> ValidateParams["验证参数完整性"]
|
||||
ValidateParams --> ParamsValid{"参数有效?"}
|
||||
ParamsValid --> |否| ReturnError["返回参数错误"]
|
||||
ParamsValid --> |是| VerifySign["验证签名"]
|
||||
VerifySign --> SignValid{"签名有效?"}
|
||||
SignValid --> |否| ReturnAuthError["返回认证错误"]
|
||||
SignValid --> |是| ProcessBusiness["处理业务逻辑"]
|
||||
ProcessBusiness --> GenerateResponse["生成响应"]
|
||||
GenerateResponse --> End([返回结果])
|
||||
ReturnError --> End
|
||||
ReturnAuthError --> End
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go)
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
|
||||
**本节来源**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go)
|
||||
|
||||
### 代付服务分析
|
||||
pay_for服务模块实现了代付功能的核心业务逻辑,包括自动代付、结果查询和余额查询等功能。服务层与模型层紧密协作,确保数据的一致性和完整性。
|
||||
|
||||
#### 服务功能关系
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PayForResultQuery {
|
||||
+PayForResultQuery(ctx, params)
|
||||
}
|
||||
class BalanceQuery {
|
||||
+BalanceQuery(ctx, params)
|
||||
}
|
||||
class checkSettAmount {
|
||||
+checkSettAmount(ctx, settAmount)
|
||||
}
|
||||
PayForResultQuery --> GetMerchantByPasskey : "调用"
|
||||
PayForResultQuery --> Md5Verify : "调用"
|
||||
PayForResultQuery --> GetPayForByMerchantOrderId : "调用"
|
||||
PayForResultQuery --> GetMD5Sign : "调用"
|
||||
BalanceQuery --> GetMerchantByPasskey : "调用"
|
||||
BalanceQuery --> Md5Verify : "调用"
|
||||
BalanceQuery --> GetAccountByUid : "调用"
|
||||
BalanceQuery --> GetMD5Sign : "调用"
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
|
||||
**本节来源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
|
||||
### 代付信息模型分析
|
||||
PayforInfo模型定义了代付记录的数据结构,包含代付交易的完整信息,从商户信息到银行账户详情,再到交易状态和金额明细。
|
||||
|
||||
#### 数据模型结构
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PayforInfo {
|
||||
+PayforUid : string
|
||||
+MerchantUid : string
|
||||
+MerchantName : string
|
||||
+MerchantOrderId : string
|
||||
+BankOrderId : string
|
||||
+PayforFee : float64
|
||||
+PayforAmount : float64
|
||||
+PayforTotalAmount : float64
|
||||
+BankCode : string
|
||||
+BankName : string
|
||||
+BankAccountName : string
|
||||
+BankAccountNo : string
|
||||
+Status : string
|
||||
+IsSend : string
|
||||
+RequestTime : time.Time
|
||||
+CreateTime : time.Time
|
||||
+UpdateTime : time.Time
|
||||
}
|
||||
class PayforInfoMethods {
|
||||
+InsertPayFor(ctx, payFor)
|
||||
+IsExistPayForByBankOrderId(bankOrderId)
|
||||
+IsExistPayForByMerchantOrderId(merchantOrderId)
|
||||
+GetPayForByBankOrderId(ctx, bankOrderId)
|
||||
+GetPayForByMerchantOrderId(ctx, merchantOrderId)
|
||||
+UpdatePayFor(ctx, payFor)
|
||||
}
|
||||
PayforInfo <|-- PayforInfoMethods : "实现"
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [payfor_info.go](file://internal/models/payfor/payfor_info.go)
|
||||
|
||||
**本节来源**
|
||||
- [payfor_info.go](file://internal/models/payfor/payfor_info.go)
|
||||
|
||||
### 响应结构分析
|
||||
响应结构体定义了API的输出格式,确保了前后端交互的一致性和可预测性。不同的代付操作返回不同但结构相似的响应。
|
||||
|
||||
#### 响应结构关系
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PayForResponse {
|
||||
+ResultCode : string
|
||||
+ResultMsg : string
|
||||
+MerchantOrderId : string
|
||||
+SettAmount : string
|
||||
+SettFee : string
|
||||
+Sign : string
|
||||
}
|
||||
class PayForQueryResponse {
|
||||
+ResultMsg : string
|
||||
+MerchantOrderId : string
|
||||
+SettAmount : string
|
||||
+SettFee : string
|
||||
+SettStatus : string
|
||||
+Sign : string
|
||||
}
|
||||
class BalanceResponse {
|
||||
+ResultCode : string
|
||||
+Balance : string
|
||||
+AvailableAmount : string
|
||||
+FreezeAmount : string
|
||||
+WaitAmount : string
|
||||
+LoanAmount : string
|
||||
+PayforAmount : string
|
||||
+ResultMsg : string
|
||||
+Sign : string
|
||||
}
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [payfor_resp.go](file://internal/schema/response/payfor_resp.go)
|
||||
|
||||
**本节来源**
|
||||
- [payfor_resp.go](file://internal/schema/response/payfor_resp.go)
|
||||
|
||||
## 依赖分析
|
||||
代付功能依赖于多个内部模块和外部服务,形成了复杂的依赖网络。理解这些依赖关系对于维护和扩展系统至关重要。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
PayForController[PayForGateway控制器] --> PayForService[pay_for服务]
|
||||
PayForService --> MerchantModel[商户模型]
|
||||
PayForService --> PayforModel[代付模型]
|
||||
PayForService --> AccountModel[账户模型]
|
||||
PayForService --> Utils[工具模块]
|
||||
Utils --> SignVerify[签名验证]
|
||||
PayForController --> Router[路由配置]
|
||||
PayForSolve[payfor_solve] --> PayforModel
|
||||
PayForSolve --> AccountModel
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go)
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
- [router.go](file://internal/routers/router.go)
|
||||
|
||||
**本节来源**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go)
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
- [router.go](file://internal/routers/router.go)
|
||||
|
||||
## 性能考虑
|
||||
代付功能在设计时考虑了性能和可靠性。通过使用数据库事务确保数据一致性,采用签名验证机制保障安全性,并通过合理的错误处理提高系统的健壮性。尽管功能被注释,但其设计体现了对高并发场景下性能和一致性的考量。
|
||||
|
||||
## 故障排除指南
|
||||
当代付功能出现问题时,应首先检查日志输出,重点关注签名验证失败、参数校验错误和数据库操作异常。由于相关路由被注释,需要确认是否已正确启用代付功能。同时,检查商户配置和通道设置是否正确,确保代付流程所需的各项条件都已满足。
|
||||
|
||||
**本节来源**
|
||||
- [payfor_controller.go](file://internal/controllers/payfor_controller.go)
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
|
||||
## 结论
|
||||
尽管kami_gateway的代付功能当前被注释,但其代码结构清晰,设计完整。系统提供了从代付请求到结果处理的全流程支持,包括自动代付、结果查询、余额查询和手动结果修正等关键功能。通过详细的API文档和清晰的代码结构,为未来启用和维护代付功能奠定了坚实基础。
|
||||
181
.qoder/repowiki/zh/content/API参考/回调API.md
Normal file
181
.qoder/repowiki/zh/content/API参考/回调API.md
Normal file
@@ -0,0 +1,181 @@
|
||||
<cite>
|
||||
**本文档中引用的文件**
|
||||
- [router.go](file://internal/routers/router.go)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go)
|
||||
- [mf178_v2.go](file://internal/service/supplier/third_party/mf178_v2.go)
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go)
|
||||
- [order_notify.go](file://internal/service/notify/order_notify.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [回调API](#回调api)
|
||||
2. [核心组件](#核心组件)
|
||||
3. [处理流程](#处理流程)
|
||||
4. [统一处理模式与差异](#统一处理模式与差异)
|
||||
5. [错误处理与重试机制](#错误处理与重试机制)
|
||||
6. [结论](#结论)
|
||||
|
||||
## 回调API
|
||||
|
||||
kami_gateway系统通过一系列回调API端点接收来自不同第三方支付渠道的支付结果通知。这些端点包括`/mfcard/notifyV2`、`/appleCard/notify`、`/tMallGame/notify`等,每个端点对应一个特定的支付渠道,如爱博、京东卡、天猫游戏等。当支付渠道完成交易后,会向这些预设的URL发送HTTP请求,携带支付结果信息,从而触发网关内部的订单状态更新流程。
|
||||
|
||||
这些回调接口由`internal/routers/router.go`文件中的路由配置定义,将不同的URL路径映射到对应的实现结构体(如`MFCardV2Impl`、`AppleCardImpl`、`TMAllGameImpl`)的`PayNotify`方法上。整个回调系统的设计旨在确保支付结果的最终一致性,通过验证、处理和状态更新等一系列操作,保证商户和平台的账务准确无误。
|
||||
|
||||
**Section sources**
|
||||
- [router.go](file://internal/routers/router.go#L49-L74)
|
||||
|
||||
## 核心组件
|
||||
|
||||
回调API的核心处理逻辑由三个关键组件构成:路由控制器、支付渠道实现和订单解决服务。
|
||||
|
||||
首先,`internal/routers/router.go`文件定义了所有回调端点的路由规则。例如,`web.Router("/mfcard/notifyV2", &third_party.MFCardV2Impl{}, "*:PayNotify")`这一行代码将`/mfcard/notifyV2`路径的请求路由到`MFCardV2Impl`结构体的`PayNotify`方法。这为每个支付渠道提供了独立的入口。
|
||||
|
||||
其次,各个支付渠道的处理逻辑封装在`internal/service/supplier/third_party/`目录下的具体实现中。`MFCardV2Impl`、`AppleCardImpl`和`TMAllGameImpl`等结构体都嵌入了`web.Controller`,并实现了`PayNotify`方法。这些方法负责接收、解析和验证来自不同渠道的回调数据。
|
||||
|
||||
最后,`SolvePaySuccess`和`SolvePayFail`函数是订单状态更新的核心。它们位于`internal/service/pay_solve.go`文件中,被所有`PayNotify`方法调用。`SolvePaySuccess`在支付成功时执行,负责更新订单状态为成功、增加商户账户余额、记录动账历史和更新通道统计数据。`SolvePayFail`则在支付失败时调用,将订单状态标记为失败,并记录失败原因。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class MFCardV2Impl {
|
||||
+PayNotify()
|
||||
}
|
||||
class AppleCardImpl {
|
||||
+PayNotify()
|
||||
}
|
||||
class TMAllGameImpl {
|
||||
+PayNotify()
|
||||
}
|
||||
class PaySolveService {
|
||||
+SolvePaySuccess()
|
||||
+SolvePayFail()
|
||||
}
|
||||
MFCardV2Impl --> PaySolveService : "调用"
|
||||
AppleCardImpl --> PaySolveService : "调用"
|
||||
TMAllGameImpl --> PaySolveService : "调用"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [router.go](file://internal/routers/router.go#L49-L74)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L37-L254)
|
||||
- [mf178_v2.go](file://internal/service/supplier/third_party/mf178_v2.go#L187-L253)
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L202-L240)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L167-L223)
|
||||
|
||||
**Section sources**
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L37-L254)
|
||||
- [mf178_v2.go](file://internal/service/supplier/third_party/mf178_v2.go#L37-L39)
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L33-L35)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L33-L35)
|
||||
|
||||
## 处理流程
|
||||
|
||||
回调API的处理流程是一个严谨的、多步骤的验证和更新过程,确保了数据的完整性和业务逻辑的正确性。
|
||||
|
||||
1. **接收与查找**:当一个回调请求到达时,其`PayNotify`方法首先从请求参数中提取出订单号(如`attach`或`merchantOrder`)。然后,系统调用`order.GetOrderByBankOrderId(ctx, orderNo)`来根据这个订单号从数据库中查找对应的订单信息。如果订单不存在,系统会立即终止处理并返回失败响应。
|
||||
|
||||
2. **验证与完整性检查**:在确认订单存在后,系统会进行一系列验证。这包括检查支付通道是否有效(通过`road.GetRoadInfoByRoadUid`),以及验证回调数据的签名。例如,`MFCardV2Impl`使用`utils.GetMD5SignMF`函数根据预设的密钥重新计算签名,并与回调中的`sign`参数进行比对。如果签名不匹配,处理将被终止。
|
||||
|
||||
3. **状态判断与解决**:一旦数据通过验证,系统会根据回调中的状态码来决定后续操作。以`MFCardV2Impl`为例,当`status`为`"9"`时,系统会调用`service.SolvePaySuccess`;当`status`为`"8"`时,则调用`service.SolvePayFail`。`SolvePaySuccess`函数在一个数据库事务中执行,确保了所有相关操作(更新订单、更新账户、插入动账记录等)的原子性。
|
||||
|
||||
4. **响应返回**:处理完成后,系统会向支付渠道返回一个响应。通常,处理成功时返回`"SUCCESS"`,失败时返回`"FAIL"`。这个响应告诉支付渠道网关是否成功接收并处理了通知。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[接收回调请求] --> B[提取订单号]
|
||||
B --> C[查找订单信息]
|
||||
C --> D{订单存在?}
|
||||
D --> |否| E[返回FAIL]
|
||||
D --> |是| F[验证支付通道]
|
||||
F --> G[验证数据签名]
|
||||
G --> H{签名有效?}
|
||||
H --> |否| E
|
||||
H --> |是| I[检查支付状态]
|
||||
I --> J{支付成功?}
|
||||
J --> |是| K[调用SolvePaySuccess]
|
||||
J --> |否| L[调用SolvePayFail]
|
||||
K --> M[返回SUCCESS]
|
||||
L --> M
|
||||
M --> N[结束]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [mf178_v2.go](file://internal/service/supplier/third_party/mf178_v2.go#L187-L253)
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L202-L240)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L167-L223)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L37-L254)
|
||||
|
||||
**Section sources**
|
||||
- [mf178_v2.go](file://internal/service/supplier/third_party/mf178_v2.go#L187-L253)
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L202-L240)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L167-L223)
|
||||
|
||||
## 统一处理模式与差异
|
||||
|
||||
尽管各个支付渠道的回调接口各不相同,但它们遵循一个高度统一的处理模式,同时在细节上存在必要的差异。
|
||||
|
||||
**统一处理模式**:
|
||||
所有`PayNotify`方法都遵循相同的高层逻辑:接收参数 -> 查找订单 -> 验证通道 -> 验证签名 -> 判断状态 -> 调用`SolvePaySuccess`或`SolvePayFail` -> 返回响应。这种模式确保了核心业务逻辑(订单状态更新)的集中化和一致性,避免了代码重复。
|
||||
|
||||
**渠道间差异**:
|
||||
差异主要体现在以下几个方面:
|
||||
1. **URL路径**:每个渠道有唯一的回调路径,如`/mfcard/notifyV2`和`/appleCard/notify`。
|
||||
2. **参数名称**:不同渠道使用的参数名不同。例如,爱博使用`attach`作为订单号,而天猫游戏使用`merchantOrder`。
|
||||
3. **签名算法**:虽然都使用签名验证,但具体的算法和密钥来源可能不同。`MFCardV2Impl`从`roadInfo.Params`中获取`appSecret`,而`TMAllGameImpl`使用一个名为`TmpEncrypt`的通用加密函数。
|
||||
4. **状态码定义**:各渠道的状态码含义不同。爱博用`"9"`表示成功,`"8"`表示失败;天猫游戏用`"finished"`表示成功。
|
||||
|
||||
这些差异通过在各自的`PayNotify`方法中实现特定的解析和验证逻辑来处理,而核心的订单解决逻辑则保持不变。
|
||||
|
||||
**Section sources**
|
||||
- [mf178_v2.go](file://internal/service/supplier/third_party/mf178_v2.go#L187-L253)
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L202-L240)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L167-L223)
|
||||
|
||||
## 错误处理与重试机制
|
||||
|
||||
系统不仅处理来自支付渠道的回调,还负责向商户系统发送支付结果通知,并为此设计了完善的错误处理和重试机制,以确保最终一致性。
|
||||
|
||||
当`SolvePaySuccess`或`SolvePayFail`被调用后,它们会异步触发`CreateOrderNotifyInfo`函数。该函数将商户的回调信息(包括订单号、金额、状态等)插入`notify_info`数据库表,并将订单号发送到名为`config.MqOrderNotify`的消息队列中。
|
||||
|
||||
`internal/service/notify/order_notify.go`文件中的`CreateOrderNotifyConsumer`函数是一个消息队列消费者,它监听该队列。每当收到一个订单号,它就会启动`SendOrderNotify`流程,向商户配置的`notifyUrl`发起HTTP GET请求。
|
||||
|
||||
为了应对网络波动或商户服务器暂时不可用的情况,系统实现了指数退避重试策略:
|
||||
- 第1次重试:延迟1分钟
|
||||
- 第2次重试:延迟2分钟
|
||||
- 第3次重试:延迟5分钟
|
||||
- 第4次重试:延迟15分钟
|
||||
- 第5次及以后:延迟30分钟
|
||||
|
||||
这个策略由`GetOrderNotifyMinute`函数定义。如果在`consts.LimitTimes`(通常为5次)尝试后仍未能收到商户返回的`"SUCCESS"`响应,该订单的回调状态将被标记为`"fail"`,并停止重试。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Gateway as kami_gateway
|
||||
participant MQ as 消息队列
|
||||
participant Merchant as 商户系统
|
||||
Gateway->>MQ : 发送订单号 (bankOrderId)
|
||||
loop 消费者轮询
|
||||
MQ->>Gateway : 接收订单号
|
||||
Gateway->>Merchant : GET /notify?params...
|
||||
alt 响应成功
|
||||
Merchant-->>Gateway : "SUCCESS"
|
||||
Gateway->>Gateway : 标记为成功
|
||||
else 响应失败或超时
|
||||
Gateway->>Gateway : 记录失败,启动定时器
|
||||
Note right of Gateway : 延迟1/2/5/15/30分钟后...
|
||||
Gateway->>MQ : 重新入队或定时重试
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [order_notify.go](file://internal/service/notify/order_notify.go#L0-L218)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L148-L195)
|
||||
|
||||
**Section sources**
|
||||
- [order_notify.go](file://internal/service/notify/order_notify.go#L0-L218)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L148-L195)
|
||||
|
||||
## 结论
|
||||
|
||||
kami_gateway的回调API系统是一个设计精良、健壮可靠的支付结果处理机制。它通过统一的`PayNotify`接口模式和集中的`SolvePaySuccess`/`SolvePayFail`服务,有效地管理了多个第三方支付渠道的集成。系统在接收和处理支付渠道通知时,严格执行订单查找、通道验证和签名验证,确保了数据的安全性。同时,通过基于消息队列和指数退避的重试机制,系统能够可靠地将支付结果通知到商户端,即使在面对网络或服务故障时,也能保证最终一致性。这种分层、模块化的设计使得系统易于维护和扩展,为平台的稳定运行提供了坚实的基础。
|
||||
280
.qoder/repowiki/zh/content/API参考/支付API.md
Normal file
280
.qoder/repowiki/zh/content/API参考/支付API.md
Normal file
@@ -0,0 +1,280 @@
|
||||
# 支付API
|
||||
|
||||
<cite>
|
||||
**本文档中引用的文件**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go)
|
||||
- [pay_service.go](file://internal/service/pay_service.go)
|
||||
- [sign_verify.go](file://internal/utils/sign_verify.go)
|
||||
- [order.go](file://internal/schema/request/order.go)
|
||||
- [pay_resp.go](file://internal/schema/response/pay_resp.go)
|
||||
- [order_info.go](file://internal/models/order/order_info.go)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go)
|
||||
- [road_info.go](file://internal/models/road/road_info.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [核心功能概述](#核心功能概述)
|
||||
3. [API端点详解](#api端点详解)
|
||||
4. [请求流程分析](#请求流程分析)
|
||||
5. [签名验证机制](#签名验证机制)
|
||||
6. [响应结构说明](#响应结构说明)
|
||||
7. [错误处理与限制策略](#错误处理与限制策略)
|
||||
8. [支付通道选择逻辑](#支付通道选择逻辑)
|
||||
9. [第三方支付渠道调用](#第三方支付渠道调用)
|
||||
10. [JSON请求/响应示例](#json请求响应示例)
|
||||
|
||||
## 简介
|
||||
本文档详细说明了kami_gateway系统中与支付相关的两个核心API端点:`/gateway/scan` 和 `/gateway/createOrder`。这两个接口负责处理商户发起的支付下单请求,涵盖从参数验证、签名校验、订单生成到最终调用第三方支付渠道的完整流程。文档重点解析了请求参数结构、MD5签名机制、支付通道选择逻辑(ChooseRoadV2)、订单记录生成以及自有渠道与第三方渠道的区别处理方式。
|
||||
|
||||
**Section sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L1-L543)
|
||||
|
||||
## 核心功能概述
|
||||
/gateway/scan 和 /gateway/createOrder 是kami_gateway处理支付请求的核心入口。它们共同实现了商户支付订单的创建与处理,支持扫码支付场景。系统通过统一的参数验证、商户身份识别、支付通道选择和订单状态管理,确保交易的安全性与可靠性。两个接口均采用基于MD5的签名验证机制来保证请求的完整性,并通过灵活的支付通道配置支持多种第三方支付供应商。
|
||||
|
||||
**Section sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L1-L543)
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L30-L74)
|
||||
|
||||
## API端点详解
|
||||
|
||||
### /gateway/scan 端点
|
||||
该端点用于处理商户直接提交的扫码支付请求。它接收包含订单信息的HTTP GET请求,执行完整的支付流程,包括参数验证、签名校验、支付通道选择、订单生成及调用上游支付接口。
|
||||
|
||||
**请求方法**: GET
|
||||
**主要参数**:
|
||||
- `orderNo`: 商户订单号
|
||||
- `productCode`: 产品编码
|
||||
- `orderPrice`: 订单金额
|
||||
- `notifyUrl`: 回调通知地址
|
||||
- `payKey`: 商户密钥
|
||||
- `timestamp`: 时间戳
|
||||
- `sign`: 签名值
|
||||
- `ip`: 客户端IP
|
||||
- `deviceId`: 设备ID
|
||||
|
||||
### /gateway/createOrder 端点
|
||||
该端点用于创建支付订单并返回支付链接(payUrl)。与/gateway/scan不同,此接口不立即执行支付动作,而是生成一个可后续访问的支付页面链接,适用于需要跳转支付页面的场景。
|
||||
|
||||
**请求方法**: POST
|
||||
**请求体格式**: JSON
|
||||
**主要参数**:
|
||||
- `payKey`: 商户密钥
|
||||
- `orderNo`: 商户订单号
|
||||
- `orderPrice`: 订单金额
|
||||
- `orderPeriod`: 订单有效期(小时)
|
||||
- `notifyUrl`: 回调地址
|
||||
- `productCode`: 产品编码
|
||||
- `timestamp`: 时间戳
|
||||
- `sign`: 签名值
|
||||
|
||||
**Section sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L1-L543)
|
||||
- [order.go](file://internal/schema/request/order.go#L1-L35)
|
||||
|
||||
## 请求流程分析
|
||||
当商户调用支付API时,系统执行以下关键步骤:
|
||||
|
||||
1. **参数提取与基础验证**:从请求中提取所有参数并进行非空和格式校验。
|
||||
2. **商户身份识别**:根据`payKey`查询商户信息,确认商户存在且状态正常。
|
||||
3. **签名验证**:使用商户密钥对请求参数进行MD5签名比对,确保请求未被篡改。
|
||||
4. **业务参数验证**:检查订单号、金额、产品编码、通知地址等业务参数的有效性。
|
||||
5. **支付通道选择(ChooseRoadV2)**:根据产品编码查找对应的支付通道配置。
|
||||
6. **订单重复性检查**:通过全局映射`orderSubmitLimiter`防止同一订单号在短时间内被重复提交。
|
||||
7. **订单记录生成**:调用`GenerateRecord`服务生成系统内部订单记录。
|
||||
8. **支付通道调用**:通过`third_party.GetPaySupplierByCode`获取第三方支付供应商实例并执行支付操作。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[接收商户请求] --> B[提取请求参数]
|
||||
B --> C[商户身份验证]
|
||||
C --> D[签名验证]
|
||||
D --> E[参数合法性检查]
|
||||
E --> F[选择支付通道 ChooseRoadV2]
|
||||
F --> G[检查订单重复提交]
|
||||
G --> H[生成订单记录]
|
||||
H --> I[调用第三方支付渠道]
|
||||
I --> J[返回响应]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L1-L543)
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L30-L74)
|
||||
|
||||
**Section sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L1-L543)
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L30-L74)
|
||||
|
||||
## 签名验证机制
|
||||
系统采用基于MD5的签名验证机制来保障API请求的安全性。相关逻辑实现在`sign_verify.go`文件中,主要涉及以下函数:
|
||||
|
||||
- `GetMD5SignMF`: 生成MD5签名的核心函数,对参数按字典序排序后拼接并附加商户密钥进行MD5计算。
|
||||
- `Md5MFVerify`: 验证请求签名的函数,会排除`sign`、`deviceId`和`ip`字段后重新计算签名进行比对。
|
||||
|
||||
签名生成规则如下:
|
||||
1. 将除`sign`、`deviceId`、`ip`外的所有请求参数按键名进行字典序排序。
|
||||
2. 拼接所有参数为`key1value1key2value2...`格式。
|
||||
3. 在末尾追加商户密钥(`paySecret`)。
|
||||
4. 对最终字符串进行MD5哈希计算,并转换为小写。
|
||||
|
||||
**Section sources**
|
||||
- [sign_verify.go](file://internal/utils/sign_verify.go#L1-L104)
|
||||
|
||||
## 响应结构说明
|
||||
|
||||
### ScanSuccessData 成功响应
|
||||
当支付请求成功处理时,返回`ScanSuccessData`结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"orderNo": "string",
|
||||
"sign": "string",
|
||||
"orderPrice": "string",
|
||||
"statusCode": "string",
|
||||
"msg": "string",
|
||||
"code": 0,
|
||||
"payUrl": "string"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 描述 |
|
||||
|------|------|------|
|
||||
| orderNo | string | 系统生成的订单号 |
|
||||
| sign | string | 响应签名 |
|
||||
| orderPrice | string | 订单金额 |
|
||||
| statusCode | string | 状态码 ("00"表示成功) |
|
||||
| msg | string | 响应消息 |
|
||||
| code | int | 业务状态码 (0表示成功) |
|
||||
| payUrl | string | 支付链接(如有) |
|
||||
|
||||
### ScanFailData 失败响应
|
||||
当支付请求处理失败时,返回`ScanFailData`结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"payKey": "string",
|
||||
"statusCode": "string",
|
||||
"msg": "string",
|
||||
"code": -1
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 描述 |
|
||||
|------|------|------|
|
||||
| payKey | string | 商户密钥 |
|
||||
| statusCode | string | 状态码 ("01"表示失败) |
|
||||
| msg | string | 错误消息 |
|
||||
| code | int | 业务状态码 (-1表示失败) |
|
||||
|
||||
**Section sources**
|
||||
- [pay_resp.go](file://internal/schema/response/pay_resp.go#L1-L38)
|
||||
|
||||
## 错误处理与限制策略
|
||||
系统实现了多层次的错误处理与安全限制机制:
|
||||
|
||||
### 订单重复提交限制
|
||||
通过`sync.Map`类型的`orderSubmitLimiter`全局变量实现订单号提交频率限制。默认情况下,同一订单号在2秒内只能提交一次,防止恶意重放攻击。
|
||||
|
||||
```go
|
||||
func isAllowed(orderNo string, intervalSec int64) bool
|
||||
```
|
||||
|
||||
### IP限制机制
|
||||
系统支持基于IP的访问控制,可通过`backend.GetIPIsRestricted`函数检查特定IP是否被限制。虽然相关代码被注释,但架构上已预留此功能接口。
|
||||
|
||||
### 其他验证规则
|
||||
- **商户状态验证**:确保商户处于激活状态(`ACTIVE`)
|
||||
- **通道配置验证**:检查商户是否已配置对应支付通道
|
||||
- **金额范围验证**:确保订单金额在通道允许的最小/最大限额内
|
||||
- **产品编码验证**:确认产品编码对应的支付通道已配置
|
||||
|
||||
**Section sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L1-L543)
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L1-L437)
|
||||
|
||||
## 支付通道选择逻辑
|
||||
`ChooseRoadV2`函数是支付通道选择的核心逻辑,其实现位于`pay_service.go`文件中。该函数根据商户请求中的`productCode`查找对应的支付通道配置,并进行一系列有效性验证:
|
||||
|
||||
1. 解析订单金额
|
||||
2. 根据`productCode`获取`roadInfo`
|
||||
3. 验证通道是否存在且状态正常
|
||||
4. 检查商户是否已配置该通道(通过`merchant_deploy`信息)
|
||||
5. 验证订单金额是否符合通道的费率配置
|
||||
6. 返回包含通道信息、平台费率、代理费率等数据的响应对象
|
||||
|
||||
该函数确保只有符合所有业务规则的请求才能进入后续支付流程。
|
||||
|
||||
**Section sources**
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L30-L74)
|
||||
- [road_info.go](file://internal/models/road/road_info.go#L1-L202)
|
||||
|
||||
## 第三方支付渠道调用
|
||||
系统通过工厂模式管理第三方支付渠道,核心函数为`GetPaySupplierByCode`,定义在`third_party/init.go`中:
|
||||
|
||||
```go
|
||||
func GetPaySupplierByCode(code string) supplier.PayInterface {
|
||||
return registerSupplier[code]
|
||||
}
|
||||
```
|
||||
|
||||
该函数根据`productUid`从注册表中获取对应的支付供应商实例。调用流程如下:
|
||||
|
||||
1. 从`roadInfo`中获取`productUid`作为供应商代码
|
||||
2. 调用`GetPaySupplierByCode`获取供应商接口实例
|
||||
3. 执行`Scan`方法发起实际支付请求
|
||||
4. 处理返回结果并更新订单状态
|
||||
|
||||
系统支持区分自有渠道和第三方渠道:自有渠道可能直接返回支付链接,而第三方渠道需通过API调用完成支付。
|
||||
|
||||
**Section sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L1-L543)
|
||||
- [init.go](file://internal/service/supplier/third_party/init.go#L151-L153)
|
||||
|
||||
## JSON请求/响应示例
|
||||
|
||||
### /gateway/createOrder 请求示例
|
||||
```json
|
||||
{
|
||||
"payKey": "merchant_123",
|
||||
"orderNo": "M20231107001",
|
||||
"orderPrice": 100.00,
|
||||
"orderPeriod": 24,
|
||||
"notifyUrl": "https://merchant.com/notify",
|
||||
"productCode": "ALI_SCAN",
|
||||
"timestamp": 1699344000,
|
||||
"sign": "d41d8cd98f00b204e9800998ecf8427e"
|
||||
}
|
||||
```
|
||||
|
||||
### /gateway/createOrder 成功响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": {
|
||||
"productCode": "ALI_SCAN",
|
||||
"paymentName": "支付宝扫码",
|
||||
"transactionType": "SCAN",
|
||||
"payUrl": "https://gateway.kami.com/pay?sign=abc123",
|
||||
"orderNo": "K20231107001",
|
||||
"merchantOrderNo": "M20231107001",
|
||||
"sign": "e3b0c44298fc1c149afbf4c8996fb924"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### /gateway/scan 失败响应示例
|
||||
```json
|
||||
{
|
||||
"payKey": "merchant_123",
|
||||
"statusCode": "01",
|
||||
"msg": "请勿频繁提交",
|
||||
"code": -1
|
||||
}
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L1-L543)
|
||||
- [order.go](file://internal/schema/request/order.go#L1-L35)
|
||||
- [pay_resp.go](file://internal/schema/response/pay_resp.go#L1-L38)
|
||||
170
.qoder/repowiki/zh/content/API参考/查询API.md
Normal file
170
.qoder/repowiki/zh/content/API参考/查询API.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# 查询API
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go)
|
||||
- [merchant_query.go](file://internal/schema/query/merchant_query.go)
|
||||
- [sign_verify.go](file://internal/utils/sign_verify.go)
|
||||
- [response.go](file://internal/models/response/response.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [商户查询接口](#商户查询接口)
|
||||
3. [供应商订单查询接口](#供应商订单查询接口)
|
||||
4. [订单重新调度接口](#订单重新调度接口)
|
||||
5. [响应格式](#响应格式)
|
||||
|
||||
## 简介
|
||||
本文档详细说明了kami_gateway系统中的查询API,包括商户查询、供应商订单查询和订单重新调度功能。文档涵盖了接口的认证机制、参数验证流程、业务逻辑处理以及各种情况下的响应格式。
|
||||
|
||||
## 商户查询接口
|
||||
|
||||
`/gateway/merchant/query`端点为下游商户提供订单查询服务。该接口通过appKey、timestamp和sign参数进行安全验证,确保只有授权商户可以查询其订单信息。
|
||||
|
||||
接口首先验证所有必需参数是否提供,然后通过appKey查找商户信息。认证机制使用两种签名验证方法:`GetMD5SignMF`和`GetMD5Sign`,确保签名的正确性。验证通过后,系统查询订单信息并返回包含订单号、卡号、卡密、面值等敏感信息的响应。
|
||||
|
||||
**Section sources**
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L140-L224)
|
||||
- [sign_verify.go](file://internal/utils/sign_verify.go#L89-L102)
|
||||
|
||||
## 供应商订单查询接口
|
||||
|
||||
`/gateway/supplier/order/query`端点为上游供应商提供订单查询服务。该接口允许供应商通过bankOrderId查询订单状态和相关信息。
|
||||
|
||||
接口处理流程包括:接收bankOrderId参数,记录查询日志,然后调用`SupplierOrderQueryResult`函数获取查询结果。该函数会从数据库中检索订单信息,并以字符串形式返回结果。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 供应商 as 上游供应商
|
||||
participant 控制器 as OrderController
|
||||
participant 查询服务 as query.SupplierOrderQueryResult
|
||||
供应商->>控制器 : GET /gateway/supplier/order/query?bankOrderId=xxx
|
||||
控制器->>控制器 : 记录查询日志
|
||||
控制器->>查询服务 : 调用SupplierOrderQueryResult(bankOrderId)
|
||||
查询服务-->>控制器 : 返回查询结果
|
||||
控制器-->>供应商 : 返回JSON响应
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L30-L40)
|
||||
|
||||
**Section sources**
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L30-L40)
|
||||
- [merchant_query.go](file://internal/schema/query/merchant_query.go#L28-L75)
|
||||
|
||||
## 订单重新调度接口
|
||||
|
||||
`OrderSchedule`接口提供订单重新调度功能,允许在特定条件下重新处理订单。调度条件包括:
|
||||
|
||||
1. 订单状态不能为"success"(成功状态订单不可重新调度)
|
||||
2. 处于"wait"(等待)状态的订单需要在创建3分钟后才能重新调度
|
||||
3. 通道必须允许重新调度
|
||||
|
||||
调度执行流程:
|
||||
1. 验证调度条件
|
||||
2. 修改订单状态为"wait"
|
||||
3. 调用供应商的Scan方法重新提交订单
|
||||
4. 根据扫描结果处理成功或失败情况
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> 验证订单状态
|
||||
验证订单状态 --> |状态为success| 返回"成功状态订单不可重新调度"
|
||||
验证订单状态 --> |状态为wait且创建时间<3分钟| 返回"等待订单需3分钟后调度"
|
||||
验证订单状态 --> |通道不允许调度| 返回"当前通道不允许重新调度"
|
||||
验证订单状态 --> |通过验证| 修改订单状态
|
||||
修改订单状态 --> 调用供应商Scan
|
||||
调用供应商Scan --> |状态为01| 处理成功回调
|
||||
调用供应商Scan --> |状态为00| 返回"success"
|
||||
调用供应商Scan --> |其他状态| 处理失败通知
|
||||
处理成功回调 --> End([结束])
|
||||
返回"success" --> End
|
||||
处理失败通知 --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L42-L95)
|
||||
|
||||
**Section sources**
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L42-L95)
|
||||
|
||||
## 响应格式
|
||||
|
||||
### 成功响应示例
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"msg": "订单获取成功",
|
||||
"data": {
|
||||
"orderNo": "bank123",
|
||||
"cardNo": "1234567890",
|
||||
"cardPwd": "password123",
|
||||
"status": "success",
|
||||
"faceVal": 100.0,
|
||||
"amount": "100.00",
|
||||
"cardReturnData": "充值成功"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应示例
|
||||
```json
|
||||
{
|
||||
"code": -1,
|
||||
"msg": "参数错误",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"code": -1,
|
||||
"msg": "key错误",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"code": -1,
|
||||
"msg": "签名错误",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"code": -1,
|
||||
"msg": "订单不存在",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
响应数据结构定义在`response.OrderQueryResp`中,包含订单号、卡号、卡密、状态、面值、金额和卡片返回数据等字段。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Resp[T] {
|
||||
+int Code
|
||||
+string Msg
|
||||
+T Data
|
||||
}
|
||||
class OrderQueryResp {
|
||||
+string OrderNo
|
||||
+string CardNo
|
||||
+string CardPwd
|
||||
+string Status
|
||||
+float64 FaceVal
|
||||
+string Amount
|
||||
+string CardReturnData
|
||||
}
|
||||
Resp[T] <|-- Resp[OrderQueryResp]
|
||||
Resp[OrderQueryResp] --> OrderQueryResp : "包含"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [response.go](file://internal/models/response/response.go#L1-L28)
|
||||
|
||||
**Section sources**
|
||||
- [response.go](file://internal/models/response/response.go#L1-L28)
|
||||
329
.qoder/repowiki/zh/content/开发者指南.md
Normal file
329
.qoder/repowiki/zh/content/开发者指南.md
Normal file
@@ -0,0 +1,329 @@
|
||||
# 开发者指南
|
||||
|
||||
<cite>
|
||||
**本文档中引用的文件**
|
||||
- [main.go](file://main.go)
|
||||
- [build.sh](file://build.sh)
|
||||
- [go.mod](file://go.mod)
|
||||
- [conf/app.conf](file://conf/app.conf)
|
||||
- [deploy/docker-compose.yaml](file://deploy/docker-compose.yaml)
|
||||
- [deploy/docker-compose-local.yaml](file://deploy/docker-compose-local.yaml)
|
||||
- [internal/config/config.go](file://internal/config/config.go)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [开发环境设置](#开发环境设置)
|
||||
4. [构建与运行](#构建与运行)
|
||||
5. [Docker Compose 配置](#docker-compose-配置)
|
||||
6. [调试与性能分析](#调试与性能分析)
|
||||
7. [代码修改示例](#代码修改示例)
|
||||
8. [测试与覆盖率](#测试与覆盖率)
|
||||
|
||||
## 简介
|
||||
本指南旨在为新加入的开发者提供一个全面的入门指导,帮助您快速搭建本地开发环境并理解项目的基本架构。我们将详细介绍从克隆仓库到成功运行应用的完整流程,包括环境配置、构建运行、Docker部署、调试技巧以及代码修改示例。
|
||||
|
||||
## 项目结构
|
||||
本项目采用模块化设计,主要目录结构如下:
|
||||
|
||||
- `conf/`:配置文件目录,包含应用的主要配置文件 `app.conf`
|
||||
- `deploy/`:部署相关文件,包含Docker镜像构建和编排文件
|
||||
- `internal/`:核心业务逻辑代码,按功能模块组织
|
||||
- `internal/controllers/`:控制器层,处理HTTP请求
|
||||
- `internal/models/`:数据模型层,定义数据库实体
|
||||
- `internal/service/`:服务层,实现核心业务逻辑
|
||||
- `internal/routers/`:路由配置,定义API端点
|
||||
- `internal/otelTrace/`:OpenTelemetry追踪配置
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[main.go] --> B[config]
|
||||
A --> C[proxy]
|
||||
A --> D[otelTrace]
|
||||
A --> E[service]
|
||||
A --> F[cache]
|
||||
A --> G[utils]
|
||||
A --> H[routers]
|
||||
H --> I[controllers]
|
||||
E --> J[supplier]
|
||||
E --> K[notify]
|
||||
J --> L[third_party]
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
|
||||
**本节来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
|
||||
## 开发环境设置
|
||||
### Go版本要求
|
||||
根据 `go.mod` 文件中的声明,本项目需要 Go 1.24.0 或更高版本:
|
||||
|
||||
```go
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.6
|
||||
```
|
||||
|
||||
建议使用 Go 1.24.6 工具链以确保兼容性。
|
||||
|
||||
### 依赖安装
|
||||
使用 Go Modules 管理依赖,通过以下命令安装所有依赖:
|
||||
|
||||
```bash
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
此命令将根据 `go.mod` 文件下载并整理所有必需的依赖包,包括:
|
||||
- Beego Web框架
|
||||
- OpenTelemetry追踪库
|
||||
- Redis客户端
|
||||
- MySQL驱动
|
||||
- 各种工具库
|
||||
|
||||
### 数据库初始化
|
||||
根据 `conf/app.conf` 文件中的配置,数据库连接信息如下:
|
||||
|
||||
```ini
|
||||
[mysql]
|
||||
dbhost = 127.0.0.1
|
||||
dbport = 3306
|
||||
dbuser = root
|
||||
dbpasswd = Woaizixkie!123
|
||||
dbbase = kami
|
||||
debug = true
|
||||
```
|
||||
|
||||
初始化步骤:
|
||||
1. 确保MySQL服务正在运行
|
||||
2. 创建名为 `kami` 的数据库
|
||||
3. 导入项目所需的表结构和初始数据(具体SQL文件未在项目中提供)
|
||||
|
||||
**本节来源**
|
||||
- [go.mod](file://go.mod#L2-L85)
|
||||
- [conf/app.conf](file://conf/app.conf#L20-L25)
|
||||
|
||||
## 构建与运行
|
||||
### 构建方法
|
||||
项目提供了 `build.sh` 脚本用于构建可执行文件:
|
||||
|
||||
```bash
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
|
||||
```
|
||||
|
||||
此脚本配置了以下构建参数:
|
||||
- `CGO_ENABLED=0`:禁用CGO,使二进制文件静态链接
|
||||
- `GOOS=linux`:目标操作系统为Linux
|
||||
- `GOARCH=amd64`:目标架构为AMD64
|
||||
|
||||
### 运行方法
|
||||
有两种方式可以运行应用:
|
||||
|
||||
#### 使用构建脚本
|
||||
```bash
|
||||
# 构建
|
||||
./build.sh
|
||||
# 运行
|
||||
./main
|
||||
```
|
||||
|
||||
#### 直接运行
|
||||
```bash
|
||||
go run main.go
|
||||
```
|
||||
|
||||
### 应用初始化流程
|
||||
`main.go` 文件中的 `main()` 函数定义了应用的初始化流程:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([应用启动]) --> Config["加载MQ地址配置"]
|
||||
Config --> Proxy["初始化代理池"]
|
||||
Proxy --> Tracer["初始化OpenTelemetry追踪器"]
|
||||
Tracer --> Goroutines["启动Goroutines"]
|
||||
Goroutines --> Notify["订单通知消费者"]
|
||||
Goroutines --> Query["供应商查询消费者"]
|
||||
Goroutines --> Settle["订单结算初始化"]
|
||||
Goroutines --> Queue["初始化队列系统"]
|
||||
Goroutines --> ThirdParty["启动第三方订单池"]
|
||||
Notify --> Cache["启动缓存服务"]
|
||||
Query --> Utils["启动代理池工具"]
|
||||
Settle --> Web["启动Web服务器"]
|
||||
Queue --> Web
|
||||
ThirdParty --> Web
|
||||
Web --> End([应用运行中])
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [main.go](file://main.go#L23-L57)
|
||||
|
||||
**本节来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [build.sh](file://build.sh#L1-L2)
|
||||
|
||||
## Docker Compose 配置
|
||||
项目提供了两个Docker Compose配置文件,分别用于本地开发和生产部署。
|
||||
|
||||
### 本地开发配置
|
||||
`deploy/docker-compose-local.yaml` 文件定义了本地开发环境:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
gateway_kami:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: ./deploy/Dockerfile
|
||||
container_name: kami_gateway
|
||||
image: kami_gateway:latest
|
||||
ports:
|
||||
- "12309:12309"
|
||||
networks:
|
||||
- 1panel-network
|
||||
```
|
||||
|
||||
特点:
|
||||
- 使用 `latest` 标签的镜像
|
||||
- 将容器的12309端口映射到主机的12309端口
|
||||
- 连接到外部网络 `1panel-network`
|
||||
|
||||
### 生产部署配置
|
||||
`deploy/docker-compose.yaml` 文件定义了生产环境:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
kami_gateway:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: ./deploy/Dockerfile
|
||||
container_name: kami_gateway
|
||||
image: kami_gateway:$VERSION
|
||||
restart:
|
||||
always
|
||||
ports:
|
||||
- "127.0.0.1:22309:12309"
|
||||
- "127.0.0.1:22390:12390"
|
||||
volumes:
|
||||
- /data/kami/gateway/conf/:/app/conf/
|
||||
- /data/kami/gateway/logs/:/app/logs/
|
||||
networks:
|
||||
- 1panel-network
|
||||
```
|
||||
|
||||
特点:
|
||||
- 使用 `$VERSION` 环境变量指定镜像版本
|
||||
- 设置容器自动重启
|
||||
- 仅允许本地回环地址访问服务端口
|
||||
- 挂载外部配置和日志目录
|
||||
|
||||
**本节来源**
|
||||
- [deploy/docker-compose.yaml](file://deploy/docker-compose.yaml#L1-L23)
|
||||
- [deploy/docker-compose-local.yaml](file://deploy/docker-compose-local.yaml#L1-L17)
|
||||
|
||||
## 调试与性能分析
|
||||
### pprof性能分析
|
||||
项目已通过导入 `_ "net/http/pprof"` 包启用了pprof性能分析功能。可以通过以下URL访问pprof接口:
|
||||
|
||||
- `http://localhost:12309/debug/pprof/`:pprof主页
|
||||
- `http://localhost:12309/debug/pprof/profile`:CPU性能分析
|
||||
- `http://localhost:12309/debug/pprof/heap`:内存堆分析
|
||||
|
||||
使用方法:
|
||||
```bash
|
||||
# 获取30秒的CPU性能分析数据
|
||||
go tool pprof http://localhost:12309/debug/pprof/profile?seconds=30
|
||||
|
||||
# 获取内存堆分析数据
|
||||
go tool pprof http://localhost:12309/debug/pprof/heap
|
||||
```
|
||||
|
||||
### 日志配置
|
||||
根据 `conf/app.conf` 文件,日志配置如下:
|
||||
|
||||
```ini
|
||||
[logs]
|
||||
level =7
|
||||
filepath= ./logs/jhmerchant.log
|
||||
separate="["emergency","alert","critical","error","warning","notice","info","debug"]"
|
||||
maxdays=10
|
||||
```
|
||||
|
||||
日志级别为7(DEBUG级别),日志文件保存在 `./logs/jhmerchant.log`,最多保留10天。
|
||||
|
||||
**本节来源**
|
||||
- [main.go](file://main.go#L10-L11)
|
||||
- [conf/app.conf](file://conf/app.conf#L7-L15)
|
||||
|
||||
## 代码修改示例
|
||||
### 添加健康检查API
|
||||
以下是如何添加一个新的健康检查API的示例:
|
||||
|
||||
1. **创建控制器方法**:在 `internal/controllers/base_controller.go` 中添加健康检查方法
|
||||
|
||||
```go
|
||||
func (c *BaseController) HealthCheck() {
|
||||
c.Data["json"] = map[string]string{
|
||||
"status": "healthy",
|
||||
"timestamp": time.Now().Format(time.RFC3339),
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
```
|
||||
|
||||
2. **注册路由**:在 `internal/routers/router.go` 中添加路由映射
|
||||
|
||||
```go
|
||||
// 在init函数中添加
|
||||
web.Router("/health", &controllers.BaseController{}, "*:HealthCheck")
|
||||
```
|
||||
|
||||
3. **验证功能**:启动应用后访问 `http://localhost:12309/health` 应返回JSON格式的健康状态信息。
|
||||
|
||||
此示例展示了项目的基本代码结构:控制器处理请求,路由配置定义端点。通过这种方式,您可以轻松添加新的API端点。
|
||||
|
||||
**本节来源**
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
|
||||
## 测试与覆盖率
|
||||
### 运行测试套件
|
||||
使用Go内置的测试工具运行所有测试:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
此命令将递归执行项目中所有包的测试文件(以 `_test.go` 结尾的文件)。
|
||||
|
||||
### 查看覆盖率报告
|
||||
生成测试覆盖率报告:
|
||||
|
||||
```bash
|
||||
# 生成覆盖率数据
|
||||
go test ./... -coverprofile=coverage.out
|
||||
|
||||
# 查看文本格式的覆盖率报告
|
||||
go tool cover -func=coverage.out
|
||||
|
||||
# 生成HTML格式的覆盖率报告
|
||||
go tool cover -html=coverage.out -o coverage.html
|
||||
```
|
||||
|
||||
HTML报告将显示每行代码的执行情况,绿色表示已覆盖,红色表示未覆盖。
|
||||
|
||||
### 测试文件示例
|
||||
项目中包含多个测试文件,如:
|
||||
- `internal/dto/order_test.go`:DTO层测试
|
||||
- `internal/service/supplier/third_party/pool/worker_test.go`:第三方供应商服务测试
|
||||
- `internal/cache/redis_test.go`:Redis缓存测试
|
||||
|
||||
这些测试文件遵循Go测试惯例,使用 `testing` 包和 `testify` 断言库。
|
||||
|
||||
**本节来源**
|
||||
- [internal/dto/order_test.go](file://internal/dto/order_test.go#L1-L65)
|
||||
- [internal/cache/redis_test.go](file://internal/cache/redis_test.go#L1-L2)
|
||||
- [go.mod](file://go.mod#L70-L71)
|
||||
413
.qoder/repowiki/zh/content/技术栈与依赖/Beego 框架集成.md
Normal file
413
.qoder/repowiki/zh/content/技术栈与依赖/Beego 框架集成.md
Normal file
@@ -0,0 +1,413 @@
|
||||
# Beego 框架集成
|
||||
|
||||
<cite>
|
||||
**本文档引用文件**
|
||||
- [main.go](file://main.go)
|
||||
- [router.go](file://internal/routers/router.go)
|
||||
- [base_controller.go](file://internal/controllers/base_controller.go)
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go)
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go)
|
||||
- [init.go](file://internal/models/init.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [介绍](#介绍)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概述](#架构概述)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 介绍
|
||||
本文档详细说明了 Beego v2 框架在 kami_gateway 项目中的集成与应用。重点阐述了 Web 服务的启动流程、路由注册机制、控制器继承结构、中间件集成方式、RESTful API 接口的注解路由映射、Beego ORM 的自动模型注册机制及其与 MySQL 数据库的交互模式。同时提供性能调优建议、常见陷阱规避策略以及框架升级兼容性注意事项。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[kami_gateway] --> B[conf]
|
||||
A --> C[deploy]
|
||||
A --> D[internal]
|
||||
A --> E[main.go]
|
||||
A --> F[CLAUDE.md]
|
||||
A --> G[build.sh]
|
||||
D --> H[cache]
|
||||
D --> I[config]
|
||||
D --> J[consts]
|
||||
D --> K[controllers]
|
||||
D --> L[dto]
|
||||
D --> M[models]
|
||||
D --> N[otelTrace]
|
||||
D --> O[proxy]
|
||||
D --> P[routers]
|
||||
D --> Q[schema]
|
||||
D --> R[service]
|
||||
D --> S[swagger]
|
||||
D --> T[tasks]
|
||||
D --> U[utils]
|
||||
K --> V[base_controller.go]
|
||||
K --> W[order_controller.go]
|
||||
K --> X[payfor_controller.go]
|
||||
K --> Y[scan_controller.go]
|
||||
M --> Z[init.go]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
|
||||
**章节来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
|
||||
## 核心组件
|
||||
|
||||
kami_gateway 项目基于 Beego v2 框架构建,实现了网关服务的核心功能,包括订单处理、支付回调、商户查询等。系统通过 Beego 的 MVC 架构组织代码,利用其强大的路由、控制器、ORM 和中间件功能,构建了一个高并发、可扩展的支付网关系统。
|
||||
|
||||
**章节来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/controllers/base_controller.go](file://internal/controllers/base_controller.go#L1-L9)
|
||||
- [internal/models/init.go](file://internal/models/init.go#L1-L56)
|
||||
|
||||
## 架构概述
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Client[客户端] --> |HTTP请求| Beego[Beego Web框架]
|
||||
Beego --> Router[路由分发]
|
||||
Router --> Controller[控制器处理]
|
||||
Controller --> Service[业务服务层]
|
||||
Service --> Model[数据模型层]
|
||||
Model --> ORM[Beego ORM]
|
||||
ORM --> DB[(MySQL数据库)]
|
||||
Controller --> Cache[Redis缓存]
|
||||
Controller --> MQ[消息队列]
|
||||
Controller --> ThirdParty[第三方支付渠道]
|
||||
Beego --> Middleware[全局中间件]
|
||||
Middleware --> Tracing[分布式追踪]
|
||||
Middleware --> Logging[日志记录]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
- [internal/controllers/base_controller.go](file://internal/controllers/base_controller.go#L1-L9)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### Web 服务启动流程
|
||||
|
||||
Web 服务的启动通过 `web.Run()` 方法实现。在 `main.go` 文件中,程序首先进行各种初始化操作,包括配置加载、代理池初始化、分布式追踪初始化、消息队列消费者启动、缓存服务启动等,最后调用 `web.Run()` 启动 Web 服务器。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Main as main.go
|
||||
participant Web as Beego Web
|
||||
participant Init as 初始化模块
|
||||
Main->>Init : config.GetMQAddress()
|
||||
Main->>Init : proxy.InitProxyPool()
|
||||
Main->>Init : otelTrace.InitTracer()
|
||||
Main->>Init : notify.CreateOrderNotifyConsumer()
|
||||
Main->>Init : query.CreateSupplierOrderQueryCuConsumer()
|
||||
Main->>Init : service.OrderSettleInit()
|
||||
Main->>Init : cache.Start()
|
||||
Main->>Init : utils.StartProxyPool()
|
||||
Main->>Init : queue.Init()
|
||||
Main->>Init : third_party.StartOrderPool()
|
||||
Main->>Web : web.Run()
|
||||
Web-->>Main : 服务启动成功
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
|
||||
**章节来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
|
||||
### 路由注册机制
|
||||
|
||||
路由注册在 `internal/routers/router.go` 文件的 `init()` 函数中完成。系统使用 `web.Router()` 方法将不同的 URL 路径映射到相应的控制器和方法。同时,通过 `web.InsertFilterChain()` 注册了全局中间件,用于处理分布式追踪。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[路由注册] --> B[全局中间件]
|
||||
B --> C[web.InsertFilterChain]
|
||||
C --> D[otelTrace.Middleware]
|
||||
A --> E[网关处理路由]
|
||||
E --> F["/gateway/queryAccountInfo/:channel -> ScanController.QueryAccountInfo"]
|
||||
E --> G["/gateway/scan -> ScanController.Scan"]
|
||||
E --> H["/gateway/getAllowedMM -> ScanController.GetAllowedMM"]
|
||||
E --> I["/gateway/createOrder -> ScanController.CreateOrder"]
|
||||
A --> J[订单处理路由]
|
||||
J --> K["/gateway/supplier/order/query -> OrderController.OrderQuery"]
|
||||
J --> L["/gateway/update/order -> OrderController.OrderUpdate"]
|
||||
J --> M["/gateway/merchant/query -> OrderController.MerchantQuery"]
|
||||
J --> N["/solve/order/schedule -> OrderController.OrderSchedule"]
|
||||
A --> O[第三方回调路由]
|
||||
O --> P["/mfcard/notifyV2 -> MFCardV2Impl.PayNotify"]
|
||||
O --> Q["/appleCard/notify -> AppleCardImpl.PayNotify"]
|
||||
O --> R["/tMallGame/notify -> TMAllGameImpl.PayNotify"]
|
||||
O --> S["/jdCard/notify -> JDCardImpl.PayNotify"]
|
||||
O --> T["/walMart/notify -> WalMartImpl.PayNotify"]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L12-L74)
|
||||
|
||||
**章节来源**
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
|
||||
### 控制器继承结构
|
||||
|
||||
控制器采用继承结构,所有控制器都继承自 `BaseGateway` 结构体,而 `BaseGateway` 又继承自 Beego 的 `web.Controller`。这种设计实现了代码复用和统一的基类功能。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class web.Controller {
|
||||
+ServeJSON()
|
||||
+StopRun()
|
||||
+GetString()
|
||||
+GetFloat()
|
||||
+Bind()
|
||||
+Ctx *beecontext.Context
|
||||
+Data map[string]any
|
||||
}
|
||||
class BaseGateway {
|
||||
-继承自 web.Controller
|
||||
}
|
||||
class ScanController {
|
||||
+Scan()
|
||||
+SolveFailJSON()
|
||||
+GetAllowedMM()
|
||||
+CreateOrder()
|
||||
+QueryAccountInfo()
|
||||
}
|
||||
class OrderController {
|
||||
+OrderQuery()
|
||||
+OrderSchedule()
|
||||
+OrderUpdate()
|
||||
+MerchantQuery()
|
||||
}
|
||||
class PayForGateway {
|
||||
+PayFor()
|
||||
+PayForQuery()
|
||||
+QuerySupplierPayForResult()
|
||||
+SolvePayForResult()
|
||||
+Balance()
|
||||
}
|
||||
web.Controller <|-- BaseGateway
|
||||
BaseGateway <|-- ScanController
|
||||
BaseGateway <|-- OrderController
|
||||
BaseGateway <|-- PayForGateway
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/controllers/base_controller.go](file://internal/controllers/base_controller.go#L6-L8)
|
||||
- [internal/controllers/scan_controller.go](file://internal/controllers/scan_controller.go#L64-L66)
|
||||
- [internal/controllers/order_controller.go](file://internal/controllers/order_controller.go#L26-L28)
|
||||
|
||||
**章节来源**
|
||||
- [internal/controllers/base_controller.go](file://internal/controllers/base_controller.go#L1-L9)
|
||||
- [internal/controllers/scan_controller.go](file://internal/controllers/scan_controller.go#L1-L542)
|
||||
- [internal/controllers/order_controller.go](file://internal/controllers/order_controller.go#L1-L225)
|
||||
|
||||
### 中间件集成方式
|
||||
|
||||
系统通过 `web.InsertFilterChain()` 方法集成全局中间件。在 `router.go` 文件中,所有请求路径 (`*`) 都会经过 `otelTrace.Middleware` 中间件处理,实现分布式追踪功能。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as 客户端
|
||||
participant Beego as Beego框架
|
||||
participant Middleware as otelTrace.Middleware
|
||||
participant Controller as 控制器
|
||||
Client->>Beego : HTTP请求
|
||||
Beego->>Middleware : 调用中间件
|
||||
Middleware->>Controller : 调用next函数
|
||||
Controller->>Middleware : 执行控制器逻辑
|
||||
Middleware->>Beego : 返回响应
|
||||
Beego->>Client : HTTP响应
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L12-L14)
|
||||
|
||||
**章节来源**
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L12-L74)
|
||||
|
||||
### RESTful API 接口映射
|
||||
|
||||
RESTful API 接口通过 Beego 的路由机制映射到具体的控制器方法。以 `ScanController` 为例,不同的 URL 路径映射到不同的方法,实现不同的业务功能。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[/gateway/scan] --> B[ScanController.Scan]
|
||||
C[/gateway/getAllowedMM] --> D[ScanController.GetAllowedMM]
|
||||
E[/gateway/createOrder] --> F[ScanController.CreateOrder]
|
||||
G[/gateway/queryAccountInfo/:channel] --> H[ScanController.QueryAccountInfo]
|
||||
subgraph ScanController方法
|
||||
B --> I[参数验证]
|
||||
B --> J[商户信息校验]
|
||||
B --> K[通道选择]
|
||||
B --> L[订单生成]
|
||||
B --> M[上游支付]
|
||||
B --> N[结果处理]
|
||||
D --> O[面额获取]
|
||||
D --> P[商户校验]
|
||||
D --> Q[通道校验]
|
||||
D --> R[比例计算]
|
||||
F --> S[订单创建]
|
||||
F --> T[签名验证]
|
||||
F --> U[订单记录]
|
||||
F --> V[支付链接生成]
|
||||
end
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L18-L21)
|
||||
- [internal/controllers/scan_controller.go](file://internal/controllers/scan_controller.go#L69-L541)
|
||||
|
||||
**章节来源**
|
||||
- [internal/controllers/scan_controller.go](file://internal/controllers/scan_controller.go#L69-L541)
|
||||
|
||||
### Beego ORM 自动模型注册
|
||||
|
||||
Beego ORM 的自动模型注册在 `internal/models/init.go` 文件的 `init()` 函数中完成。系统通过 `orm.RegisterModel()` 方法注册所有数据模型,实现数据库表的自动映射和管理。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class orm {
|
||||
+RegisterDriver()
|
||||
+RegisterDataBase()
|
||||
+RegisterModel()
|
||||
+Debug bool
|
||||
}
|
||||
class models {
|
||||
+init()
|
||||
+dbHost string
|
||||
+dbUser string
|
||||
+dbPassword string
|
||||
+dbBase string
|
||||
+dbPort string
|
||||
+debug bool
|
||||
+link string
|
||||
}
|
||||
class AccountInfo {
|
||||
+Id int
|
||||
+Status string
|
||||
+AccountUid string
|
||||
+Balance float64
|
||||
+SettleAmount float64
|
||||
+LoanAmount float64
|
||||
+FreezeAmount float64
|
||||
+WaitAmount float64
|
||||
+PayforAmount float64
|
||||
+CreateTime time.Time
|
||||
+UpdateTime time.Time
|
||||
}
|
||||
class MerchantInfo {
|
||||
+Id int
|
||||
+Status string
|
||||
+MerchantUid string
|
||||
+MerchantKey string
|
||||
+MerchantSecret string
|
||||
+PayforFee float64
|
||||
+CreateTime time.Time
|
||||
+UpdateTime time.Time
|
||||
}
|
||||
class OrderInfo {
|
||||
+Id int
|
||||
+BankOrderId string
|
||||
+MerchantOrderId string
|
||||
+OrderAmount float64
|
||||
+ShowAmount float64
|
||||
+FactAmount float64
|
||||
+RoadUid string
|
||||
+Status string
|
||||
+NotifyUrl string
|
||||
+MerchantUid string
|
||||
+ExValue string
|
||||
+CardReturnData string
|
||||
+CreateTime *time.Time
|
||||
+UpdateTime *time.Time
|
||||
+PayTime *time.Time
|
||||
}
|
||||
orm --> models : 初始化
|
||||
models --> AccountInfo : 注册模型
|
||||
models --> MerchantInfo : 注册模型
|
||||
models --> OrderInfo : 注册模型
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L1-L56)
|
||||
- [internal/models/accounts/account.go](file://internal/models/accounts/account.go#L1-L114)
|
||||
- [internal/models/merchant/merchant_info.go](file://internal/models/merchant/merchant_info.go#L1-L208)
|
||||
- [internal/models/order/order_info.go](file://internal/models/order/order_info.go#L1-L436)
|
||||
|
||||
**章节来源**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L1-L56)
|
||||
|
||||
## 依赖分析
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Beego[Beego v2框架] --> MySQL[MySQL数据库]
|
||||
Beego --> Redis[Redis缓存]
|
||||
Beego --> Kafka[Kafka消息队列]
|
||||
Beego --> OTel[OpenTelemetry]
|
||||
main[main.go] --> routers[routers]
|
||||
main --> controllers[controllers]
|
||||
main --> models[models]
|
||||
main --> service[service]
|
||||
main --> otelTrace[otelTrace]
|
||||
main --> proxy[proxy]
|
||||
main --> cache[cache]
|
||||
routers --> controllers
|
||||
controllers --> service
|
||||
service --> models
|
||||
service --> third_party[第三方支付]
|
||||
service --> message[消息服务]
|
||||
models --> MySQL
|
||||
cache --> Redis
|
||||
service --> Kafka
|
||||
otelTrace --> OTel
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [go.mod](file://go.mod)
|
||||
|
||||
**章节来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
|
||||
## 性能考虑
|
||||
|
||||
1. **并发处理**:系统使用 `gopool.NewPool` 创建多个协程池(如 `delayPool`、`submitLimiterPool`),用于处理异步任务,提高系统并发能力。
|
||||
2. **数据库优化**:使用 ORM 的 `DoTxWithCtx` 方法进行事务处理,确保数据一致性;通过 `for update` 实现行级锁,防止并发更新问题。
|
||||
3. **缓存机制**:集成 Redis 缓存,减少数据库访问压力。
|
||||
4. **连接池**:数据库连接使用连接池管理,提高连接复用率。
|
||||
5. **异步处理**:将耗时操作(如回调通知、消息发送)放入协程池异步执行,避免阻塞主流程。
|
||||
|
||||
**章节来源**
|
||||
- [internal/service/pay_solve.go](file://internal/service/pay_solve.go#L1-L580)
|
||||
- [internal/controllers/scan_controller.go](file://internal/controllers/scan_controller.go#L1-L542)
|
||||
|
||||
## 故障排除指南
|
||||
|
||||
1. **服务无法启动**:检查 `main.go` 中的初始化顺序,确保数据库连接、缓存服务等依赖项正常启动。
|
||||
2. **路由不生效**:确认 `routers/router.go` 文件被正确导入(`_ "gateway/internal/routers"`),且路由路径和控制器方法名正确。
|
||||
3. **数据库连接失败**:检查 `models/init.go` 中的数据库配置是否正确,确保 MySQL 服务正常运行。
|
||||
4. **ORM 模型未注册**:确认 `models/init.go` 文件被正确导入(`_ "gateway/internal/models"`),且所有模型都已通过 `RegisterModel` 注册。
|
||||
5. **中间件不生效**:检查 `InsertFilterChain` 的调用时机,确保在路由注册之前完成中间件注册。
|
||||
|
||||
**章节来源**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
- [internal/models/init.go](file://internal/models/init.go#L1-L56)
|
||||
|
||||
## 结论
|
||||
|
||||
kami_gateway 项目成功集成了 Beego v2 框架,构建了一个功能完整、性能优良的支付网关系统。通过合理的架构设计和代码组织,系统实现了高内聚、低耦合的模块化结构。Beego 框架的路由、控制器、ORM 和中间件功能为系统开发提供了强大支持,使得开发效率和代码质量得到显著提升。未来可进一步优化异步处理机制,增强系统的可扩展性和容错能力。
|
||||
@@ -0,0 +1,276 @@
|
||||
# HTTP中间件集成
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go)
|
||||
- [circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go)
|
||||
- [consts.go](file://internal/otelTrace/consts.go)
|
||||
- [init.go](file://internal/otelTrace/init.go)
|
||||
- [utils.go](file://internal/otelTrace/utils.go)
|
||||
- [simple.go](file://internal/otelTrace/simple.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [核心组件](#核心组件)
|
||||
3. [熔断器保护机制](#熔断器保护机制)
|
||||
4. [中间件执行流程](#中间件执行流程)
|
||||
5. [性能监控与错误恢复](#性能监控与错误恢复)
|
||||
6. [系统初始化与配置](#系统初始化与配置)
|
||||
7. [依赖关系分析](#依赖关系分析)
|
||||
8. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
本文档详细说明了 OpenTelemetry 中间件在 Beego 框架中的实现机制,重点分析了全链路追踪功能的实现方式。文档涵盖了中间件如何作为 Beego 的 FilterFunc 注入请求处理链,如何从 HeaderCarrier 提取上游 trace context 并创建新的 Server Span,以及熔断器保护机制如何确保在追踪系统异常时不影响核心业务流程。同时,文档还分析了中间件的性能影响和优化策略。
|
||||
|
||||
## 核心组件
|
||||
|
||||
OpenTelemetry 中间件的核心组件包括 `Middleware` 函数、`CircuitBreaker` 结构体和相关的配置常量。这些组件共同实现了分布式追踪、性能监控和系统保护功能。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Middleware {
|
||||
+Middleware(ctx *beecontext.Context, next web.FilterFunc)
|
||||
}
|
||||
class CircuitBreaker {
|
||||
+maxFailures int32
|
||||
+resetTimeout time.Duration
|
||||
+failures int32
|
||||
+lastFailTime time.Time
|
||||
+state CircuitBreakerState
|
||||
+Call(fn func() error) error
|
||||
}
|
||||
class CircuitBreakerState {
|
||||
<<enumeration>>
|
||||
StateClosed
|
||||
StateOpen
|
||||
StateHalfOpen
|
||||
}
|
||||
class CustomLogger {
|
||||
+logger *zap.Logger
|
||||
}
|
||||
Middleware --> CircuitBreaker : "使用"
|
||||
Middleware --> CustomLogger : "使用"
|
||||
CircuitBreaker --> CircuitBreakerState : "包含"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L21-L140)
|
||||
- [circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go#L5-L11)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L50-L55)
|
||||
|
||||
**Section sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
- [circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go#L1-L51)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L1-L56)
|
||||
|
||||
## 熔断器保护机制
|
||||
|
||||
熔断器(Circuit Breaker)是系统稳定性的重要保障机制,它通过监控追踪系统的健康状态,在检测到连续失败时自动开启熔断,防止追踪系统的问题影响核心业务流程。
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Closed
|
||||
Closed --> Open : "连续失败 >= maxFailures"
|
||||
Open --> HalfOpen : "resetTimeout 超时"
|
||||
HalfOpen --> Closed : "调用成功"
|
||||
HalfOpen --> Open : "调用失败"
|
||||
note right of Closed
|
||||
正常状态
|
||||
允许所有请求通过
|
||||
end note
|
||||
note right of Open
|
||||
熔断状态
|
||||
直接返回,不执行业务逻辑
|
||||
end note
|
||||
note right of HalfOpen
|
||||
半开状态
|
||||
允许有限请求通过
|
||||
用于测试系统恢复情况
|
||||
end note
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go#L5-L50)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L50-L55)
|
||||
|
||||
**Section sources**
|
||||
- [circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go#L1-L51)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L50-L55)
|
||||
|
||||
## 中间件执行流程
|
||||
|
||||
OpenTelemetry 中间件作为 Beego 框架的 FilterFunc 被注入到请求处理链中,实现了全链路追踪功能。中间件在请求开始时从 HeaderCarrier 提取上游 trace context,并创建新的 Server Span。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant Middleware as "OTEL中间件"
|
||||
participant Beego as "Beego框架"
|
||||
participant Business as "业务逻辑"
|
||||
Client->>Middleware : 发起HTTP请求
|
||||
Middleware->>Middleware : 开始计时
|
||||
Middleware->>Middleware : 熔断器检查
|
||||
alt 熔断器关闭
|
||||
Middleware->>Middleware : 提取上游trace context
|
||||
Middleware->>Middleware : 创建Server Span
|
||||
Middleware->>Beego : 注入增强的context
|
||||
else 熔断器开启
|
||||
Middleware->>Middleware : 使用原始context
|
||||
Middleware->>Middleware : 记录导出失败
|
||||
end
|
||||
Middleware->>Business : 调用next处理业务
|
||||
Business->>Business : 执行业务逻辑
|
||||
Business->>Middleware : 返回结果
|
||||
Middleware->>Middleware : defer函数执行
|
||||
alt 发生panic
|
||||
Middleware->>Middleware : 记录panic错误
|
||||
Middleware->>Middleware : 返回500错误
|
||||
Middleware->>Middleware : 结束Span
|
||||
else 正常执行
|
||||
Middleware->>Middleware : 记录响应状态码
|
||||
Middleware->>Middleware : 记录性能指标
|
||||
alt 慢请求
|
||||
Middleware->>Middleware : 记录慢请求告警
|
||||
end
|
||||
alt 错误请求
|
||||
Middleware->>Middleware : 记录错误信息
|
||||
end
|
||||
Middleware->>Middleware : 结束Span
|
||||
end
|
||||
Middleware->>Client : 返回响应
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L21-L140)
|
||||
- [utils.go](file://internal/otelTrace/utils.go#L60-L75)
|
||||
|
||||
**Section sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L21-L140)
|
||||
- [utils.go](file://internal/otelTrace/utils.go#L60-L75)
|
||||
|
||||
## 性能监控与错误恢复
|
||||
|
||||
中间件实现了全面的性能监控和错误恢复机制,确保在生产环境中能够稳定运行并提供有价值的监控数据。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([请求开始]) --> ExtractTrace["提取上游trace context"]
|
||||
ExtractTrace --> CreateSpan["创建Server Span"]
|
||||
CreateSpan --> AddAttributes["添加关键属性"]
|
||||
AddAttributes --> HTTP["HTTP基础信息"]
|
||||
AddAttributes --> Service["服务信息"]
|
||||
AddAttributes --> Network["网络信息"]
|
||||
AddAttributes --> Business["业务逻辑处理"]
|
||||
Business --> CheckPanic{"发生panic?"}
|
||||
CheckPanic --> |是| HandlePanic["处理panic"]
|
||||
HandlePanic --> RecordError["记录错误信息"]
|
||||
RecordError --> Return500["返回500错误"]
|
||||
Return500 --> EndSpan["结束Span"]
|
||||
CheckPanic --> |否| RecordMetrics["记录性能指标"]
|
||||
RecordMetrics --> StatusCode["记录响应状态码"]
|
||||
StatusCode --> CheckSlow{"慢请求? >5秒"}
|
||||
CheckSlow --> |是| LogSlow["记录慢请求告警"]
|
||||
CheckSlow --> |否| CheckError{"错误请求? >=400"}
|
||||
CheckError --> |是| LogError["记录错误信息"]
|
||||
CheckError --> |否| Normal["正常请求"]
|
||||
LogSlow --> EndSpan
|
||||
LogError --> EndSpan
|
||||
Normal --> EndSpan
|
||||
EndSpan --> End([请求结束])
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L21-L140)
|
||||
- [utils.go](file://internal/otelTrace/utils.go#L80-L90)
|
||||
|
||||
**Section sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L21-L140)
|
||||
- [utils.go](file://internal/otelTrace/utils.go#L80-L90)
|
||||
|
||||
## 系统初始化与配置
|
||||
|
||||
系统初始化过程配置了 OpenTelemetry 的各种组件,包括追踪器、度量器和日志提供者,并设置了生产环境优化的参数。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Init[系统初始化] --> InitTracer["InitTracer()"]
|
||||
InitTracer --> ConfigTrace["配置Trace导出器"]
|
||||
ConfigTrace --> Network["网络优化配置"]
|
||||
Network --> Timeout["5秒超时"]
|
||||
Network --> Retry["重试机制"]
|
||||
Network --> Compress["Gzip压缩"]
|
||||
ConfigTrace --> Resource["资源标识配置"]
|
||||
Resource --> Service["服务名称"]
|
||||
Resource --> Env["环境标识"]
|
||||
InitTracer --> ConfigSampler["配置采样策略"]
|
||||
ConfigSampler --> Dynamic["动态采样率"]
|
||||
InitTracer --> ConfigBatch["配置批量处理器"]
|
||||
ConfigBatch --> BatchTimeout["5秒批量发送间隔"]
|
||||
ConfigBatch --> MaxBatchSize["512条单次导出批量"]
|
||||
ConfigBatch --> MaxQueueSize["2048条最大队列大小"]
|
||||
InitTracer --> ConfigMetric["配置Metrics导出器"]
|
||||
ConfigMetric --> MetricNetwork["网络优化配置"]
|
||||
InitTracer --> ConfigLog["配置日志导出器"]
|
||||
ConfigLog --> LogNetwork["网络优化配置"]
|
||||
InitTracer --> SetPropagator["设置TextMapPropagator"]
|
||||
SetPropagator --> TraceContext["W3C Trace Context"]
|
||||
SetPropagator --> Baggage["W3C Baggage"]
|
||||
InitTracer --> StartMonitor["启动监控goroutine"]
|
||||
StartMonitor --> monitorExporterHealth["monitorExporterHealth()"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L10-L45)
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L10-L45)
|
||||
|
||||
## 依赖关系分析
|
||||
|
||||
分析各组件之间的依赖关系,有助于理解系统的整体架构和组件间的交互方式。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
middleware.go --> circuit_breaker.go : "使用"
|
||||
middleware.go --> consts.go : "使用"
|
||||
middleware.go --> utils.go : "使用"
|
||||
init.go --> circuit_breaker.go : "使用"
|
||||
init.go --> consts.go : "使用"
|
||||
init.go --> utils.go : "使用"
|
||||
simple.go --> consts.go : "使用"
|
||||
middleware.go --> init.go : "依赖初始化"
|
||||
init.go --> middleware.go : "初始化完成后使用"
|
||||
style middleware.go fill:#f9f,stroke:#333
|
||||
style circuit_breaker.go fill:#bbf,stroke:#333
|
||||
style consts.go fill:#f96,stroke:#333
|
||||
style init.go fill:#9f9,stroke:#333
|
||||
style utils.go fill:#ff9,stroke:#333
|
||||
style simple.go fill:#9ff,stroke:#333
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
- [circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go#L1-L51)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L1-L56)
|
||||
- [init.go](file://internal/otelTrace/init.go#L1-L257)
|
||||
- [utils.go](file://internal/otelTrace/utils.go#L1-L108)
|
||||
- [simple.go](file://internal/otelTrace/simple.go#L1-L93)
|
||||
|
||||
**Section sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
- [circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go#L1-L51)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L1-L56)
|
||||
- [init.go](file://internal/otelTrace/init.go#L1-L257)
|
||||
- [utils.go](file://internal/otelTrace/utils.go#L1-L108)
|
||||
- [simple.go](file://internal/otelTrace/simple.go#L1-L93)
|
||||
|
||||
## 结论
|
||||
|
||||
OpenTelemetry 中间件在 Beego 框架中的实现充分考虑了生产环境的需求,通过熔断器保护机制确保了系统的稳定性。中间件能够有效地集成到 Beego 的请求处理链中,实现全链路追踪功能。通过从 HeaderCarrier 提取上游 trace context 并创建新的 Server Span,实现了跨服务的追踪链路。中间件添加了丰富的监控指标,包括 HTTP 方法、URL、状态码、响应大小、持续时间以及客户端 IP 等,为系统监控和问题排查提供了有力支持。
|
||||
|
||||
熔断器机制在追踪系统异常时能够捕获错误并降级,确保不影响核心业务流程。defer 函数中实现了 panic 的记录和优雅降级返回 500 错误,同时确保 Span 能够正确结束。系统初始化过程配置了生产环境优化的参数,包括网络超时、重试机制、批量处理等,有效降低了中间件对系统性能的影响。
|
||||
|
||||
总体而言,该中间件设计合理,功能完善,能够在保证系统稳定性的同时提供全面的监控能力,是生产环境中理想的分布式追踪解决方案。
|
||||
@@ -0,0 +1,176 @@
|
||||
# OpenTelemetry 分布式追踪
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [main.go](file://main.go)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go)
|
||||
- [internal/otelTrace/span.go](file://internal/otelTrace/span.go)
|
||||
- [internal/otelTrace/logs.go](file://internal/otelTrace/logs.go)
|
||||
- [internal/otelTrace/middleware.go](file://internal/otelTrace/middleware.go)
|
||||
- [internal/otelTrace/circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [OpenTelemetry 初始化机制](#opentelemetry-初始化机制)
|
||||
2. [全局上下文与Span生命周期管理](#全局上下文与span生命周期管理)
|
||||
3. [日志与追踪上下文关联](#日志与追踪上下文关联)
|
||||
4. [Beego HTTP服务集成](#beego-http服务集成)
|
||||
5. [资源释放与清理机制](#资源释放与清理机制)
|
||||
6. [OTLP导出与采样策略](#otlp导出与采样策略)
|
||||
7. [链路追踪调试最佳实践](#链路追踪调试最佳实践)
|
||||
|
||||
## OpenTelemetry 初始化机制
|
||||
|
||||
`otelTrace.InitTracer()` 函数是 OpenTelemetry 在 kami_gateway 中的核心初始化入口,负责配置和启动追踪、指标和日志的导出功能。该函数在 `main.go` 的 `main()` 函数中被调用,是整个分布式追踪系统的起点。
|
||||
|
||||
初始化过程首先创建 OTLP (OpenTelemetry Protocol) 的 gRPC 客户端,用于将追踪数据发送到指定的收集器(collector)。在 `internal/otelTrace/init.go` 文件中,通过 `otlptracegrpc.NewClient` 配置了生产环境优化的网络参数,包括 5 秒的超时时间、启用重试机制(初始间隔 1 秒,最大间隔 10 秒)以及使用 gzip 压缩来减少网络传输量。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant main as main.go
|
||||
participant InitTracer as otelTrace.InitTracer()
|
||||
participant TracerProvider as TracerProvider
|
||||
participant BatchProcessor as BatchSpanProcessor
|
||||
participant TraceExporter as otlptrace.Exporter
|
||||
main->>InitTracer : 调用 InitTracer()
|
||||
InitTracer->>TraceExporter : 创建 OTLP 追踪导出器
|
||||
InitTracer->>TracerProvider : 创建资源标识 (Resource)
|
||||
InitTracer->>TracerProvider : 配置采样策略 (10%)
|
||||
InitTracer->>BatchProcessor : 创建批量处理器
|
||||
BatchProcessor->>TraceExporter : 配置批量发送 (5秒/512条)
|
||||
InitTracer->>TracerProvider : 设置 TracerProvider
|
||||
InitTracer-->>main : 返回清理函数
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
|
||||
## 全局上下文与Span生命周期管理
|
||||
|
||||
kami_gateway 使用 `InitCtx` 作为全局的上下文(Context),它在 `internal/otelTrace/consts.go` 文件中被定义为 `context.Background()`。这个上下文贯穿整个应用的生命周期,用于传递追踪信息和控制取消信号。
|
||||
|
||||
Span 的生命周期管理在 `internal/otelTrace/span.go` 文件中实现。`Span()` 函数是创建新 Span 的主要方法,它接受一个父上下文、追踪名称和 Span 名称,并返回一个新的上下文和 Span 对象。该函数内部使用 `otel.Tracer(traceName).Start()` 来启动一个 Span,并自动添加了通用属性,如 `tracer.name` 和 `span.kind`,以优化指标收集。
|
||||
|
||||
对于异步操作,系统提供了 `CreateLinkContext` 和 `CreateAsyncContext` 等函数。这些函数创建的上下文与父 Span 保持关联(通过 `trace.WithLinks`),但不继承父上下文的取消信号。这确保了异步任务(如后台处理)可以独立完成,不会因为父请求的取消而中断,同时又能将追踪信息链接到原始请求链路中。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class SpanManager {
|
||||
+Span(ctx, traceName, spanName) (context.Context, trace.Span)
|
||||
+CreateLinkContext(ctx, spanName) (context.Context, trace.Span)
|
||||
+CreateAsyncContext(ctx, operationName) (context.Context, trace.Span)
|
||||
+CreateTraceableContext(ctx, operationName) (context.Context, trace.Span)
|
||||
+SetSpanError(span, err, message)
|
||||
+SetSpanSuccess(span, message)
|
||||
+AddSpanAttributes(span, attrs)
|
||||
+SpanWithMetrics(ctx, traceName, spanName, operationType) (context.Context, trace.Span)
|
||||
}
|
||||
class Span {
|
||||
<<interface>>
|
||||
+End()
|
||||
+SetStatus(code, message)
|
||||
+SetAttributes(attrs)
|
||||
+RecordError(err)
|
||||
}
|
||||
SpanManager --> Span : 创建和管理
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/span.go](file://internal/otelTrace/span.go#L1-L151)
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L11-L11)
|
||||
- [internal/otelTrace/span.go](file://internal/otelTrace/span.go#L1-L151)
|
||||
|
||||
## 日志与追踪上下文关联
|
||||
|
||||
为了实现日志与追踪的上下文关联,kami_gateway 实现了一个自定义的 `CustomLogger` 结构体。该结构体在 `internal/otelTrace/logs.go` 文件中定义,包装了 `zap.Logger`,并提供了一个 `WithContext(ctx context.Context)` 方法。
|
||||
|
||||
当调用 `WithContext` 方法时,它会检查传入的上下文。如果上下文有效且包含一个有效的 Span,则会将该上下文作为 `zap.Reflect("ctx", ctx)` 添加到日志记录器中。这样,当一条日志被记录时,它就携带了当前的追踪上下文(包括 Trace ID 和 Span ID),使得在日志系统中可以轻松地通过 Trace ID 关联到完整的调用链路。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[业务代码] --> B{调用 Logger.WithContext(ctx)}
|
||||
B --> C[检查 ctx 是否有效]
|
||||
C --> |是| D[检查 Span 是否有效]
|
||||
D --> |是| E[创建带 ctx 字段的新 Logger]
|
||||
D --> |否| F[返回原始 Logger]
|
||||
C --> |否| F
|
||||
E --> G[记录日志]
|
||||
F --> G
|
||||
G --> H[日志包含 Trace ID/Span ID]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/logs.go](file://internal/otelTrace/logs.go#L1-L29)
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/logs.go](file://internal/otelTrace/logs.go#L1-L29)
|
||||
|
||||
## Beego HTTP服务集成
|
||||
|
||||
OpenTelemetry 通过中间件(Middleware)的方式集成到 Beego 的 HTTP 服务中。`internal/otelTrace/middleware.go` 文件中的 `Middleware` 函数是一个 Beego 过滤器,它在每个 HTTP 请求处理前被调用。
|
||||
|
||||
该中间件首先从 HTTP 请求头中提取上游服务传递的追踪上下文(如 `traceparent` 头),确保了分布式调用链路的连续性。然后,它使用 `otel.Tracer("gateway-router").Start()` 创建一个新的服务器端 Span,并设置了一系列关键属性,包括 HTTP 方法、URL、状态码、客户端 IP 等。为了保护业务逻辑不受追踪系统故障的影响,中间件还集成了一个熔断器(Circuit Breaker),当追踪导出器连续失败达到阈值时,会自动开启熔断,绕过追踪逻辑,确保业务请求的正常处理。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as HTTP客户端
|
||||
participant Beego as Beego框架
|
||||
participant Middleware as otelTrace.Middleware
|
||||
participant Business as 业务逻辑
|
||||
Client->>Beego : 发送HTTP请求 (含traceparent头)
|
||||
Beego->>Middleware : 调用中间件
|
||||
Middleware->>Middleware : 从Header提取父SpanContext
|
||||
Middleware->>Middleware : 创建新的Server Span
|
||||
Middleware->>Business : 调用业务逻辑(next)
|
||||
Business-->>Middleware : 返回
|
||||
Middleware->>Middleware : 记录响应状态码和耗时
|
||||
Middleware->>Middleware : 结束Span
|
||||
Middleware-->>Beego : 返回
|
||||
Beego-->>Client : 返回HTTP响应
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
|
||||
## 资源释放与清理机制
|
||||
|
||||
在 `main.go` 文件中,`otelTrace.InitTracer()` 的返回值被赋值给 `cleanup1`, `cleanup2`, `cleanup3` 三个函数。这些函数分别对应追踪、指标和日志导出器的 `Shutdown` 方法。
|
||||
|
||||
通过 `defer` 语句,这些清理函数被注册为程序退出时执行。当 `main()` 函数执行完毕或程序因异常退出时,`defer` 会确保这些清理函数被调用。`Shutdown` 方法会优雅地关闭导出器,将内存中尚未导出的缓冲数据(如队列中的 Span)刷新到后端收集器,然后断开与收集器的连接。这种机制保证了在程序终止前,所有已生成的追踪数据都能被完整地发送出去,避免了数据丢失。
|
||||
|
||||
**Section sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
|
||||
## OTLP导出与采样策略
|
||||
|
||||
kami_gateway 使用 OTLP/gRPC 协议将数据导出到位于 `38.38.251.113:31547` 的收集器。为了优化性能和资源使用,系统配置了多项生产环境策略:
|
||||
|
||||
* **批量导出 (Batch Export):** 使用 `sdktrace.NewBatchSpanProcessor` 将多个 Span 批量发送。配置了 5 秒的批量发送间隔 (`BatchTimeout`) 和 512 条的最大批量大小 (`MaxExportBatchSize`),平衡了实时性和网络开销。
|
||||
* **采样策略 (Sampling):** 采用基于 Trace ID 比例的采样策略 (`traceIDRatioBased`),默认采样率为 10% (`DefaultSamplingRatio`)。这在保证可观测性的同时,有效控制了高负载下的数据量和系统开销。
|
||||
* **熔断保护 (Circuit Breaker):** 在 `internal/otelTrace/circuit_breaker.go` 中实现了一个简单的熔断器。当导出失败次数达到阈值(默认5次)时,熔断器会开启,暂时停止导出操作,防止因后端收集器故障导致应用资源耗尽。
|
||||
* **资源限制:** 配置了最大队列大小 (`MaxQueueSize=2048`) 和最大并发导出数,防止内存溢出。
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L1-L55)
|
||||
- [internal/otelTrace/circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go#L1-L50)
|
||||
|
||||
## 链路追踪调试最佳实践
|
||||
|
||||
1. **检查初始化:** 确保 `otelTrace.InitTracer()` 在 `main()` 函数早期被成功调用,并且返回的清理函数被正确注册。
|
||||
2. **验证中间件:** 确认 Beego 的路由配置中已注册 `otelTrace.Middleware`,否则 HTTP 请求将无法被追踪。
|
||||
3. **分析日志:** 利用 `CustomLogger.WithContext()` 记录的日志,通过 `ctx` 字段中的 Trace ID 在日志系统中搜索,可以快速定位到与特定请求相关的所有日志。
|
||||
4. **监控导出器健康:** 关注 `monitorExporterHealth` goroutine 的输出,它会监控导出器的健康状态。如果发现大量导出失败,应检查网络连接和收集器状态。
|
||||
5. **利用熔断器:** 在生产环境中,熔断器是保护系统稳定的关键。如果发现追踪数据突然中断,应首先检查熔断器是否已开启,并排查后端收集器的问题。
|
||||
6. **调整采样率:** 在调试阶段,可以临时提高采样率以获取更完整的数据;在生产环境稳定后,应恢复到较低的采样率以控制成本。
|
||||
@@ -0,0 +1,152 @@
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [span.go](file://internal/otelTrace/span.go)
|
||||
- [consts.go](file://internal/otelTrace/consts.go)
|
||||
- [utils.go](file://internal/otelTrace/utils.go)
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go)
|
||||
- [queue.go](file://internal/service/supplier/third_party/queue/queue.go)
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go)
|
||||
- [batch_six_handler.go](file://internal/service/supplier/third_party/queue/channel/batch_six_handler.go)
|
||||
- [fat_six.go](file://internal/service/supplier/third_party/pool/card_sender/fat_six.go)
|
||||
</cite>
|
||||
|
||||
# Span生命周期管理
|
||||
|
||||
## Table of Contents
|
||||
1. [引言](#引言)
|
||||
2. [核心Span管理函数](#核心span管理函数)
|
||||
3. [异步与可追踪上下文](#异步与可追踪上下文)
|
||||
4. [状态与属性管理](#状态与属性管理)
|
||||
5. [业务场景中的应用](#业务场景中的应用)
|
||||
6. [总结](#总结)
|
||||
|
||||
## 引言
|
||||
|
||||
在 `kami_gateway` 系统中,OpenTelemetry (OTel) 被用于实现全面的分布式追踪。`internal/otelTrace` 包提供了对 OTel API 的封装和扩展,旨在简化开发者在创建、关联和管理追踪 Span 时的操作。本文档将深入解析该包中关于 Span 生命周期管理的核心机制,包括 `Span()` 辅助函数、`CreateAsyncContext()`、`CreateTraceableContext()` 等关键函数的设计意图与实现细节,并结合订单处理、渠道调用等业务代码,展示其在实际场景中的应用模式。
|
||||
|
||||
**Section sources**
|
||||
- [span.go](file://internal/otelTrace/span.go#L13-L129)
|
||||
|
||||
## 核心Span管理函数
|
||||
|
||||
### Span() 辅助函数
|
||||
|
||||
`Span()` 函数是创建追踪 Span 的核心入口,它封装了 `otel.Tracer().Start()` 的调用,并自动注入了通用的追踪属性,极大地简化了开发者的使用。
|
||||
|
||||
该函数通过 `trace.WithAttributes()` 选项,为所有创建的 Span 统一添加了 `tracer.name` 和 `span.kind` 两个关键属性。其中,`tracer.name` 的值由调用者传入,用于标识追踪的来源;`span.kind` 固定为 `"internal"`,表明这是一个内部处理的 Span。这种设计确保了追踪数据的一致性和可读性,便于在监控系统中进行聚合分析。
|
||||
|
||||
此外,函数还包含了一个安全检查,当传入的 `ctx` 为 `nil` 时,会自动创建一个 `context.Background()`,以避免程序因空上下文而 panic。
|
||||
|
||||
**Section sources**
|
||||
- [span.go](file://internal/otelTrace/span.go#L13-L30)
|
||||
|
||||
## 异步与可追踪上下文
|
||||
|
||||
### CreateLinkContext() 与关联关系
|
||||
|
||||
`CreateLinkContext()` 函数的设计核心在于创建一种“关联关系”(Linked Relationship),而非传统的“父子关系”(Parent-Child Relationship)。在标准的上下文传播中,父 Context 的取消会通过 `context.WithCancel` 等机制传播到所有子 Context,导致所有关联的 Goroutine 被强制终止。
|
||||
|
||||
`CreateLinkContext()` 通过 `trace.ContextWithSpanContext(context.Background(), parentSpanCtx)` 创建了一个新的、独立于父 Context 生命周期的上下文。它使用 `context.Background()` 作为新上下文的根,从而切断了取消信号的传播链。同时,它通过 `trace.WithLinks()` 选项,将新创建的 Span 与父 Span 的 `SpanContext` 进行链接。这使得在追踪系统中,我们仍然可以看到这两个 Span 之间的关联,但它们的执行是独立的。
|
||||
|
||||
**Diagram sources**
|
||||
- [span.go](file://internal/otelTrace/span.go#L42-L57)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[父请求处理] --> |创建父Span| B(父Span)
|
||||
B --> |调用CreateLinkContext| C[创建关联上下文]
|
||||
C --> D[新Goroutine启动]
|
||||
D --> |创建新Span| E(新Span)
|
||||
B -- "WithLinks" --> E
|
||||
style E fill:#f9f,stroke:#333
|
||||
note right of E: 生命周期独立<br/>不继承取消信号
|
||||
```
|
||||
|
||||
### CreateAsyncContext()
|
||||
|
||||
`CreateAsyncContext()` 是 `CreateLinkContext()` 的一个特化版本,专门用于处理异步任务(如 Goroutine)。它在调用 `CreateLinkContext()` 的基础上,进一步添加了 `async.operation`、`async.type` 和 `relationship` 等属性,明确标识了这是一个异步操作,并且与父 Span 是关联关系。
|
||||
|
||||
这种设计模式在处理后台任务时非常有用,例如,当一个订单请求需要异步调用第三方渠道进行处理时,即使用户的 HTTP 请求已经超时或被取消,后台的渠道调用任务仍能独立完成,确保业务逻辑的完整性。
|
||||
|
||||
**Section sources**
|
||||
- [span.go](file://internal/otelTrace/span.go#L61-L69)
|
||||
|
||||
### CreateTraceableContext()
|
||||
|
||||
`CreateTraceableContext()` 函数旨在创建一个“可追踪的上下文”,它不仅保持了与父 Span 的关联,还通过 `trace.WithAttributes()` 传递了更丰富的追踪信息,如 `parent.trace_id`、`parent.span_id` 等。这使得在复杂的分布式系统中,即使经过多层异步调用,也能完整地追溯到原始请求的链路。
|
||||
|
||||
此函数同样使用 `context.Background()` 作为新上下文的根,保证了生命周期的独立性,同时通过链接和属性注入,实现了追踪链路的完整传递。
|
||||
|
||||
**Section sources**
|
||||
- [span.go](file://internal/otelTrace/span.go#L73-L93)
|
||||
|
||||
## 状态与属性管理
|
||||
|
||||
### SetSpanError() 与 SetSpanSuccess()
|
||||
|
||||
`SetSpanError()` 和 `SetSpanSuccess()` 函数提供了标准化的错误和成功状态标记方法。`SetSpanError()` 会将 Span 的状态码设置为 `codes.Error`,并添加 `error.message` 和 `error.type` 属性。`SetSpanSuccess()` 则将状态码设置为 `codes.Ok`,并添加 `operation.status` 属性。
|
||||
|
||||
这种标准化的处理方式,使得在追踪系统中可以轻松地根据这些预定义的属性进行错误率统计、成功率分析等监控操作,而无需在每个业务代码中重复编写类似的逻辑。
|
||||
|
||||
**Section sources**
|
||||
- [span.go](file://internal/otelTrace/span.go#L96-L120)
|
||||
|
||||
### AddSpanAttributes()
|
||||
|
||||
`AddSpanAttributes()` 函数提供了一个便捷的接口,用于向 Span 添加自定义属性。它直接调用了 `span.SetAttributes()`,并处理了 `span` 为 `nil` 的边界情况。开发者可以利用此函数轻松地将业务相关的上下文信息(如订单号、用户ID等)注入到追踪数据中,极大地增强了追踪信息的诊断价值。
|
||||
|
||||
**Section sources**
|
||||
- [span.go](file://internal/otelTrace/span.go#L123-L129)
|
||||
|
||||
## 业务场景中的应用
|
||||
|
||||
### 订单池匹配中的异步处理
|
||||
|
||||
在订单池服务 `OrderPoolServiceImpl.matchOrdersForFaceValue` 中,为了处理高并发的订单匹配,系统使用了 Goroutine 来并行处理不同面值的订单。在此场景下,`CreateAsyncContext()` 被用来创建独立的追踪上下文。
|
||||
|
||||
```go
|
||||
func (s *OrderPoolServiceImpl) matchOrdersForFaceValue(ctx context.Context, channel card_sender.SendCardTaskEnum, roadUid string, faceValue float64) {
|
||||
ctx, span := otelTrace.CreateAsyncContext(ctx, "matchOrdersForFaceValue")
|
||||
defer span.End()
|
||||
// ... 处理逻辑
|
||||
}
|
||||
```
|
||||
|
||||
这确保了即使主订单处理流程被取消,正在进行的订单匹配任务仍能继续执行,避免了因上游请求取消而导致的业务中断。
|
||||
|
||||
**Section sources**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L336-L526)
|
||||
|
||||
### 队列任务处理中的追踪
|
||||
|
||||
在队列系统 `QueueManager.EnqueueTask` 中,当一个任务被加入队列时,会创建一个 Span 来追踪 `EnqueueTask` 操作。而在 `RedisQueue.processQueue` 方法中,为了处理队列中的任务,系统使用 `CreateAsyncContext` 创建了一个新的上下文来处理每个任务。
|
||||
|
||||
```go
|
||||
func (q *RedisQueue) processQueue(ctx context.Context) {
|
||||
// ...
|
||||
linkCtx, span := otelTrace.CreateAsyncContext(ctx, "queue_process")
|
||||
defer span.End()
|
||||
// ...
|
||||
err := q.handler.HandleTask(linkCtx, task)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
这种方式将队列的消费过程与生产过程在追踪链路上关联起来,同时保证了任务处理的独立性,是典型的异步任务追踪模式。
|
||||
|
||||
**Section sources**
|
||||
- [queue.go](file://internal/service/supplier/third_party/queue/queue.go#L380-L400)
|
||||
|
||||
### 渠道调用中的复杂追踪
|
||||
|
||||
在 `BatchSixChannelHandler.SubmitCard` 和 `SendCardTaskTypeFatSix.HandleSendCardTask` 等渠道调用的实现中,`Span()` 函数被广泛用于创建追踪 Span。这些 Span 会携带 `channelCode`、`faceValue`、`cardNo` 等关键业务参数作为属性,使得在排查渠道调用失败问题时,能够快速定位到具体的调用实例。
|
||||
|
||||
例如,在 `SendCardTaskTypeFatSix.HandleSendCardTask` 中,Span 被用来追踪整个提交卡密的复杂流程,包括获取代理、识别验证码、提交数据等步骤,并通过 `span.AddEvent()` 记录关键事件点,为性能分析和故障排查提供了详尽的数据。
|
||||
|
||||
**Section sources**
|
||||
- [batch_six_handler.go](file://internal/service/supplier/third_party/queue/channel/batch_six_handler.go#L105-L212)
|
||||
- [fat_six.go](file://internal/service/supplier/third_party/pool/card_sender/fat_six.go#L156-L307)
|
||||
|
||||
## 总结
|
||||
|
||||
`kami_gateway` 的 OTel 追踪系统通过精心设计的辅助函数,有效地解决了分布式系统中 Span 的创建、关联与管理问题。`Span()` 函数简化了基础 Span 的创建;`CreateAsyncContext()` 和 `CreateTraceableContext()` 通过“关联关系”模式,巧妙地平衡了追踪链路的完整性与异步任务的独立性;`SetSpanError()`、`SetSpanSuccess()` 和 `AddSpanAttributes()` 则提供了标准化的状态和属性管理接口。这些机制在订单处理、渠道调用等核心业务场景中得到了广泛应用,为系统的可观测性奠定了坚实的基础。
|
||||
@@ -0,0 +1,107 @@
|
||||
# 分布式上下文传播
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [init.go](file://internal/otelTrace/init.go)
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go)
|
||||
- [main.go](file://main.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [引言](#引言)
|
||||
2. [上下文传播机制配置](#上下文传播机制配置)
|
||||
3. [W3C Trace Context 传播](#w3c-trace-context-传播)
|
||||
4. [W3C Baggage 传播](#w3c-baggage-传播)
|
||||
5. [支付网关场景应用](#支付网关场景应用)
|
||||
6. [结论](#结论)
|
||||
|
||||
## 引言
|
||||
kami_gateway 系统通过 OpenTelemetry 实现了完整的分布式追踪能力,其中上下文传播机制是确保跨服务调用链路连续性的核心。本系统通过配置复合文本映射传播器,集成了 W3C Trace Context 和 W3C Baggage 两种标准,实现了调用链路追踪和业务上下文数据的跨服务传递。在支付网关的多通道调用场景中,这一机制确保了从订单调度到代付处理的全链路追踪完整性,为系统监控、故障排查和性能优化提供了坚实基础。
|
||||
|
||||
## 上下文传播机制配置
|
||||
|
||||
kami_gateway 系统在初始化阶段通过 `otel.SetTextMapPropagator` 配置了复合文本映射传播器,该传播器集成了 W3C Trace Context 和 W3C Baggage 两种标准。这种配置确保了系统能够同时处理追踪上下文和业务上下文的传播需求。
|
||||
|
||||
在 `internal/otelTrace/init.go` 文件中,系统通过 `propagation.NewCompositeTextMapPropagator` 创建了一个复合传播器,将 `propagation.TraceContext{}` 和 `propagation.Baggage{}` 两种传播器组合在一起。这种设计允许系统在单个 HTTP 请求中同时传播追踪信息和业务数据,实现了功能的解耦和复用。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[HTTP请求] --> B[复合传播器]
|
||||
B --> C[W3C Trace Context]
|
||||
B --> D[W3C Baggage]
|
||||
C --> E[traceparent]
|
||||
C --> F[tracestate]
|
||||
D --> G[业务上下文数据]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L150-L153)
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L150-L153)
|
||||
|
||||
## W3C Trace Context 传播
|
||||
|
||||
W3C Trace Context 标准通过 `traceparent` 和 `tracestate` HTTP 头在服务间传递追踪上下文,确保调用链的连续性。在 kami_gateway 系统中,这一机制通过中间件在请求处理的各个阶段被激活和维护。
|
||||
|
||||
当 HTTP 请求到达网关时,`internal/otelTrace/middleware.go` 中的 `Middleware` 函数会从请求头中提取上游的 trace context。通过 `otel.GetTextMapPropagator()` 获取配置的传播器,并使用 `propagator.Extract` 方法从请求头中提取上下文信息。这一过程确保了即使在复杂的微服务架构中,每个服务实例也能正确地继承和延续调用链。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant Gateway as "支付网关"
|
||||
participant Supplier as "第三方供应商"
|
||||
Client->>Gateway : HTTP请求(traceparent, tracestate)
|
||||
Gateway->>Gateway : 提取追踪上下文
|
||||
Gateway->>Supplier : 转发请求(traceparent, tracestate)
|
||||
Supplier->>Gateway : 响应
|
||||
Gateway->>Client : 响应
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L30-L33)
|
||||
|
||||
**Section sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L30-L33)
|
||||
|
||||
## W3C Baggage 传播
|
||||
|
||||
W3C Baggage 标准允许在服务间传递业务相关的上下文数据,这些数据可以包含商户信息、订单属性、风险控制参数等业务关键信息。在 kami_gateway 系统中,Baggage 传播机制为跨服务的业务逻辑处理提供了必要的上下文支持。
|
||||
|
||||
虽然代码中没有直接展示 Baggage 的具体使用,但通过配置 `propagation.Baggage{}` 作为复合传播器的一部分,系统具备了处理业务上下文数据的能力。在实际应用中,这些数据可以用于实现基于上下文的路由决策、风险控制策略应用和个性化服务处理。
|
||||
|
||||
在支付网关的场景中,Baggage 可以携带如商户等级、风险评分、特殊处理要求等信息,确保下游服务能够根据这些上下文做出正确的业务决策。例如,在订单调度过程中,可以根据 Baggage 中携带的商户优先级信息,优先选择高优先级商户的支付通道。
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L152)
|
||||
|
||||
## 支付网关场景应用
|
||||
|
||||
在支付网关的多通道调用场景中,上下文传播机制在订单调度和代付处理等关键流程中发挥着重要作用。以 `internal/controllers/order_controller.go` 中的 `OrderSchedule` 方法为例,当商户请求重新调度订单时,系统需要确保从接收请求到最终调用第三方供应商的整个流程中,追踪上下文保持连续。
|
||||
|
||||
在 `internal/service/pay_solve.go` 文件中,`SolvePaySuccess` 和 `SolvePayFail` 方法处理支付成功和失败的后续操作。这些方法通过 `ctx` 参数接收并传递上下文信息,确保在异步通知、账户更新等操作中,追踪信息能够正确关联到原始请求。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[订单调度请求] --> B{验证订单状态}
|
||||
B --> |有效| C[选择支付通道]
|
||||
C --> D[创建支付请求]
|
||||
D --> E{第三方响应}
|
||||
E --> |成功| F[SolvePaySuccess]
|
||||
E --> |失败| G[SolvePayFail]
|
||||
F --> H[更新账户余额]
|
||||
F --> I[发送成功通知]
|
||||
G --> J[记录失败原因]
|
||||
G --> K[发送失败通知]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L30-L75)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L10-L100)
|
||||
|
||||
**Section sources**
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L30-L75)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L10-L100)
|
||||
|
||||
## 结论
|
||||
kami_gateway 系统通过精心设计的上下文传播机制,实现了 W3C Trace Context 和 W3C Baggage 标准的集成应用。这一机制不仅确保了全链路追踪的完整性,还为跨服务的业务上下文传递提供了支持。在支付网关的实际应用中,该机制有效支持了订单调度、代付处理等复杂业务流程的监控和管理,为系统的稳定运行和持续优化提供了重要保障。
|
||||
@@ -0,0 +1,117 @@
|
||||
# 初始化与资源配置
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [main.go](file://main.go)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [OpenTelemetry 初始化机制](#opentelemetry-初始化机制)
|
||||
2. [OTLP 协议连接与网络优化](#otlp-协议连接与网络优化)
|
||||
3. [资源对象与上下文标识](#资源对象与上下文标识)
|
||||
4. [分布式追踪上下文传播](#分布式追踪上下文传播)
|
||||
5. [追踪资源的优雅关闭](#追踪资源的优雅关闭)
|
||||
6. [生产环境配置建议](#生产环境配置建议)
|
||||
|
||||
## OpenTelemetry 初始化机制
|
||||
|
||||
`kami_gateway` 项目通过 `otelTrace.InitTracer()` 函数实现 OpenTelemetry 的全面初始化。该函数不仅负责追踪(Tracing)功能的启动,还同时初始化了指标(Metrics)和日志(Logs)的导出,构建了一个完整的可观测性(Observability)体系。此初始化过程是整个网关服务可观测性的基石,确保了所有运行时数据能够被有效收集和分析。
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
|
||||
## OTLP 协议连接与网络优化
|
||||
|
||||
`InitTracer()` 函数通过 OTLP (OpenTelemetry Protocol) 协议连接到后端的 OpenTelemetry 收集器(Collector)。其核心配置如下:
|
||||
|
||||
1. **非安全模式连接 (Insecure Mode)**:通过 `otlptracegrpc.WithInsecure()` 配置,使用明文 gRPC 连接。这适用于内部网络或已通过其他方式(如 VPC、服务网格)保障安全的环境,避免了 TLS 握手的性能开销。
|
||||
2. **超时设置 (Timeout)**:通过 `otlptracegrpc.WithTimeout(DefaultTimeout)` 设置了 5 秒的超时时间。该值在 `consts.go` 文件中定义为 `DefaultTimeout = 5 * time.Second`,旨在避免因网络延迟或收集器故障导致的应用程序长时间阻塞。
|
||||
3. **网络传输优化**:
|
||||
* **Gzip 压缩**:通过 `otlptracegrpc.WithCompressor("gzip")` 启用 Gzip 压缩,显著减少了网络传输的数据量,降低了带宽消耗和传输延迟。
|
||||
* **重试机制**:配置了指数退避重试策略(`WithRetry`),初始重试间隔为 1 秒,最大为 10 秒,总重试时间不超过 30 秒,增强了在短暂网络抖动下的鲁棒性。
|
||||
* **批量处理**:使用 `sdktrace.NewBatchSpanProcessor` 将多个追踪片段(Span)批量导出,减少了与收集器的连接次数,提升了整体性能。
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L32-L54)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L14-L18)
|
||||
|
||||
## 资源对象与上下文标识
|
||||
|
||||
为了确保追踪数据具备正确的上下文信息,`InitTracer()` 函数创建了一个 `Resource` 对象,并通过 `attribute.String` 设置了关键的标识属性:
|
||||
|
||||
* **服务名称 (service.name)**:从环境变量 `serverName` 中读取,确保每个服务实例都有唯一的标识。
|
||||
* **部署环境 (deployment.environment)**:从环境变量 `ENVIRONMENT` 中读取,用于区分开发、测试、生产等不同环境的追踪数据。
|
||||
* **语言类型 (library.language)**:固定为 `go`,标识了服务的开发语言。
|
||||
|
||||
这些属性作为追踪数据的元数据,使得在后端分析系统(如 Jaeger、Zipkin)中能够方便地对数据进行过滤、聚合和分析。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["InitTracer()"] --> B["创建 OTLP gRPC 客户端"]
|
||||
B --> C["配置: Insecure, Endpoint, Timeout, Gzip"]
|
||||
C --> D["创建 Resource 对象"]
|
||||
D --> E["设置属性: service.name, deployment.environment, library.language"]
|
||||
E --> F["创建 TracerProvider"]
|
||||
F --> G["返回 Shutdown 函数"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L56-L70)
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L56-L70)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L37-L38)
|
||||
|
||||
## 分布式追踪上下文传播
|
||||
|
||||
为了实现跨服务的分布式追踪,`InitTracer()` 函数通过 `otel.SetTextMapPropagator()` 配置了全局的文本映射传播器(TextMapPropagator)。它使用 `propagation.NewCompositeTextMapPropagator` 组合了两种 W3C 标准:
|
||||
|
||||
* **W3C Trace Context (`propagation.TraceContext{}`)**:负责在 HTTP 请求头(如 `traceparent` 和 `tracestate`)中传递追踪的上下文信息(Trace ID, Span ID 等),确保调用链路的连续性。
|
||||
* **W3C Baggage (`propagation.Baggage{}`)**:允许在请求中携带用户自定义的键值对数据(如用户ID、租户ID),这些数据会随着调用链路在所有服务间传递,为业务逻辑提供上下文。
|
||||
|
||||
此配置确保了 `kami_gateway` 发起或接收的任何请求都能正确地传播和接收分布式追踪上下文。
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L198-L203)
|
||||
|
||||
## 追踪资源的优雅关闭
|
||||
|
||||
在 `main.go` 文件中,`InitTracer()` 函数被调用,并通过 `defer` 语句注册了其返回的关闭函数。`InitTracer()` 函数返回了三个 `Shutdown` 函数,分别用于优雅地关闭追踪、指标和日志的导出器。
|
||||
|
||||
```go
|
||||
cleanup1, cleanup2, cleanup3 := otelTrace.InitTracer()
|
||||
defer func() {
|
||||
if cleanup1 != nil {
|
||||
_ = cleanup1(otelTrace.InitCtx)
|
||||
}
|
||||
if cleanup2 != nil {
|
||||
_ = cleanup2(otelTrace.InitCtx)
|
||||
}
|
||||
if cleanup3 != nil {
|
||||
_ = cleanup3(otelTrace.InitCtx)
|
||||
}
|
||||
}()
|
||||
```
|
||||
|
||||
当应用程序接收到终止信号(如 SIGTERM)并开始退出时,`defer` 语句会执行。此时,`Shutdown` 函数会被调用,它们会:
|
||||
1. 将导出器缓冲区中所有待处理的追踪、指标和日志数据强制刷新(flush)到后端收集器。
|
||||
2. 等待数据发送完成或达到超时时间。
|
||||
3. 释放相关资源。
|
||||
|
||||
这个过程确保了在服务关闭前,所有重要的可观测性数据都不会丢失,实现了资源的优雅关闭。
|
||||
|
||||
**Section sources**
|
||||
- [main.go](file://main.go#L25-L35)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L205)
|
||||
|
||||
## 生产环境配置建议
|
||||
|
||||
基于 `kami_gateway` 的实现,以下是一些生产环境的配置调整建议:
|
||||
|
||||
1. **安全连接**:在生产环境中,应优先使用 `otlptracegrpc.WithTLSCredentials()` 替代 `WithInsecure()`,通过 TLS 加密保障数据传输安全。
|
||||
2. **动态采样**:当前代码中使用了固定的采样率(`DefaultSamplingRatio`)。在高流量场景下,可以考虑实现更复杂的采样策略,如基于请求速率、错误率或特定业务逻辑的动态采样,以平衡观测需求和系统开销。
|
||||
3. **收集器地址**:`collectorURL` 在 `consts.go` 中硬编码。建议将其移至配置文件或环境变量中,以提高部署的灵活性。
|
||||
4. **监控与告警**:代码中启动了 `monitorExporterHealth()` goroutine 来监控导出器健康状态。应确保此监控机制有效,并与告警系统集成,以便在数据导出失败时及时通知运维人员。
|
||||
5. **资源限制**:根据实际的服务器资源和流量情况,调整 `MaxQueueSize` 和 `MaxExportBatchSize` 等参数,避免因缓冲区过大导致内存溢出,或因批量过小影响性能。
|
||||
@@ -0,0 +1,135 @@
|
||||
# 资源标识与上下文配置
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [init.go](file://internal/otelTrace/init.go)
|
||||
- [consts.go](file://internal/otelTrace/consts.go)
|
||||
- [main.go](file://main.go)
|
||||
- [logs.go](file://internal/otelTrace/logs.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [资源对象构建](#资源对象构建)
|
||||
2. [关键属性配置](#关键属性配置)
|
||||
3. [上下文共享机制](#上下文共享机制)
|
||||
4. [初始化流程](#初始化流程)
|
||||
|
||||
## 资源对象构建
|
||||
|
||||
在 `kami_gateway` 项目中,OpenTelemetry 的资源(Resource)对象是通过 `resource.New` 函数构建的,该过程是整个可观测性系统的基础。资源对象的创建发生在 `InitTracer` 函数内部,作为初始化追踪系统的一部分。此对象封装了服务的静态元数据,为后续的追踪、指标和日志数据提供了统一的上下文标识。
|
||||
|
||||
`resource.New` 函数接收一个上下文和一系列配置选项,其中核心是 `resource.WithAttributes`,它允许开发者定义一组键值对属性。这些属性在分布式系统中至关重要,它们使得来自不同服务的遥测数据能够被正确地关联、聚合和过滤。资源对象的构建是整个 `InitTracer` 流程中的关键步骤,它确保了所有后续生成的追踪、指标和日志都携带一致的元数据。
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L47-L55)
|
||||
|
||||
## 关键属性配置
|
||||
|
||||
资源对象通过 `attribute.String` 函数设置了三个核心属性,这些属性构成了服务的身份标识。
|
||||
|
||||
`library.language` 属性被硬编码为 `"go"`,明确标识了该服务是使用 Go 语言编写的。这一信息对于运维团队和监控系统至关重要,因为它允许按编程语言对服务进行分类和分析,例如,可以快速筛选出所有 Go 语言服务的性能指标或错误日志。
|
||||
|
||||
`service.name` 属性的值来源于 `consts.go` 文件中定义的 `serviceName` 变量。该变量的值是字符串 `"网关服务——"` 与环境变量 `serverName` 的拼接结果。这种动态命名方式确保了即使在同一个代码库下部署多个实例(如不同的网关实例),每个实例也能拥有一个唯一且可识别的服务名称,便于在服务发现和监控中进行区分。
|
||||
|
||||
`deployment.environment` 属性的值通过 `env.Get("ENVIRONMENT", "production")` 从环境变量 `ENVIRONMENT` 中读取。如果该环境变量未设置,则默认值为 `"production"`。这一设计实践遵循了十二要素应用(12-Factor App)的原则,使得同一份代码可以在开发、测试、预发布和生产等不同环境中运行,而无需修改代码。监控系统可以利用此属性对不同环境的数据进行隔离分析,例如,可以对比生产环境和预发布环境的性能差异。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start["开始构建资源对象"] --> SetLanguage["设置 library.language='go'"]
|
||||
SetLanguage --> SetServiceName["设置 service.name"]
|
||||
SetServiceName --> GetServerName["读取环境变量 serverName"]
|
||||
GetServerName --> Concat["拼接 '网关服务——' + serverName"]
|
||||
Concat --> SetDeploymentEnv["设置 deployment.environment"]
|
||||
SetDeploymentEnv --> ReadEnv["读取环境变量 ENVIRONMENT"]
|
||||
ReadEnv --> CheckExist{"ENVIRONMENT 是否存在?"}
|
||||
CheckExist --> |是| UseEnv["使用 ENVIRONMENT 的值"]
|
||||
CheckExist --> |否| UseDefault["使用默认值 'production'"]
|
||||
UseEnv --> End["完成资源对象构建"]
|
||||
UseDefault --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L49-L51)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L47)
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L49-L51)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L47)
|
||||
|
||||
## 上下文共享机制
|
||||
|
||||
资源对象在 `kami_gateway` 中扮演着“上下文锚点”的角色,它被 `TracerProvider`、`MeterProvider` 和 `LoggerProvider` 共享,从而实现了追踪、指标和日志三者之间的上下文一致性。
|
||||
|
||||
`TracerProvider` 在创建时,通过 `sdktrace.WithResource(resources)` 将资源对象注入。这意味着该服务生成的所有追踪(Trace)和跨度(Span)都会自动携带 `library.language`、`service.name` 和 `deployment.environment` 这些标签。这使得在分布式追踪系统中,可以轻松地根据服务名称或部署环境来过滤和聚合链路。
|
||||
|
||||
`MeterProvider` 同样通过 `sdkMetric.WithResource(resources)` 接收资源对象。这确保了该服务上报的所有指标(如请求延迟、错误率)都带有相同的服务标识。监控系统可以基于这些标签进行多维度的数据切片和聚合,例如,计算生产环境中所有网关服务的平均响应时间。
|
||||
|
||||
`LoggerProvider` 通过 `sdklog.WithResource(resources)` 将资源对象与日志系统绑定。这使得通过 OpenTelemetry 导出的日志条目也包含了服务的元数据。更重要的是,项目中定义了一个 `CustomLogger` 结构体,它封装了 `zap.Logger`,并提供了一个 `WithContext` 方法。该方法能够从传入的 `context.Context` 中提取追踪上下文(如 Trace ID 和 Span ID),并将其作为字段注入到日志中。这实现了日志与追踪的深度关联,运维人员可以通过一个 Trace ID 在日志系统中直接搜索到与该次请求相关的所有日志。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Resource["Resource对象<br>library.language='go'<br>service.name='...'<br>deployment.environment='...'"]:::resource
|
||||
TracerProvider["TracerProvider<br>生成Traces"]:::provider
|
||||
MeterProvider["MeterProvider<br>生成Metrics"]:::provider
|
||||
LoggerProvider["LoggerProvider<br>生成Logs"]:::provider
|
||||
Resource --> TracerProvider
|
||||
Resource --> MeterProvider
|
||||
Resource --> LoggerProvider
|
||||
subgraph "CustomLogger"
|
||||
CustomLogger["CustomLogger结构体"]
|
||||
WithContext["WithContext(ctx)方法<br>从ctx提取TraceID/SpanID"]
|
||||
end
|
||||
LoggerProvider --> CustomLogger
|
||||
CustomLogger --> WithContext
|
||||
classDef resource fill:#e1f5fe,stroke:#039be5,stroke-width:2px;
|
||||
classDef provider fill:#f3e5f5,stroke:#8e24aa,stroke-width:2px;
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L58-L62)
|
||||
- [init.go](file://internal/otelTrace/init.go#L84-L88)
|
||||
- [init.go](file://internal/otelTrace/init.go#L112-L116)
|
||||
- [init.go](file://internal/otelTrace/init.go#L131-L135)
|
||||
- [logs.go](file://internal/otelTrace/logs.go#L9-L11)
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L58-L62)
|
||||
- [init.go](file://internal/otelTrace/init.go#L84-L88)
|
||||
- [init.go](file://internal/otelTrace/init.go#L112-L116)
|
||||
- [init.go](file://internal/otelTrace/init.go#L131-L135)
|
||||
- [logs.go](file://internal/otelTrace/logs.go#L9-L11)
|
||||
|
||||
## 初始化流程
|
||||
|
||||
资源对象的构建和共享是 `InitTracer` 函数执行流程中的一个环节。该函数的调用发生在 `main.go` 文件的 `main` 函数中,是服务启动过程的关键步骤。`InitTracer` 不仅负责创建资源对象,还负责配置追踪、指标和日志的导出器(Exporter),并将它们与各自的 Provider 绑定。
|
||||
|
||||
在 `main` 函数中,`otelTrace.InitTracer()` 被调用,其返回的三个 `Shutdown` 函数通过 `defer` 语句注册,确保在服务退出时能够优雅地关闭所有可观测性组件,避免数据丢失。`InitTracer` 函数内部的执行顺序是:首先创建资源对象,然后基于该资源对象初始化 `TracerProvider`、`MeterProvider` 和 `LoggerProvider`,最后将这些 Provider 设置为全局单例。
|
||||
|
||||
这种设计模式确保了整个应用生命周期内,所有组件使用的都是同一个、配置一致的可观测性基础设施。通过在应用启动时集中配置,避免了在代码各处分散配置所带来的不一致性和维护困难。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Main as main.go
|
||||
participant Init as InitTracer()
|
||||
participant Resource as resource.New()
|
||||
participant Tracer as TracerProvider
|
||||
participant Meter as MeterProvider
|
||||
participant Logger as LoggerProvider
|
||||
Main->>Init : 调用 InitTracer()
|
||||
Init->>Resource : 创建资源对象
|
||||
Resource-->>Init : 返回 resources
|
||||
Init->>Tracer : 使用 resources 初始化
|
||||
Init->>Meter : 使用 resources 初始化
|
||||
Init->>Logger : 使用 resources 初始化
|
||||
Init->>Init : 设置全局 Provider
|
||||
Init-->>Main : 返回 cleanup 函数
|
||||
Main->>Main : defer 执行 cleanup
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [main.go](file://main.go#L23-L26)
|
||||
- [init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
|
||||
**Section sources**
|
||||
- [main.go](file://main.go#L23-L26)
|
||||
- [init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
@@ -0,0 +1,256 @@
|
||||
# 追踪导出器配置
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [init.go](file://internal/otelTrace/init.go)
|
||||
- [consts.go](file://internal/otelTrace/consts.go)
|
||||
- [main.go](file://main.go)
|
||||
- [circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go)
|
||||
- [utils.go](file://internal/otelTrace/utils.go)
|
||||
- [logs.go](file://internal/otelTrace/logs.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [项目结构](#项目结构)
|
||||
2. [核心组件](#核心组件)
|
||||
3. [追踪导出器配置机制](#追踪导出器配置机制)
|
||||
4. [gRPC通信配置](#grpc通信配置)
|
||||
5. [超时与重试机制](#超时与重试机制)
|
||||
6. [数据压缩与网络优化](#数据压缩与网络优化)
|
||||
7. [优雅关闭机制](#优雅关闭机制)
|
||||
8. [熔断与健康监控](#熔断与健康监控)
|
||||
|
||||
## 项目结构
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "根目录"
|
||||
main[main.go]
|
||||
end
|
||||
subgraph "配置"
|
||||
conf[conf/app.conf]
|
||||
end
|
||||
subgraph "内部模块"
|
||||
otelTrace[internal/otelTrace]
|
||||
config[internal/config]
|
||||
models[internal/models]
|
||||
service[internal/service]
|
||||
routers[internal/routers]
|
||||
end
|
||||
main --> otelTrace
|
||||
main --> config
|
||||
main --> models
|
||||
main --> service
|
||||
main --> routers
|
||||
otelTrace --> consts[consts.go]
|
||||
otelTrace --> init[init.go]
|
||||
otelTrace --> circuit_breaker[circuit_breaker.go]
|
||||
otelTrace --> utils[utils.go]
|
||||
otelTrace --> logs[logs.go]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L1-L56)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L1-L257)
|
||||
|
||||
**Section sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L1-L56)
|
||||
|
||||
## 核心组件
|
||||
|
||||
本项目的核心组件包括OpenTelemetry追踪系统、配置管理、服务路由和代理池等模块。其中,OpenTelemetry追踪系统通过`internal/otelTrace`包实现,负责分布式追踪、指标收集和日志导出功能。该系统与后端收集器通过gRPC协议通信,实现了生产级的可观测性解决方案。
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L1-L257)
|
||||
- [internal/config/config.go](file://internal/config/config.go)
|
||||
|
||||
## 追踪导出器配置机制
|
||||
|
||||
kami_gateway中的OpenTelemetry追踪导出器配置机制通过`InitTracer`函数实现,该函数在应用启动时初始化追踪、指标和日志的导出器。配置机制采用生产环境优化策略,包括动态采样率、批量传输优化和资源标识配置。
|
||||
|
||||
导出器配置的核心是`otlptracegrpc.NewClient`,它创建了一个gRPC客户端用于与后端收集器通信。同时,系统还配置了指标导出器和日志导出器,形成完整的可观测性解决方案。资源标识配置包含了服务名称、部署环境等关键属性,便于在分布式系统中识别和关联追踪数据。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class InitTracer {
|
||||
+InitTracer() (func(context.Context) error, func(context.Context) error, func(context.Context) error)
|
||||
}
|
||||
class traceExporter {
|
||||
+traceExporter
|
||||
}
|
||||
class metricExporter {
|
||||
+metricExporter
|
||||
}
|
||||
class logExporter {
|
||||
+logExporter
|
||||
}
|
||||
class resources {
|
||||
+resources
|
||||
}
|
||||
class batchProcessor {
|
||||
+batchProcessor
|
||||
}
|
||||
InitTracer --> traceExporter : "创建"
|
||||
InitTracer --> metricExporter : "创建"
|
||||
InitTracer --> logExporter : "创建"
|
||||
InitTracer --> resources : "创建"
|
||||
InitTracer --> batchProcessor : "创建"
|
||||
InitTracer --> otel : "设置TracerProvider"
|
||||
InitTracer --> otel : "设置MeterProvider"
|
||||
InitTracer --> global : "设置LoggerProvider"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L1-L56)
|
||||
|
||||
## gRPC通信配置
|
||||
|
||||
追踪导出器通过gRPC协议与后端收集器通信,配置中包含了多个关键参数。`WithInsecure`模式用于建立非加密连接,适用于内部网络环境,降低了TLS握手的性能开销。`WithEndpoint`指定了收集器的端点地址,当前配置为`38.38.251.113:31547`。
|
||||
|
||||
gRPC通信配置还包含了批量传输队列的优化设置,如`WithBatchTimeout`设置为5秒的批量发送间隔,`WithMaxExportBatchSize`限制单次导出批量为512条,`WithMaxQueueSize`设置最大队列大小为2048条。这些配置平衡了实时性和性能,防止内存溢出。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Gateway as kami_gateway
|
||||
participant Collector as 后端收集器
|
||||
Gateway->>Gateway : InitTracer()
|
||||
Gateway->>Gateway : 创建otlptracegrpc.NewClient
|
||||
Gateway->>Gateway : 配置WithInsecure模式
|
||||
Gateway->>Gateway : 设置collectorURL端点
|
||||
Gateway->>Gateway : 配置5秒超时
|
||||
Gateway->>Gateway : 配置重试机制
|
||||
Gateway->>Gateway : 启用gzip压缩
|
||||
Gateway->>Collector : 建立gRPC连接
|
||||
loop 批量发送
|
||||
Gateway->>Gateway : 收集追踪数据
|
||||
Gateway->>Gateway : 达到5秒间隔或批量阈值
|
||||
Gateway->>Collector : 发送批量追踪数据
|
||||
Collector-->>Gateway : 确认接收
|
||||
end
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L33-L75)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L1-L56)
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L33-L75)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L1-L56)
|
||||
|
||||
## 超时与重试机制
|
||||
|
||||
系统配置了5秒的默认超时(`DefaultTimeout`),这对系统稳定性至关重要。超时设置平衡了响应性和稳定性,避免因网络延迟导致的长时间阻塞。同时,系统实现了生产级的重试机制,通过`RetryConfig`进行调优。
|
||||
|
||||
重试机制的生产级调优策略包括:1秒的初始重试间隔(`InitialRetryInterval`),允许快速恢复;10秒的最大重试间隔(`MaxRetryInterval`),避免频繁重试造成网络拥塞;30秒的最大重试总时间(`MaxRetryElapsedTime`),防止长时间阻塞。这些参数共同构成了一个指数退避重试策略,有效应对网络抖动。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始导出]) --> CheckNetwork["检查网络连接"]
|
||||
CheckNetwork --> NetworkOK{"网络正常?"}
|
||||
NetworkOK --> |是| Export["导出追踪数据"]
|
||||
NetworkOK --> |否| Retry["执行重试逻辑"]
|
||||
Export --> ExportSuccess{"导出成功?"}
|
||||
ExportSuccess --> |是| End([完成])
|
||||
ExportSuccess --> |否| Timeout{"超时?"}
|
||||
Timeout --> |是| Retry
|
||||
Timeout --> |否| CheckError["检查错误类型"]
|
||||
Retry --> Wait["等待重试间隔"]
|
||||
Wait --> WaitTime{"等待时间 < 最大重试时间?"}
|
||||
WaitTime --> |是| WaitDuration["计算等待时长<br/>初始:1秒<br/>最大:10秒"]
|
||||
WaitTime --> |否| Fail["导出失败"]
|
||||
WaitDuration --> Wait
|
||||
Fail --> End
|
||||
End --> Cleanup["清理资源"]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L38-L75)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L1-L56)
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L38-L75)
|
||||
- [internal/otelTrace/consts.go](file://internal/otelTrace/consts.go#L1-L56)
|
||||
|
||||
## 数据压缩与网络优化
|
||||
|
||||
系统通过`WithCompressor("gzip")`配置启用gzip压缩,显著降低网络传输开销。这一配置应用于追踪、指标和日志三种数据类型的导出器,实现了全面的网络优化。gzip压缩在高负载场景下尤为重要,可以大幅减少带宽消耗和传输延迟。
|
||||
|
||||
除了数据压缩,系统还实施了多项网络优化策略。批量传输机制减少了网络请求次数,5秒的批量发送间隔平衡了实时性和性能。最大导出批量(512条)和最大队列大小(2048条)的设置防止了单次传输过大导致的网络拥塞和内存溢出问题。
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L68-L75)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L104-L111)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L135-L140)
|
||||
|
||||
## 优雅关闭机制
|
||||
|
||||
`traceExporter.Shutdown`函数在`main.go`中通过defer调用实现优雅关闭行为。该机制确保在应用终止时,所有待处理的追踪数据都能被完整导出,避免数据丢失。优雅关闭是生产环境的关键特性,保证了可观测性数据的完整性。
|
||||
|
||||
在`main.go`中,`InitTracer`函数返回三个清理函数,分别用于追踪、指标和日志导出器的关闭。defer语句确保这些清理函数在main函数退出时执行,即使发生panic也能保证资源的正确释放。这种设计模式提高了系统的可靠性和稳定性。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Main as main.go
|
||||
participant Init as InitTracer
|
||||
participant Shutdown as Shutdown
|
||||
Main->>Init : 调用InitTracer()
|
||||
Init-->>Main : 返回cleanup1, cleanup2, cleanup3
|
||||
Main->>Main : defer调用清理函数
|
||||
Main->>Main : 执行主要业务逻辑
|
||||
Main->>Main : 应用即将退出
|
||||
Main->>Shutdown : 执行defer清理
|
||||
Shutdown->>Shutdown : 调用cleanup1(InitCtx)
|
||||
Shutdown->>Shutdown : 调用cleanup2(InitCtx)
|
||||
Shutdown->>Shutdown : 调用cleanup3(InitCtx)
|
||||
Shutdown-->>Main : 完成优雅关闭
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [main.go](file://main.go#L30-L38)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L204-L205)
|
||||
|
||||
**Section sources**
|
||||
- [main.go](file://main.go#L30-L38)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L204-L205)
|
||||
|
||||
## 熔断与健康监控
|
||||
|
||||
系统实现了熔断器模式和健康监控机制,增强了追踪系统的可靠性。`CircuitBreaker`结构体实现了简单的熔断器,当连续失败达到阈值(5次)时,熔断器开启,避免对后端收集器造成持续压力。30秒的重置超时允许系统在故障恢复后自动恢复正常。
|
||||
|
||||
`monitorExporterHealth`函数作为后台goroutine运行,每30秒检查一次导出器的健康状态。它监控错误率,当失败次数超过阈值时发出警告。同时,系统实现了动态采样率调整机制,在检测到高负载时自动降低采样率(从10%降至5%),保护系统稳定性。
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Closed
|
||||
Closed --> Open : 连续失败≥5次
|
||||
Open --> HalfOpen : 超时30秒
|
||||
HalfOpen --> Closed : 成功
|
||||
HalfOpen --> Open : 失败
|
||||
Closed --> Closed : 正常导出
|
||||
Open --> Open : 直接返回
|
||||
HalfOpen --> HalfOpen : 尝试导出
|
||||
note right of Open
|
||||
熔断器开启状态
|
||||
暂停导出请求
|
||||
避免雪崩效应
|
||||
end note
|
||||
note left of HalfOpen
|
||||
熔断器半开状态
|
||||
允许试探性请求
|
||||
验证系统恢复情况
|
||||
end note
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go#L1-L50)
|
||||
- [internal/otelTrace/utils.go](file://internal/otelTrace/utils.go#L16-L46)
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/circuit_breaker.go](file://internal/otelTrace/circuit_breaker.go#L1-L50)
|
||||
- [internal/otelTrace/utils.go](file://internal/otelTrace/utils.go#L16-L46)
|
||||
@@ -0,0 +1,180 @@
|
||||
# 采样策略与批量处理
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [init.go](file://internal/otelTrace/init.go)
|
||||
- [simple.go](file://internal/otelTrace/simple.go)
|
||||
- [consts.go](file://internal/otelTrace/consts.go)
|
||||
- [utils.go](file://internal/otelTrace/utils.go)
|
||||
- [main.go](file://main.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [引言](#引言)
|
||||
2. [采样策略详解](#采样策略详解)
|
||||
3. [批量处理机制](#批量处理机制)
|
||||
4. [生产环境性能分析](#生产环境性能分析)
|
||||
5. [高并发支付场景调优建议](#高并发支付场景调优建议)
|
||||
6. [结论](#结论)
|
||||
|
||||
## 引言
|
||||
本文档详细解析 kami_gateway 项目中 OpenTelemetry 的采样策略与批量处理机制。通过分析 `traceIDRatioBased` 采样器如何基于比率控制生成的追踪数据量,以及 `sdktrace.NewBatchSpanProcessor` 的配置参数,深入探讨这些机制如何在监控精度与系统性能开销之间取得平衡。文档结合生产环境需求,分析这些参数对高并发支付场景下的性能影响,并提供相应的调优建议。
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [simple.go](file://internal/otelTrace/simple.go#L80-L93)
|
||||
|
||||
## 采样策略详解
|
||||
|
||||
### traceIDRatioBased 采样器
|
||||
`traceIDRatioBased` 采样器是 kami_gateway 项目中用于控制追踪数据生成量的核心机制。该采样器基于给定的比率对追踪进行采样,确保在高负载场景下既能保持足够的监控精度,又不会对系统性能造成过大开销。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> ValidateRatio["验证采样比率"]
|
||||
ValidateRatio --> RatioValid{"比率有效?"}
|
||||
RatioValid --> |否| SetZero["设置为0"]
|
||||
RatioValid --> |是| CalculateBound["计算traceIDUpperBound"]
|
||||
CalculateBound --> SampleDecision["基于TraceID进行采样决策"]
|
||||
SampleDecision --> Drop["丢弃Span"]
|
||||
SampleDecision --> Record["记录并采样Span"]
|
||||
SetZero --> Drop
|
||||
Drop --> End([结束])
|
||||
Record --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [simple.go](file://internal/otelTrace/simple.go#L80-L93)
|
||||
|
||||
#### 采样逻辑实现
|
||||
`traceIDRatioBased` 函数接收一个浮点数 `fraction` 作为采样比率参数。当比率大于等于1时,返回 `sdkTrace.AlwaysSample()`,表示始终采样;当比率小于等于0时,将其设置为0,表示不采样。对于介于0和1之间的比率,函数创建并返回一个 `traceIDRatioSampler` 结构体实例,该实例包含 `traceIDUpperBound` 和描述信息。
|
||||
|
||||
`traceIDUpperBound` 是通过将采样比率乘以 `1 << 63` 计算得出的,这确保了采样决策的均匀分布。在 `ShouldSample` 方法中,系统通过比较生成的 TraceID 与 `traceIDUpperBound` 来决定是否采样该 Span。
|
||||
|
||||
#### 强制采样机制
|
||||
除了基于比率的采样外,系统还实现了强制采样机制。对于特定的追踪名称(如 "PayNotify"、"HandleSendCardTask")或包含错误属性的 Span,系统会强制进行采样,确保关键业务流程和错误情况能够被完整记录。
|
||||
|
||||
**Section sources**
|
||||
- [simple.go](file://internal/otelTrace/simple.go#L1-L94)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L30-L33)
|
||||
|
||||
## 批量处理机制
|
||||
|
||||
### 批量处理器配置
|
||||
kami_gateway 使用 `sdktrace.NewBatchSpanProcessor` 来优化追踪数据的导出过程。该处理器通过批量处理 Span,显著减少了网络传输次数和系统开销。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BatchSpanProcessor {
|
||||
+traceExporter Exporter
|
||||
+batchTimeout Duration
|
||||
+maxExportBatchSize int
|
||||
+maxQueueSize int
|
||||
+exportTimeout Duration
|
||||
+Start()
|
||||
+OnStart(span Span)
|
||||
+OnEnd(span Span)
|
||||
+Shutdown(ctx Context)
|
||||
+ForceFlush(ctx Context)
|
||||
}
|
||||
class SpanExporter {
|
||||
<<interface>>
|
||||
+Export(ctx Context, spans []SpanData) error
|
||||
+Shutdown(ctx Context) error
|
||||
}
|
||||
BatchSpanProcessor --> SpanExporter : "使用"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L60-L75)
|
||||
|
||||
#### 核心配置参数
|
||||
批量处理器的配置参数经过精心设计,以优化内存使用与吞吐量:
|
||||
|
||||
- **BatchTimeout (5秒)**:控制批量发送的频率。每5秒,处理器会将队列中的 Span 批量导出,平衡了实时性与性能。
|
||||
- **MaxExportBatchSize (512条)**:定义单次导出的最大批量。该参数避免了单次传输过大,减少了网络拥塞的风险。
|
||||
- **MaxQueueSize (2048条)**:设置最大队列大小,防止内存溢出。当队列满时,新的 Span 将被丢弃,保护系统稳定性。
|
||||
- **ExportTimeout (5秒)**:防止导出操作阻塞。如果导出操作在5秒内未完成,将被中断,避免影响主业务流程。
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L60-L75)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L20-L25)
|
||||
|
||||
## 生产环境性能分析
|
||||
|
||||
### 系统性能影响
|
||||
在高并发支付场景下,OpenTelemetry 的采样与批量处理机制对系统性能有着重要影响。合理的配置能够显著降低系统开销,同时保持足够的监控精度。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "性能影响因素"
|
||||
A[采样率] --> B[CPU使用率]
|
||||
A --> C[内存占用]
|
||||
D[批量大小] --> E[网络延迟]
|
||||
D --> F[吞吐量]
|
||||
G[导出超时] --> H[响应时间]
|
||||
G --> I[稳定性]
|
||||
end
|
||||
subgraph "优化目标"
|
||||
B --> J[降低CPU开销]
|
||||
C --> K[减少内存压力]
|
||||
E --> L[降低网络延迟]
|
||||
F --> M[提高吞吐量]
|
||||
H --> N[保证响应速度]
|
||||
I --> O[提升系统稳定性]
|
||||
end
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L15-L35)
|
||||
|
||||
#### 动态采样率调整
|
||||
系统实现了动态采样率调整机制,通过 `monitorExporterHealth` 函数定期检查导出器的健康状态。当导出失败次数超过阈值时,系统会自动降低采样率,从默认的10%降至5%,以保护系统稳定性。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Monitor as monitorExporterHealth
|
||||
participant Logger as Logger
|
||||
participant Tracer as TracerProvider
|
||||
Monitor->>Monitor : 每30秒检查一次
|
||||
Monitor->>Monitor : 获取导出失败次数
|
||||
Monitor->>Monitor : 判断是否超过阈值
|
||||
alt 失败次数>10
|
||||
Monitor->>Logger : 记录警告日志
|
||||
Monitor->>Monitor : 判断是否需要降低采样率
|
||||
alt 需要降低
|
||||
Monitor->>Tracer : 调整采样率为5%
|
||||
Monitor->>Logger : 记录调整日志
|
||||
end
|
||||
end
|
||||
Monitor->>Monitor : 重置失败计数器
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [utils.go](file://internal/otelTrace/utils.go#L16-L46)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L33-L34)
|
||||
|
||||
**Section sources**
|
||||
- [utils.go](file://internal/otelTrace/utils.go#L16-L46)
|
||||
- [consts.go](file://internal/otelTrace/consts.go#L33-L34)
|
||||
|
||||
## 高并发支付场景调优建议
|
||||
|
||||
### 参数调优策略
|
||||
针对高并发支付场景,建议根据实际负载情况对 OpenTelemetry 的配置参数进行调优:
|
||||
|
||||
- **采样率**:在正常负载下保持10%的采样率,以确保足够的监控精度。在高负载或系统资源紧张时,可动态降低至5%,以保护系统稳定性。
|
||||
- **批量发送间隔**:5秒的批量发送间隔在大多数场景下表现良好。在需要更高实时性的场景中,可适当缩短至2-3秒,但需注意增加的网络开销。
|
||||
- **批量大小**:512条的单次导出批量是一个平衡点。在高吞吐量场景中,可适当增加至1000条,以提高导出效率。
|
||||
- **队列大小**:2048条的最大队列大小能够有效应对突发流量。在内存充足的系统中,可适当增加至4096条,以提供更大的缓冲空间。
|
||||
|
||||
### 监控与告警
|
||||
建议建立完善的监控与告警机制,实时监控 OpenTelemetry 导出器的健康状态。当导出失败率持续升高时,应及时调整采样率或检查后端服务的稳定性。
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [utils.go](file://internal/otelTrace/utils.go#L16-L46)
|
||||
|
||||
## 结论
|
||||
kami_gateway 项目中的 OpenTelemetry 采样与批量处理机制经过精心设计,能够在高并发支付场景下有效平衡监控精度与系统性能开销。通过 `traceIDRatioBased` 采样器和 `sdktrace.NewBatchSpanProcessor` 的合理配置,系统能够在保证关键业务流程监控的同时,最大限度地减少对主业务流程的影响。建议根据实际生产环境的需求,灵活调整相关参数,以达到最佳的性能与监控效果。
|
||||
@@ -0,0 +1,95 @@
|
||||
# 日志与追踪上下文关联
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [logs.go](file://internal/otelTrace/logs.go)
|
||||
- [init.go](file://internal/otelTrace/init.go)
|
||||
- [consts.go](file://internal/otelTrace/consts.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [CustomLogger 结构体设计](#customlogger-结构体设计)
|
||||
3. [日志与 OTLP 集成机制](#日志与-otlp-集成机制)
|
||||
4. [日志与 Trace ID 关联分析](#日志与-trace-id-关联分析)
|
||||
5. [业务代码使用最佳实践](#业务代码使用最佳实践)
|
||||
6. [总结](#总结)
|
||||
|
||||
## 简介
|
||||
本文档详细阐述 kami_gateway 项目中日志系统与分布式追踪系统的深度集成方案。重点分析 `CustomLogger` 结构体的设计原理,特别是 `WithContext()` 方法如何从 `context.Context` 中提取有效的 Span,并将上下文信息注入日志字段。同时说明在上下文无效或缺失时的降级处理逻辑,以及如何通过 OTLP 协议将日志导出至集中式观测平台,同时保留本地控制台输出用于调试。该集成机制有效实现了日志与追踪上下文的关联,极大提升了分布式系统的问题排查效率。
|
||||
|
||||
## CustomLogger 结构体设计
|
||||
|
||||
`CustomLogger` 是 kami_gateway 中用于封装日志功能的核心结构体,其设计目标是实现日志与 OpenTelemetry 分布式追踪上下文的无缝集成。该结构体通过 `WithContext()` 方法实现上下文感知的日志记录能力。
|
||||
|
||||
`WithContext()` 方法的实现逻辑如下:
|
||||
- 当传入的 `context.Context` 为 `nil` 时,直接返回原始日志记录器,避免空指针异常。
|
||||
- 使用 `trace.SpanFromContext(ctx)` 从上下文中提取当前活动的 Span。
|
||||
- 检查 Span 上下文的有效性(`IsValid()`),若无效则返回原始日志记录器或 `zap.NewNop()`(当日志器为空时)。
|
||||
- 若 Span 有效,则通过 `zap.Reflect("ctx", ctx)` 将整个上下文对象作为字段注入日志,确保追踪信息与日志条目绑定。
|
||||
|
||||
这种设计确保了在任何执行路径下日志系统都能稳定运行,即使在无追踪上下文的场景中也不会中断日志输出。
|
||||
|
||||
**节来源**
|
||||
- [logs.go](file://internal/otelTrace/logs.go#L9-L28)
|
||||
|
||||
## 日志与 OTLP 集成机制
|
||||
|
||||
在 `init.go` 文件中,通过 `otelzap.NewCore` 实现了日志到 OTLP 的导出集成。具体实现方式如下:
|
||||
|
||||
日志核心(core)通过 `zapcore.NewTee` 组合了两个输出目标:
|
||||
1. **OTLP 日志导出器**:使用 `otelzap.NewCore(serviceName, otelzap.WithLoggerProvider(loggerProvider))` 创建,将日志发送至配置的 OTLP 收集器(collectorURL)。
|
||||
2. **控制台输出**:使用 `zapcore.NewConsoleEncoder` 配合 `os.Stdout` 实现,保留本地调试输出能力。
|
||||
|
||||
该机制确保了生产环境中日志既能上报至集中式观测平台,又能在开发或调试阶段提供实时控制台输出。同时,日志导出器配置了超时、重试、gzip 压缩等生产级优化参数,保障了高负载场景下的稳定性。
|
||||
|
||||
**节来源**
|
||||
- [init.go](file://internal/otelTrace/init.go#L130-L180)
|
||||
|
||||
## 日志与 Trace ID 关联分析
|
||||
|
||||
kami_gateway 通过将 `context.Context` 注入日志字段的方式,实现了日志与 Trace ID 的自动关联。当请求经过启用了 OpenTelemetry 的中间件时,上下文会携带当前 Span 信息。通过 `WithContext()` 方法,该上下文被附加到日志记录器中,使得所有后续日志条目都隐式包含追踪上下文。
|
||||
|
||||
在观测平台中,可通过 Trace ID 直接检索与该请求相关的所有日志条目,形成完整的调用链视图。这种关联机制无需在业务代码中手动传递 Trace ID,降低了开发复杂度,同时保证了日志与追踪数据的一致性。
|
||||
|
||||
此外,系统还配置了 W3C Trace Context 和 Baggage 的复合传播器,确保跨服务调用时上下文信息的正确传递,支持完整的端到端分布式追踪。
|
||||
|
||||
**节来源**
|
||||
- [init.go](file://internal/otelTrace/init.go#L200-L205)
|
||||
- [logs.go](file://internal/otelTrace/logs.go#L20-L27)
|
||||
|
||||
## 业务代码使用最佳实践
|
||||
|
||||
在业务代码中应通过全局 `Logger` 变量结合 `WithContext()` 方法来记录上下文感知的日志。典型使用方式如下:
|
||||
|
||||
```go
|
||||
// 在处理请求的方法中
|
||||
func HandleRequest(ctx context.Context, req *Request) {
|
||||
// 获取上下文感知的日志记录器
|
||||
logger := otelTrace.Logger.WithContext(ctx)
|
||||
|
||||
// 记录带上下文的日志
|
||||
logger.Info("开始处理请求",
|
||||
zap.String("request_id", req.ID),
|
||||
zap.String("user_id", req.UserID),
|
||||
)
|
||||
|
||||
// 业务逻辑...
|
||||
|
||||
logger.Info("请求处理完成",
|
||||
zap.Int("status", http.StatusOK),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
关键要点:
|
||||
- 始终使用 `otelTrace.Logger.WithContext(ctx)` 获取日志器,而非直接使用 `zap.L()`。
|
||||
- 确保传入有效的 `context.Context`,通常由 HTTP 中间件或 RPC 框架提供。
|
||||
- 避免在无上下文的场景中强制传递 `nil`,`WithContext()` 方法已处理该情况。
|
||||
|
||||
**节来源**
|
||||
- [logs.go](file://internal/otelTrace/logs.go#L13-L28)
|
||||
- [init.go](file://internal/otelTrace/init.go#L178)
|
||||
|
||||
## 总结
|
||||
kami_gateway 通过 `CustomLogger` 结构体和 `WithContext()` 方法,实现了日志系统与 OpenTelemetry 分布式追踪的深度集成。该方案具备良好的健壮性,支持上下文缺失时的优雅降级,并通过 OTLP 协议实现日志的集中化管理。日志与 Trace ID 的自动关联极大提升了系统可观测性,为分布式环境下的问题排查提供了有力支持。建议在所有业务组件中统一采用 `WithContext()` 模式,确保日志上下文的一致性和完整性。
|
||||
354
.qoder/repowiki/zh/content/技术栈与依赖/技术栈与依赖.md
Normal file
354
.qoder/repowiki/zh/content/技术栈与依赖/技术栈与依赖.md
Normal file
@@ -0,0 +1,354 @@
|
||||
# 技术栈与依赖
|
||||
|
||||
<cite>
|
||||
**本文档中引用的文件**
|
||||
- [main.go](file://main.go)
|
||||
- [router.go](file://internal/routers/router.go)
|
||||
- [init.go](file://internal/otelTrace/init.go)
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go)
|
||||
- [redis.go](file://internal/cache/redis.go)
|
||||
- [config.go](file://internal/config/config.go)
|
||||
- [base_controller.go](file://internal/controllers/base_controller.go)
|
||||
- [init.go](file://internal/models/init.go)
|
||||
- [base_service.go](file://internal/service/base_service.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概览](#架构概览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考量](#性能考量)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
本文档详细阐述了 `kami_gateway` 项目的技术栈及其在项目中的具体应用。文档深入分析了 Beego v2 框架的使用,包括路由配置、控制器继承和中间件机制。同时,详细说明了 OpenTelemetry 分布式追踪系统的实现,涵盖 `otelTrace` 包中的 span 创建、日志集成和链路追踪上下文传递。文档还解释了 Redis 在缓存(`internal/cache`)和代理池管理中的双重角色,描述了 MySQL 数据库通过 Beego ORM 进行数据访问的模式,以及 RabbitMQ 在订单通知和查询消费者中的异步消息处理机制。最后,结合 `main.go` 中的初始化代码,说明了这些技术组件是如何被集成和启动的,并提供了每个技术选型的决策理由、版本兼容性要求和性能考量,以及配置最佳实践。
|
||||
|
||||
## 项目结构
|
||||
项目采用分层架构,主要分为 `conf`、`deploy`、`internal` 和根目录文件。`conf` 目录存放应用配置文件 `app.conf`。`deploy` 目录包含 Docker 部署相关的 `Dockerfile` 和 `docker-compose` 文件。`internal` 目录是项目的核心,包含了所有业务逻辑和基础设施代码,主要子目录包括:
|
||||
- `cache`: Redis 缓存客户端封装。
|
||||
- `config`: 应用配置和常量定义。
|
||||
- `controllers`: Beego 控制器,处理 HTTP 请求。
|
||||
- `models`: 数据库模型定义和 ORM 操作。
|
||||
- `otelTrace`: OpenTelemetry 分布式追踪和日志的初始化与中间件。
|
||||
- `proxy`: 代理池管理。
|
||||
- `routers`: 路由配置。
|
||||
- `service`: 业务逻辑服务。
|
||||
- `utils`: 工具函数。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "根目录"
|
||||
main[main.go]
|
||||
build[build.sh]
|
||||
CLAUDE[CLAUDE.md]
|
||||
end
|
||||
subgraph "配置"
|
||||
conf[conf/app.conf]
|
||||
end
|
||||
subgraph "部署"
|
||||
deploy[deploy/Dockerfile<br/>deploy/docker-compose.yaml]
|
||||
end
|
||||
subgraph "内部模块 internal"
|
||||
subgraph "基础设施"
|
||||
otelTrace[otelTrace]
|
||||
cache[cache]
|
||||
config[config]
|
||||
proxy[proxy]
|
||||
end
|
||||
subgraph "应用层"
|
||||
routers[routers]
|
||||
controllers[controllers]
|
||||
service[service]
|
||||
models[models]
|
||||
utils[utils]
|
||||
end
|
||||
end
|
||||
main --> otelTrace
|
||||
main --> cache
|
||||
main --> config
|
||||
main --> proxy
|
||||
main --> routers
|
||||
main --> service
|
||||
main --> models
|
||||
main --> utils
|
||||
conf --> config
|
||||
deploy --> main
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
|
||||
**Section sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
|
||||
## 核心组件
|
||||
本项目的核心组件围绕 Beego Web 框架构建,通过 `main.go` 文件进行初始化和集成。核心功能包括基于 Beego 的 HTTP 路由和控制器处理、通过 `otelTrace` 实现的分布式追踪、利用 Redis 实现的缓存和代理池、通过 Beego ORM 访问 MySQL 数据库,以及使用 RabbitMQ 进行异步消息处理。这些组件共同构成了一个高可用、可观测的网关服务。
|
||||
|
||||
**Section sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L1-L256)
|
||||
|
||||
## 架构概览
|
||||
系统采用典型的微服务网关架构。外部请求首先到达 Beego Web 服务器,经过 `otelTrace` 中间件进行分布式追踪和性能监控。请求被路由到相应的控制器(如 `ScanController`),控制器调用 `service` 层进行业务逻辑处理。业务逻辑层会通过 `models` 层的 Beego ORM 与 MySQL 数据库交互,并利用 `cache` 模块中的 Redis 客户端进行缓存读写。同时,系统通过 `notify` 和 `query` 模块启动后台消费者,从 RabbitMQ 消息队列中消费订单通知和查询任务,实现异步解耦。整个系统的可观测性由 OpenTelemetry 提供,所有追踪、指标和日志数据被导出到后端收集器。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Client[客户端] --> Beego[Beego Web 服务器]
|
||||
Beego --> Middleware[otelTrace 中间件]
|
||||
Middleware --> Router[路由 /gateway/scan]
|
||||
Router --> ScanController[ScanController]
|
||||
ScanController --> PayService[PayService]
|
||||
PayService --> BaseService[BaseService]
|
||||
PayService --> OrderModel[OrderInfo Model]
|
||||
PayService --> Cache[Redis 缓存]
|
||||
PayService --> DB[(MySQL 数据库)]
|
||||
PayService --> NotifyService[NotifyService]
|
||||
subgraph "异步处理"
|
||||
NotifyConsumer[订单通知消费者] --> RabbitMQ[(RabbitMQ)]
|
||||
QueryConsumer[供应商查询消费者] --> RabbitMQ
|
||||
RabbitMQ --> NotifyConsumer
|
||||
RabbitMQ --> QueryConsumer
|
||||
end
|
||||
subgraph "可观测性"
|
||||
OtelTrace[OpenTelemetry]
|
||||
OtelTrace -.-> Beego
|
||||
OtelTrace -.-> Cache
|
||||
OtelTrace -.-> DB
|
||||
OtelTrace -.-> RabbitMQ
|
||||
end
|
||||
style Beego fill:#f9f,stroke:#333
|
||||
style DB fill:#ccf,stroke:#333
|
||||
style RabbitMQ fill:#cfc,stroke:#333
|
||||
style Cache fill:#fcc,stroke:#333
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
- [internal/otelTrace/middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go#L1-L294)
|
||||
- [internal/models/init.go](file://internal/models/init.go#L1-L56)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### Beego v2 框架应用分析
|
||||
Beego v2 框架是本项目的核心 Web 框架。它负责处理所有 HTTP 请求、路由分发和控制器逻辑。
|
||||
|
||||
#### 路由配置
|
||||
项目的路由配置位于 `internal/routers/router.go` 文件中。通过调用 `web.Router` 函数,将不同的 URL 路径映射到对应的控制器和方法。例如,`/gateway/scan` 路径被映射到 `ScanController` 的 `Scan` 方法。此外,通过 `web.InsertFilterChain` 注册了一个全局过滤器链,将 `otelTrace.Middleware` 应用于所有路径(`*`),实现了对所有请求的统一追踪。
|
||||
|
||||
#### 控制器继承
|
||||
控制器通过继承 Beego 的 `web.Controller` 来获得处理 HTTP 请求的能力。`internal/controllers/base_controller.go` 定义了 `BaseGateway` 结构体,它直接嵌入了 `web.Controller`。项目中的其他控制器,如 `ScanController`,通过继承 `BaseGateway` 来复用基础功能,实现了代码的复用和分层。
|
||||
|
||||
#### 中间件使用
|
||||
`otelTrace.Middleware` 是一个关键的中间件,它被注入到请求处理链中。该中间件的主要职责是:
|
||||
1. **创建追踪 Span**:为每个 HTTP 请求创建一个新的追踪 Span,记录请求的路径、方法、状态码等信息。
|
||||
2. **上下文传递**:从请求头中提取上游服务的追踪上下文(Trace Context),并将其注入到当前请求的上下文中,实现跨服务的链路追踪。
|
||||
3. **性能监控**:记录请求的处理时长,并对慢请求(超过5秒)进行告警。
|
||||
4. **错误处理**:捕获处理过程中的 panic,记录错误信息,并返回 500 错误,实现优雅降级。
|
||||
5. **熔断保护**:集成了熔断器,当追踪系统自身出现异常时,可以自动熔断,避免影响核心业务。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as 客户端
|
||||
participant Beego as Beego 服务器
|
||||
participant Middleware as otelTrace 中间件
|
||||
participant Controller as ScanController
|
||||
Client->>Beego : POST /gateway/scan
|
||||
Beego->>Middleware : 调用中间件
|
||||
Middleware->>Middleware : Extract 上游 Trace Context
|
||||
Middleware->>Middleware : Start Span (路径, 方法)
|
||||
Middleware->>Controller : 调用 Scan 方法
|
||||
Controller-->>Middleware : 返回响应
|
||||
Middleware->>Middleware : 记录状态码、时长
|
||||
Middleware->>Middleware : End Span
|
||||
Middleware-->>Beego : 返回响应
|
||||
Beego-->>Client : HTTP 响应
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
- [internal/controllers/base_controller.go](file://internal/controllers/base_controller.go#L1-L9)
|
||||
- [internal/otelTrace/middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
|
||||
**Section sources**
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
- [internal/controllers/base_controller.go](file://internal/controllers/base_controller.go#L1-L9)
|
||||
- [internal/otelTrace/middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
|
||||
### OpenTelemetry 分布式追踪分析
|
||||
`internal/otelTrace` 包负责实现系统的分布式追踪、指标和日志功能。
|
||||
|
||||
#### 初始化与配置
|
||||
`InitTracer` 函数在 `main.go` 中被调用,负责初始化 OpenTelemetry 的 Tracer、Meter 和 Logger Provider。它创建了 OTLP 导出器,将数据通过 gRPC 发送到后端收集器(如 Jaeger 或 Prometheus)。配置中包含了超时、重试、批量发送和 Gzip 压缩等生产环境优化选项,以确保数据传输的可靠性和效率。
|
||||
|
||||
#### Span 创建与上下文传递
|
||||
如上文的序列图所示,`Middleware` 函数是创建 Span 的主要入口。它使用 `otel.Tracer("gateway-router").Start` 创建一个新的服务器 Span,并通过 `propagator.Extract` 从 HTTP 请求头中提取 `traceparent` 等标准头信息,将当前 Span 与上游调用关联起来,形成完整的调用链路。
|
||||
|
||||
#### 日志集成
|
||||
项目集成了 `zap` 日志库,并通过 `otelzap` 桥接器将日志与 OpenTelemetry 关联。`InitTracer` 函数中配置了 `LoggerProvider`,使得所有通过 `otelTrace.Logger` 记录的日志都会携带当前的 Span 上下文。这使得在追踪系统中可以将日志与特定的请求 Span 关联起来,极大地提升了问题排查的效率。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class InitTracer {
|
||||
+InitCtx context.Context
|
||||
+serviceName string
|
||||
+collectorURL string
|
||||
+InitTracer() (shutdown func)
|
||||
}
|
||||
class Middleware {
|
||||
+Middleware(ctx *beecontext.Context, next web.FilterFunc)
|
||||
}
|
||||
class CustomLogger {
|
||||
-logger *zap.Logger
|
||||
+WithContext(ctx context.Context) *CustomLogger
|
||||
+Error(msg string, fields ...Field)
|
||||
+Warn(msg string, fields ...Field)
|
||||
}
|
||||
InitTracer --> Middleware : 初始化
|
||||
InitTracer --> CustomLogger : 创建
|
||||
Middleware --> CustomLogger : 使用进行日志记录
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L1-L256)
|
||||
- [internal/otelTrace/middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
|
||||
**Section sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L1-L256)
|
||||
- [internal/otelTrace/middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
|
||||
### Redis 缓存与代理池分析
|
||||
Redis 在本项目中扮演着双重角色:通用缓存和代理池管理。
|
||||
|
||||
#### 缓存角色 (internal/cache)
|
||||
`internal/cache/redis.go` 文件封装了一个 `RedisClient` 结构体,提供了对 Redis 的高级操作。`Start` 函数在 `main.go` 中被调用,负责根据配置初始化 Redis 连接。该模块提供了 `Set`、`Get`、`Delete` 等基本操作,以及对 Redis List 和 Stream 数据结构的封装,如 `LPush`、`RPopUnmarshal`、`XAdd`、`XReadUnmarshal` 等,方便业务层使用。`sync.Once` 确保了连接的单例模式。
|
||||
|
||||
#### 代理池角色
|
||||
代理池的管理也依赖于 Redis。`proxy.InitProxyPool` 函数在 `main.go` 中被调用,负责初始化代理池。虽然具体实现未在上下文中,但可以推断它会从配置中读取代理列表,并将其存储在 Redis 中(可能使用 List 或 Hash 结构),并通过 `utils.StartProxyPool` 启动一个后台任务来维护和轮询这些代理。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[main.go] --> B[cache.Start()]
|
||||
A --> C[proxy.InitProxyPool()]
|
||||
B --> D[NewRedisClient()]
|
||||
D --> E[redis.NewClient()]
|
||||
E --> F[(Redis Server)]
|
||||
C --> G[读取代理配置]
|
||||
G --> H[存储代理到 Redis]
|
||||
H --> F
|
||||
F --> I[业务代码使用 Get/Set]
|
||||
F --> J[代理池使用 LPush/RPop]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go#L1-L294)
|
||||
|
||||
**Section sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go#L1-L294)
|
||||
|
||||
### MySQL 与 RabbitMQ 数据访问分析
|
||||
系统通过 Beego ORM 访问 MySQL 数据库,并通过消息队列与外部系统异步通信。
|
||||
|
||||
#### MySQL 与 Beego ORM
|
||||
`internal/models/init.go` 文件负责初始化数据库连接。它从 `app.conf` 配置文件中读取 MySQL 的连接信息(主机、用户名、密码、数据库名),并使用 `orm.RegisterDataBase` 注册一个名为 "default" 的数据库连接。`orm.RegisterModel` 函数注册了所有需要映射到数据库表的 Go 结构体(如 `MerchantInfo`, `OrderInfo` 等)。业务代码通过 `orm.NewOrm()` 创建 ORM 实例,然后使用 `QueryTable`、`Filter`、`All`、`One` 等方法进行数据查询和操作。
|
||||
|
||||
#### RabbitMQ 异步消息处理
|
||||
`main.go` 文件中的 `go notify.CreateOrderNotifyConsumer(otelTrace.InitCtx)` 和 `go query.CreateSupplierOrderQueryCuConsumer(otelTrace.InitCtx)` 启动了两个后台 goroutine,分别作为 RabbitMQ 的消费者。`CreateOrderNotifyConsumer` 负责消费订单通知消息,`CreateSupplierOrderQueryCuConsumer` 负责消费供应商订单查询任务。这种异步模式解耦了核心交易流程和后续的通知/查询逻辑,提高了系统的吞吐量和响应速度。
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
MERCHANT_INFO {
|
||||
string merchant_uid PK
|
||||
string login_account UK
|
||||
string status
|
||||
string merchant_name
|
||||
}
|
||||
ORDER_INFO {
|
||||
string order_no PK
|
||||
string merchant_uid FK
|
||||
float order_amount
|
||||
string status
|
||||
datetime create_time
|
||||
}
|
||||
PAYFOR_INFO {
|
||||
string payfor_no PK
|
||||
string merchant_uid FK
|
||||
float amount
|
||||
string status
|
||||
datetime create_time
|
||||
}
|
||||
MERCHANT_INFO ||--o{ ORDER_INFO : "拥有"
|
||||
MERCHANT_INFO ||--o{ PAYFOR_INFO : "拥有"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L1-L56)
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
|
||||
**Section sources**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L1-L56)
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
|
||||
## 依赖分析
|
||||
项目的主要依赖关系清晰地体现在 `main.go` 的初始化流程中。`main` 函数按顺序调用了 `config.GetMQAddress`、`proxy.InitProxyPool`、`otelTrace.InitTracer` 等函数,这些函数之间没有循环依赖,保证了启动的稳定性。`controllers` 依赖于 `service`,`service` 依赖于 `models` 和 `cache`,`models` 依赖于 Beego ORM,形成了一个清晰的依赖树。外部依赖包括 `github.com/beego/beego/v2`、`github.com/go-sql-driver/mysql`、`github.com/redis/go-redis/v9` 和 `go.opentelemetry.io/otel` 等。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
main[main.go] --> config[config]
|
||||
main --> proxy[proxy]
|
||||
main --> otelTrace[otelTrace]
|
||||
main --> routers[routers]
|
||||
main --> service[service]
|
||||
main --> models[models]
|
||||
main --> cache[cache]
|
||||
main --> utils[utils]
|
||||
routers --> controllers[controllers]
|
||||
controllers --> service
|
||||
service --> models
|
||||
service --> cache
|
||||
models --> "github.com/beego/beego/v2/client/orm"
|
||||
cache --> "github.com/redis/go-redis/v9"
|
||||
otelTrace --> "go.opentelemetry.io/otel"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
- [internal/routers/router.go](file://internal/routers/router.go#L1-L75)
|
||||
|
||||
**Section sources**
|
||||
- [main.go](file://main.go#L1-L58)
|
||||
|
||||
## 性能考量
|
||||
项目在多个层面进行了性能优化:
|
||||
1. **连接池**:Beego ORM 和 Redis 客户端内部都使用了连接池,避免了频繁创建和销毁连接的开销。
|
||||
2. **异步处理**:通过 RabbitMQ 消费者异步处理订单通知和查询,避免了阻塞主请求流程。
|
||||
3. **缓存**:Redis 被用作缓存,可以显著减少对数据库的直接访问。
|
||||
4. **批量操作**:OpenTelemetry 的导出器配置了批量发送(`WithBatchTimeout`, `WithMaxExportBatchSize`),减少了网络请求次数。
|
||||
5. **熔断机制**:`otelTrace` 中间件集成了熔断器,当追踪系统过载时,可以自动降级,保障核心业务不受影响。
|
||||
6. **资源复用**:Redis 连接使用 `sync.Once` 保证单例,避免了资源浪费。
|
||||
|
||||
## 故障排除指南
|
||||
* **Redis 连接失败**:检查 `app.conf` 中的 Redis 配置是否正确,确认 Redis 服务是否正常运行。查看日志中是否有 "redis 连接失败" 的错误信息。
|
||||
* **MySQL 连接失败**:检查 `app.conf` 中的 MySQL 配置(主机、端口、用户名、密码、数据库名),确认 MySQL 服务是否正常运行。查看日志中是否有 "failed to create ORM" 或类似错误。
|
||||
* **追踪数据未上报**:检查 `collectorURL` 配置是否正确,确认 OpenTelemetry Collector 服务是否正常运行。检查网络连通性。
|
||||
* **HTTP 500 错误**:查看日志中的 panic 信息或 "服务暂时不可用" 的错误,定位到具体的控制器或服务方法进行排查。
|
||||
* **慢请求告警**:在日志中搜索 "慢请求检测",分析具体是哪个接口或哪段代码导致了性能瓶颈。
|
||||
|
||||
**Section sources**
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go#L1-L294)
|
||||
- [internal/models/init.go](file://internal/models/init.go#L1-L56)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L1-L256)
|
||||
- [internal/otelTrace/middleware.go](file://internal/otelTrace/middleware.go#L1-L141)
|
||||
|
||||
## 结论
|
||||
`kami_gateway` 项目构建了一个技术栈选型合理、架构清晰的微服务网关。通过 Beego v2 框架提供了稳定高效的 Web 服务,OpenTelemetry 实现了全面的可观测性,Redis 和 MySQL 分别承担了缓存和持久化存储的角色,RabbitMQ 则保证了系统的异步解耦。各组件通过 `main.go` 文件被有序地集成和启动,形成了一个高可用、高性能、易于维护的系统。遵循文档中提到的配置最佳实践,可以确保系统在生产环境中的稳定运行。
|
||||
237
.qoder/repowiki/zh/content/技术栈与依赖/数据库与缓存系统/MySQL数据库集成.md
Normal file
237
.qoder/repowiki/zh/content/技术栈与依赖/数据库与缓存系统/MySQL数据库集成.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# MySQL数据库集成
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [internal/models/init.go](file://internal/models/init.go)
|
||||
- [conf/app.conf](file://conf/app.conf)
|
||||
- [internal/models/order/order_info.go](file://internal/models/order/order_info.go)
|
||||
- [internal/models/merchant/merchant_info.go](file://internal/models/merchant/merchant_info.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [数据库初始化流程](#数据库初始化流程)
|
||||
2. [配置文件解析](#配置文件解析)
|
||||
3. [ORM模型注册机制](#orm模型注册机制)
|
||||
4. [数据库连接池管理](#数据库连接池管理)
|
||||
5. [实体模型结构](#实体模型结构)
|
||||
6. [数据操作实践](#数据操作实践)
|
||||
7. [性能优化建议](#性能优化建议)
|
||||
|
||||
## 数据库初始化流程
|
||||
|
||||
系统在启动时通过`internal/models/init.go`文件中的`init`函数完成MySQL数据库的初始化配置。该函数在程序启动时自动执行,负责从配置文件读取数据库连接参数,构造连接字符串,并完成Beego ORM的注册与配置。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([程序启动]) --> ReadConfig["读取app.conf配置"]
|
||||
ReadConfig --> ExtractParams["提取数据库参数<br/>dbhost, dbuser, dbpasswd, dbbase, dbport"]
|
||||
ExtractParams --> BuildDSN["构造数据库连接字符串"]
|
||||
BuildDSN --> RegisterDriver["注册MySQL驱动"]
|
||||
RegisterDriver --> SetupDB["配置默认数据库连接"]
|
||||
SetupDB --> SetDebug["设置调试模式"]
|
||||
SetDebug --> RegisterModels["注册所有ORM模型"]
|
||||
RegisterModels --> End([数据库初始化完成])
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L22-L56)
|
||||
|
||||
**Section sources**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L1-L56)
|
||||
|
||||
## 配置文件解析
|
||||
|
||||
数据库连接参数从`conf/app.conf`配置文件中读取,采用分段式配置结构。系统通过Beego框架提供的`web.AppConfig`接口获取配置值,确保了配置管理的统一性和灵活性。
|
||||
|
||||
```ini
|
||||
[mysql]
|
||||
dbhost = 127.0.0.1
|
||||
dbport = 3306
|
||||
dbuser = root
|
||||
dbpasswd = Woaizixkie!123
|
||||
dbbase = kami
|
||||
debug = true
|
||||
```
|
||||
|
||||
配置项说明:
|
||||
- **dbhost**: 数据库服务器地址
|
||||
- **dbport**: 数据库服务端口
|
||||
- **dbuser**: 数据库用户名
|
||||
- **dbpasswd**: 数据库密码
|
||||
- **dbbase**: 数据库名称
|
||||
- **debug**: 调试模式开关
|
||||
|
||||
系统使用`web.AppConfig.String()`方法读取字符串类型配置,使用`web.AppConfig.DefaultBool()`方法读取布尔类型配置并提供默认值。
|
||||
|
||||
**Section sources**
|
||||
- [conf/app.conf](file://conf/app.conf#L40-L47)
|
||||
- [internal/models/init.go](file://internal/models/init.go#L22-L30)
|
||||
|
||||
## ORM模型注册机制
|
||||
|
||||
Beego ORM通过`RegisterModel`函数注册所有需要映射到数据库表的实体模型。系统采用集中式注册方式,在`init`函数中一次性注册所有模型,确保了模型管理的统一性。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class UserInfo {
|
||||
+string LoginAccount
|
||||
+string LoginPassword
|
||||
+string MerchantUid
|
||||
+string MerchantKey
|
||||
+string MerchantSecret
|
||||
}
|
||||
class OrderInfo {
|
||||
+string BankOrderId
|
||||
+string MerchantOrderId
|
||||
+float64 OrderAmount
|
||||
+string Status
|
||||
+string NotifyUrl
|
||||
+*time.Time CreateTime
|
||||
+*time.Time PayTime
|
||||
}
|
||||
class MerchantInfo {
|
||||
+string MerchantName
|
||||
+string BelongAgentUid
|
||||
+string WhiteIps
|
||||
+float64 PayforFee
|
||||
+time.Time CreateTime
|
||||
+time.Time UpdateTime
|
||||
}
|
||||
class RoadInfo {
|
||||
+string RoadUid
|
||||
+string RoadName
|
||||
+string PayProductCode
|
||||
+string PayTypeCode
|
||||
}
|
||||
class AgentInfo {
|
||||
+string AgentUid
|
||||
+string AgentName
|
||||
+string AgentLevel
|
||||
}
|
||||
class NotifyInfo {
|
||||
+string NotifyId
|
||||
+string OrderId
|
||||
+string NotifyUrl
|
||||
+int NotifyCount
|
||||
+*time.Time LastNotifyTime
|
||||
}
|
||||
class TaskOrderFake {
|
||||
+int Id
|
||||
+string TaskName
|
||||
+int Status
|
||||
+*time.Time ExecuteTime
|
||||
}
|
||||
OrderInfo --> MerchantInfo : "belongs to"
|
||||
OrderInfo --> RoadInfo : "uses"
|
||||
OrderInfo --> AgentInfo : "processed by"
|
||||
NotifyInfo --> OrderInfo : "notifies"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L48-L55)
|
||||
- [internal/models/order/order_info.go](file://internal/models/order/order_info.go#L19-L68)
|
||||
- [internal/models/merchant/merchant_info.go](file://internal/models/merchant/merchant_info.go#L12-L32)
|
||||
|
||||
**Section sources**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L48-L55)
|
||||
|
||||
## 数据库连接池管理
|
||||
|
||||
系统通过Beego ORM的`RegisterDataBase`函数建立数据库连接池,使用标准的MySQL连接字符串格式。连接池配置支持自动连接恢复和连接复用,提高了数据库访问效率。
|
||||
|
||||
连接字符串构造逻辑:
|
||||
```
|
||||
用户名:密码@tcp(主机:端口)/数据库名?charset=utf8&loc=Local&parseTime=true
|
||||
```
|
||||
|
||||
关键参数说明:
|
||||
- **charset=utf8**: 指定字符集为UTF-8,支持中文存储
|
||||
- **loc=Local**: 设置时区为本地时区
|
||||
- **parseTime=true**: 启用时间类型自动解析
|
||||
|
||||
系统还通过`orm.Debug`开关控制SQL日志输出,便于开发调试和性能分析。
|
||||
|
||||
**Section sources**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L32-L40)
|
||||
|
||||
## 实体模型结构
|
||||
|
||||
系统定义了多个实体模型来管理不同的业务数据,每个模型对应数据库中的一张表。模型采用Go语言的结构体定义,通过字段标签和Beego ORM约定实现数据库映射。
|
||||
|
||||
### 订单信息模型
|
||||
|
||||
`OrderInfo`结构体定义了订单相关的核心数据字段,包括订单标识、金额信息、状态跟踪和时间戳等。
|
||||
|
||||
**Section sources**
|
||||
- [internal/models/order/order_info.go](file://internal/models/order/order_info.go#L19-L68)
|
||||
|
||||
### 商户信息模型
|
||||
|
||||
`MerchantInfo`结构体管理商户的基本信息、认证凭证和业务配置,是系统权限控制和业务处理的基础。
|
||||
|
||||
**Section sources**
|
||||
- [internal/models/merchant/merchant_info.go](file://internal/models/merchant/merchant_info.go#L12-L32)
|
||||
|
||||
## 数据操作实践
|
||||
|
||||
系统通过Beego ORM提供的API实现数据的增删改查操作,遵循统一的编程模式,提高了代码的可维护性。
|
||||
|
||||
### 数据查询模式
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Service as "业务服务"
|
||||
participant ORM as "Beego ORM"
|
||||
participant DB as "MySQL数据库"
|
||||
Service->>ORM : QueryTable(表名)
|
||||
ORM->>ORM : Filter(条件)
|
||||
ORM->>ORM : Limit(分页)
|
||||
ORM->>ORM : OrderBy(排序)
|
||||
ORM->>DB : 执行SQL查询
|
||||
DB-->>ORM : 返回结果集
|
||||
ORM-->>Service : 映射为结构体
|
||||
Service-->>调用方 : 返回业务数据
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/models/order/order_info.go](file://internal/models/order/order_info.go#L100-L120)
|
||||
- [internal/models/merchant/merchant_info.go](file://internal/models/merchant/merchant_info.go#L90-L110)
|
||||
|
||||
### 数据插入模式
|
||||
|
||||
系统通过`Insert`或`InsertWithCtx`方法实现数据插入,支持上下文传递以实现请求链路追踪。
|
||||
|
||||
**Section sources**
|
||||
- [internal/models/order/order_info.go](file://internal/models/order/order_info.go#L150-L160)
|
||||
- [internal/models/merchant/merchant_info.go](file://internal/models/merchant/merchant_info.go#L170-L180)
|
||||
|
||||
### 数据更新模式
|
||||
|
||||
采用`Update`或`UpdateWithCtx`方法进行数据更新,支持批量更新和条件更新,确保了数据一致性。
|
||||
|
||||
**Section sources**
|
||||
- [internal/models/order/order_info.go](file://internal/models/order/order_info.go#L130-L140)
|
||||
- [internal/models/merchant/merchant_info.go](file://internal/models/merchant/merchant_info.go#L200-L210)
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
### 索引设计策略
|
||||
|
||||
根据业务查询模式,在高频查询字段上创建索引:
|
||||
- 订单表:`bank_order_id`, `merchant_order_id`, `create_time`, `status`
|
||||
- 商户表:`merchant_uid`, `login_account`, `belong_agent_uid`
|
||||
- 通道表:`road_uid`, `pay_product_code`
|
||||
|
||||
### 连接池调优
|
||||
|
||||
建议根据实际并发量调整连接池参数:
|
||||
- 设置合理的最大连接数(MaxOpenConns)
|
||||
- 配置适当的空闲连接数(MaxIdleConns)
|
||||
- 设置连接生命周期(ConnMaxLifetime)
|
||||
|
||||
### 查询优化
|
||||
|
||||
- 使用`Limit`避免全表扫描
|
||||
- 合理使用`Filter`条件缩小查询范围
|
||||
- 对复杂查询考虑创建数据库视图
|
||||
- 定期分析慢查询日志并优化SQL语句
|
||||
165
.qoder/repowiki/zh/content/技术栈与依赖/数据库与缓存系统/Redis缓存系统.md
Normal file
165
.qoder/repowiki/zh/content/技术栈与依赖/数据库与缓存系统/Redis缓存系统.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# Redis缓存系统
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [redis.go](file://internal/cache/redis.go)
|
||||
- [cfg_model.go](file://internal/config/cfg_model.go)
|
||||
- [proxy_pool.go](file://internal/utils/proxy_pool.go)
|
||||
- [dm_proxy_strategy.go](file://internal/utils/dm_proxy_strategy.go)
|
||||
- [app.conf](file://conf/app.conf)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [Redis双重角色概述](#redis双重角色概述)
|
||||
2. [单例初始化流程](#单例初始化流程)
|
||||
3. [分布式缓存应用](#分布式缓存应用)
|
||||
4. [代理池状态存储](#代理池状态存储)
|
||||
5. [键值设计与序列化](#键值设计与序列化)
|
||||
6. [连接池与性能配置](#连接池与性能配置)
|
||||
7. [缓存防护机制](#缓存防护机制)
|
||||
8. [监控与运维](#监控与运维)
|
||||
|
||||
## Redis双重角色概述
|
||||
|
||||
Redis在系统中扮演着双重关键角色:作为分布式缓存层处理高频数据访问,以及作为代理池的状态存储中心。这种双重架构设计实现了数据访问加速与代理资源管理的统一。
|
||||
|
||||
作为分布式缓存层,Redis被广泛应用于订单状态缓存、商户配置缓存等高频读取场景,有效降低了数据库的访问压力。同时,Redis作为代理池的状态存储,通过`internal/utils/dm_proxy_strategy.go`中的`DMProxyStrategy`结构体,将代理IP、过期时间、最后使用时间等关键信息持久化存储在Redis中,实现了代理资源的集中管理和状态追踪。
|
||||
|
||||
**Section sources**
|
||||
- [dm_proxy_strategy.go](file://internal/utils/dm_proxy_strategy.go#L38-L43)
|
||||
- [proxy_pool.go](file://internal/utils/proxy_pool.go#L537-L553)
|
||||
|
||||
## 单例初始化流程
|
||||
|
||||
Redis客户端的初始化通过`internal/cache/redis.go`中的`Start()`函数实现,采用`sync.OnceFunc`确保线程安全的单例模式。该流程严格遵循一次性初始化原则,防止并发环境下重复创建连接实例。
|
||||
|
||||
初始化过程首先从配置中心获取Redis连接参数,包括主机地址、密码和数据库编号。这些参数通过`config.GetConfig().GetRedisConfig()`方法从`conf/app.conf`文件中读取,其中默认配置为主机`127.0.0.1:6379`,数据库`db=0`,密码为空字符串。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant main as main.go
|
||||
participant cache as redis.go
|
||||
participant config as cfg_model.go
|
||||
main->>cache : Start()
|
||||
cache->>config : GetConfig()
|
||||
config->>config : GetRedisConfig()
|
||||
config-->>cache : RedisConfig{Host,Password,DB}
|
||||
cache->>cache : NewRedisClient(Host,Password,DB)
|
||||
cache->>cache : Ping()测试连接
|
||||
cache-->>main : 初始化完成
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [redis.go](file://internal/cache/redis.go#L13-L25)
|
||||
- [cfg_model.go](file://internal/config/cfg_model.go#L127-L133)
|
||||
- [app.conf](file://conf/app.conf#L60-L63)
|
||||
|
||||
**Section sources**
|
||||
- [redis.go](file://internal/cache/redis.go#L13-L30)
|
||||
- [cfg_model.go](file://internal/config/cfg_model.go#L127-L133)
|
||||
|
||||
## 分布式缓存应用
|
||||
|
||||
Redis作为分布式缓存层,在系统中支撑着多个核心业务场景的数据访问。通过`GetRedisClient()`函数提供的全局访问点,各业务模块可以高效地进行缓存操作。
|
||||
|
||||
在订单管理场景中,系统利用Redis的List数据结构实现订单队列管理,通过`LPush`和`RPopUnmarshal`等方法进行订单的入队和出队操作。对于商户配置信息,采用Hash结构进行存储,实现配置项的快速读取和更新。高频访问的静态数据如支付通道信息,通过`Set`和`Get`方法进行缓存,显著提升了接口响应速度。
|
||||
|
||||
缓存策略采用合理的过期时间设置,避免数据长期驻留导致内存浪费。同时,通过`Exists`方法实现缓存预热和缓存击穿的预防,确保系统在高并发场景下的稳定性。
|
||||
|
||||
**Section sources**
|
||||
- [redis.go](file://internal/cache/redis.go#L70-L100)
|
||||
- [redis.go](file://internal/cache/redis.go#L134-L183)
|
||||
|
||||
## 代理池状态存储
|
||||
|
||||
Redis在代理池管理中发挥着核心作用,通过与`internal/utils/proxy_pool.go`和`internal/utils/dm_proxy_strategy.go`的深度集成,实现了代理资源的智能化管理。
|
||||
|
||||
`DMProxyStrategy`结构体直接持有`*redis.Client`引用,将Redis作为代理状态的唯一事实来源。代理IP、过期时间、最后使用时间等信息以`DMProxyInfo`结构体形式序列化后存储在Redis中,键名为`proxy:{ip}`。这种设计实现了代理状态的持久化,即使服务重启也能恢复代理池状态。
|
||||
|
||||
代理池的清理机制通过定时任务实现,`startCleanupRoutine`协程每分钟执行一次`cleanupExpiredProxies`,清除过期的代理记录。同时,`ensureProxyPool`方法确保代理池中始终有足够的可用代理,当数量低于阈值时自动从代理服务商拉取新代理。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class DMProxyStrategy {
|
||||
+string proxyURL
|
||||
+*redis.Client redisClient
|
||||
+*RateLimiter rateLimiter
|
||||
+chan struct{} stopCh
|
||||
+GetProxy(ctx, request) string
|
||||
+GetHTTPClient(ctx, request) *http.Client
|
||||
+getRandomProxyFromRedis(ctx) string
|
||||
+getNewProxy(ctx) []string
|
||||
+checkProxyAvailable(ctx, proxy) error
|
||||
+startCleanupRoutine() void
|
||||
+ensureProxyPool(ctx) void
|
||||
+cleanupExpiredProxies() void
|
||||
+Stop() void
|
||||
}
|
||||
class DMProxyInfo {
|
||||
+string Proxy
|
||||
+time.Time ExpireAt
|
||||
+time.Time LastUsedAt
|
||||
}
|
||||
DMProxyStrategy --> DMProxyInfo : "序列化存储"
|
||||
DMProxyStrategy --> redis.Client : "状态存储"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [dm_proxy_strategy.go](file://internal/utils/dm_proxy_strategy.go#L38-L43)
|
||||
- [dm_proxy_strategy.go](file://internal/utils/dm_proxy_strategy.go#L11-L18)
|
||||
|
||||
**Section sources**
|
||||
- [dm_proxy_strategy.go](file://internal/utils/dm_proxy_strategy.go#L35-L377)
|
||||
- [proxy_pool.go](file://internal/utils/proxy_pool.go#L537-L553)
|
||||
|
||||
## 键值设计与序列化
|
||||
|
||||
系统采用规范化的键值设计原则,确保Redis数据的可维护性和可追溯性。键名遵循`domain:subdomain:identifier`的命名规范,如订单相关的键以`order:`为前缀,代理相关的键以`proxy:`为前缀。
|
||||
|
||||
序列化策略根据数据类型和使用场景进行优化。对于结构体数据,采用JSON序列化,通过`json.Marshal`和`json.Unmarshal`实现对象的持久化。对于简单类型数据,直接使用Redis原生的字符串存储。在List和Stream操作中,系统自动将复杂对象序列化为JSON字符串后存储,确保数据的完整性和可读性。
|
||||
|
||||
特殊数据类型如时间戳采用Unix时间戳存储,避免时区问题。布尔值通过`"true"`和`"false"`字符串表示,确保跨语言兼容性。这种统一的序列化策略简化了数据访问逻辑,降低了维护成本。
|
||||
|
||||
**Section sources**
|
||||
- [redis.go](file://internal/cache/redis.go#L134-L183)
|
||||
- [redis.go](file://internal/cache/redis.go#L224-L275)
|
||||
|
||||
## 连接池与性能配置
|
||||
|
||||
Redis客户端配置了合理的连接池参数,以平衡性能和资源消耗。虽然当前实现中未显式设置连接池大小,但依赖于`github.com/redis/go-redis/v9`库的默认连接池行为。
|
||||
|
||||
连接超时、读取超时和写入超时均设置为合理的默认值,确保在网络异常时能够快速失败,避免请求堆积。通过`Ping()`方法在初始化时进行连接测试,确保Redis服务的可用性。
|
||||
|
||||
在高并发场景下,系统利用Redis管道(Pipeline)和事务管道(TxPipeline)功能,通过`Pipeline()`和`TxPipeline()`方法减少网络往返次数,提升批量操作的性能。发布/订阅模式通过`Publish()`和`Subscribe()`方法实现,支持实时消息通知。
|
||||
|
||||
**Section sources**
|
||||
- [redis.go](file://internal/cache/redis.go#L35-L50)
|
||||
- [redis.go](file://internal/cache/redis.go#L290-L300)
|
||||
|
||||
## 缓存防护机制
|
||||
|
||||
系统实现了多层次的缓存防护机制,有效应对缓存穿透、缓存击穿和缓存雪崩等常见问题。
|
||||
|
||||
对于缓存穿透,系统在数据访问层进行参数校验,拒绝非法请求。同时,对于查询结果为空的情况,设置短时间的空值缓存,避免相同请求频繁穿透到数据库。通过`Exists`方法预先检查键的存在性,减少不必要的查询操作。
|
||||
|
||||
缓存击穿通过合理的过期时间设置和互斥锁机制防范。虽然当前代码中未显式实现分布式锁,但`sync.OnceFunc`的单例模式为关键初始化操作提供了线程安全保证。在热点数据更新时,采用先更新缓存后更新数据库的策略,确保数据一致性。
|
||||
|
||||
监控方面,系统通过`GetSize()`和`GetSizeByPrefix()`方法定期采集Redis内存使用情况,为容量规划提供数据支持。错误日志通过`otelTrace.Logger`记录,便于问题排查和性能分析。
|
||||
|
||||
**Section sources**
|
||||
- [redis.go](file://internal/cache/redis.go#L102-L115)
|
||||
- [redis.go](file://internal/cache/redis.go#L117-L125)
|
||||
|
||||
## 监控与运维
|
||||
|
||||
系统集成了全面的监控和运维功能,确保Redis服务的稳定运行。通过OpenTelemetry框架实现分布式追踪,每个Redis操作都被记录为独立的Span,便于性能分析和问题定位。
|
||||
|
||||
健康检查通过`Ping()`方法实现,定期验证Redis连接的可用性。在`Start()`函数中,连接失败时会记录详细的错误日志,包括错误类型和堆栈信息,为故障排查提供依据。
|
||||
|
||||
运维方面,系统提供了`Delete()`方法用于手动清理缓存,`Expire()`方法用于动态调整键的过期时间。通过`Keys()`方法配合前缀查询,可以统计特定类型数据的数量,为容量规划和性能优化提供数据支持。
|
||||
|
||||
定期的清理任务通过协程实现,如代理池的`startCleanupRoutine`,确保过期数据能够及时清理,避免内存泄漏。这种主动式运维策略提高了系统的自愈能力,降低了人工干预的需求。
|
||||
|
||||
**Section sources**
|
||||
- [redis.go](file://internal/cache/redis.go#L13-L30)
|
||||
- [dm_proxy_strategy.go](file://internal/utils/dm_proxy_strategy.go#L251-L263)
|
||||
325
.qoder/repowiki/zh/content/技术栈与依赖/数据库与缓存系统/数据库与缓存系统.md
Normal file
325
.qoder/repowiki/zh/content/技术栈与依赖/数据库与缓存系统/数据库与缓存系统.md
Normal file
@@ -0,0 +1,325 @@
|
||||
# 数据库与缓存系统
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [main.go](file://main.go)
|
||||
- [conf/app.conf](file://conf/app.conf)
|
||||
- [internal/models/init.go](file://internal/models/init.go)
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go)
|
||||
- [internal/config/cfg_model.go](file://internal/config/cfg_model.go)
|
||||
- [internal/utils/proxy_pool.go](file://internal/utils/proxy_pool.go)
|
||||
- [internal/proxy/proxy_pool.go](file://internal/proxy/proxy_pool.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [项目结构](#项目结构)
|
||||
2. [MySQL 数据库集成与 ORM 配置](#mysql-数据库集成与-orm-配置)
|
||||
3. [Beego ORM 模型定义与自动迁移](#beego-orm-模型定义与自动迁移)
|
||||
4. [事务处理模式](#事务处理模式)
|
||||
5. [Redis 缓存系统架构](#redis-缓存系统架构)
|
||||
6. [Redis 初始化与连接池管理](#redis-初始化与连接池管理)
|
||||
7. [分布式缓存层设计](#分布式缓存层设计)
|
||||
8. [代理池状态存储机制](#代理池状态存储机制)
|
||||
9. [键值设计规范](#键值设计规范)
|
||||
10. [数据库连接管理](#数据库连接管理)
|
||||
11. [缓存穿透与击穿应对策略](#缓存穿透与击穿应对策略)
|
||||
12. [性能监控指标](#性能监控指标)
|
||||
|
||||
## 项目结构
|
||||
|
||||
本项目采用分层架构设计,核心数据持久化与缓存组件集中于 `internal` 目录下。`models` 目录存放所有数据库实体模型,`cache` 目录封装 Redis 客户端,`proxy` 和 `utils` 目录分别实现代理池的两种状态存储方案。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "核心模块"
|
||||
Models[models/]
|
||||
Cache[cache/]
|
||||
Proxy[proxy/]
|
||||
Utils[utils/]
|
||||
end
|
||||
subgraph "配置"
|
||||
Conf[conf/app.conf]
|
||||
end
|
||||
subgraph "ORM与驱动"
|
||||
BeegoORM[github.com/beego/beego/v2/client/orm]
|
||||
MySQLDriver[github.com/go-sql-driver/mysql]
|
||||
end
|
||||
subgraph "Redis客户端"
|
||||
GoRedis[github.com/redis/go-redis/v9]
|
||||
end
|
||||
Conf --> Models
|
||||
Conf --> Cache
|
||||
MySQLDriver --> BeegoORM
|
||||
BeegoORM --> Models
|
||||
GoRedis --> Cache
|
||||
Cache --> Proxy
|
||||
Cache --> Utils
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [conf/app.conf](file://conf/app.conf)
|
||||
- [internal/models/init.go](file://internal/models/init.go)
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go)
|
||||
|
||||
## MySQL 数据库集成与 ORM 配置
|
||||
|
||||
系统通过 Beego ORM 框架集成 MySQL 数据库,使用 `github.com/go-sql-driver/mysql` 作为底层驱动。数据库连接信息从 `conf/app.conf` 文件中读取,并在程序启动时完成初始化。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Main as main.go
|
||||
participant Models as models/init.go
|
||||
participant Config as config
|
||||
participant ORM as Beego ORM
|
||||
participant MySQL as MySQL Driver
|
||||
Main->>Models : init()
|
||||
Models->>Config : 读取 mysql : : dbhost, dbuser, dbpasswd, dbbase, dbport
|
||||
Config-->>Models : 返回配置值
|
||||
Models->>Models : 构建 DSN 连接字符串
|
||||
Models->>ORM : RegisterDriver("mysql", orm.DRMySQL)
|
||||
Models->>ORM : RegisterDataBase("default", "mysql", DSN)
|
||||
Models->>ORM : 设置 orm.Debug
|
||||
Models->>ORM : RegisterModel(所有实体)
|
||||
ORM->>MySQL : 建立连接
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L21-L55)
|
||||
- [conf/app.conf](file://conf/app.conf#L15-L21)
|
||||
|
||||
**本节源码**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L21-L55)
|
||||
- [conf/app.conf](file://conf/app.conf#L15-L21)
|
||||
|
||||
## Beego ORM 模型定义与自动迁移
|
||||
|
||||
`models` 目录下的各子包(如 `accounts`, `merchant`, `order` 等)定义了具体的数据库实体。每个实体结构体通过 `orm.RegisterModel()` 在 `init()` 函数中注册,Beego ORM 会根据结构体字段自动生成对应的数据库表。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class UserInfo {
|
||||
+int Id
|
||||
+string Username
|
||||
+string Email
|
||||
+string Password
|
||||
+datetime CreatedAt
|
||||
+IsActive() bool
|
||||
}
|
||||
class MerchantInfo {
|
||||
+int Id
|
||||
+string MerchantId
|
||||
+string MerchantKey
|
||||
+string Status
|
||||
+float64 Balance
|
||||
}
|
||||
class OrderInfo {
|
||||
+int Id
|
||||
+string MerchantOrderId
|
||||
+string BankOrderId
|
||||
+float64 OrderAmount
|
||||
+string Status
|
||||
+string RoadUid
|
||||
}
|
||||
class AgentInfo {
|
||||
+int Id
|
||||
+string AgentId
|
||||
+string Level
|
||||
+float64 CommissionRate
|
||||
}
|
||||
class RoadInfo {
|
||||
+int Id
|
||||
+string RoadUid
|
||||
+string RoadName
|
||||
+string Status
|
||||
+int Concurrency
|
||||
}
|
||||
class PayforInfo {
|
||||
+int Id
|
||||
+string PayforId
|
||||
+string BankCardId
|
||||
+float64 Amount
|
||||
+string Status
|
||||
}
|
||||
UserInfo <|-- MerchantInfo : "继承"
|
||||
MerchantInfo --> OrderInfo : "拥有"
|
||||
MerchantInfo --> AgentInfo : "代理"
|
||||
OrderInfo --> RoadInfo : "使用通道"
|
||||
PayforInfo --> BankCardInfo : "关联银行卡"
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [internal/models/user/user_info.go](file://internal/models/user/user_info.go)
|
||||
- [internal/models/merchant/merchant_info.go](file://internal/models/merchant/merchant_info.go)
|
||||
- [internal/models/order/order_info.go](file://internal/models/order/order_info.go)
|
||||
- [internal/models/agent/agent_info.go](file://internal/models/agent/agent_info.go)
|
||||
- [internal/models/road/road_info.go](file://internal/models/road/road_info.go)
|
||||
- [internal/models/payfor/payfor_info.go](file://internal/models/payfor/payfor_info.go)
|
||||
|
||||
## 事务处理模式
|
||||
|
||||
系统通过 Beego ORM 的 `orm.NewOrm()` 方法获取事务对象,支持显式事务控制。在需要保证数据一致性的业务场景(如订单创建、资金变动)中,使用 `Begin()`、`Commit()` 和 `Rollback()` 方法管理事务。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始事务]) --> CreateOrm["orm.NewOrm()"]
|
||||
CreateOrm --> Begin["Begin()"]
|
||||
Begin --> ExecuteSQL["执行多条SQL语句"]
|
||||
ExecuteSQL --> Success{"操作成功?"}
|
||||
Success --> |是| Commit["Commit()"]
|
||||
Success --> |否| Rollback["Rollback()"]
|
||||
Commit --> End([事务提交])
|
||||
Rollback --> End
|
||||
```
|
||||
|
||||
**本节源码**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L21-L55)
|
||||
|
||||
## Redis 缓存系统架构
|
||||
|
||||
Redis 在系统中扮演双重角色:作为 `internal/cache` 的分布式缓存层,加速高频数据读取;作为 `internal/utils/proxy_pool.go` 中代理池的状态存储,管理代理IP的生命周期。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Redis 双重角色"
|
||||
subgraph "分布式缓存层"
|
||||
CacheLayer[internal/cache]
|
||||
CacheLayer --> RedisClient[RedisClient]
|
||||
RedisClient --> RedisServer[(Redis)]
|
||||
end
|
||||
subgraph "代理池状态存储"
|
||||
ProxyPool[internal/utils/proxy_pool.go]
|
||||
ProxyPool --> RedisClient
|
||||
ProxyPool --> OrderBasedProxyStrategy[OrderBasedProxyStrategy]
|
||||
OrderBasedProxyStrategy --> ProxiesMap["proxies map[string]*ProxyInfo"]
|
||||
end
|
||||
end
|
||||
CacheLayer --> |高频数据读取| RedisServer
|
||||
ProxyPool --> |状态存储| RedisServer
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go)
|
||||
- [internal/utils/proxy_pool.go](file://internal/utils/proxy_pool.go)
|
||||
|
||||
## Redis 初始化与连接池管理
|
||||
|
||||
Redis 客户端通过 `cache.Start()` 函数进行单例初始化。该函数使用 `sync.OnceFunc` 确保全局唯一实例,从配置中读取连接参数,并建立与 Redis 服务器的连接。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App as 应用启动
|
||||
participant Cache as cache.Start()
|
||||
participant Config as config.GetConfig()
|
||||
participant Redis as RedisClient
|
||||
App->>Cache : Start()
|
||||
Cache->>Config : GetRedisConfig()
|
||||
Config-->>Cache : Host, Password, DB
|
||||
Cache->>Redis : NewRedisClient(Host, Password, DB)
|
||||
Redis->>Redis : redis.NewClient(&Options)
|
||||
Redis->>Redis : Ping() 测试连接
|
||||
alt 连接成功
|
||||
Redis-->>Cache : 返回 *RedisClient 实例
|
||||
Cache->>Cache : redisClientInstance = 实例
|
||||
else 连接失败
|
||||
Cache->>Logger : 记录错误 "redis 连接失败"
|
||||
end
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go#L18-L28)
|
||||
- [internal/config/cfg_model.go](file://internal/config/cfg_model.go#L128-L138)
|
||||
|
||||
**本节源码**
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go#L18-L28)
|
||||
- [internal/config/cfg_model.go](file://internal/config/cfg_model.go#L128-L138)
|
||||
|
||||
## 分布式缓存层设计
|
||||
|
||||
`internal/cache` 包提供了一个封装的 `RedisClient` 结构体,对 `github.com/redis/go-redis/v9` 客户端进行了二次封装,提供了更便捷的 API。该层支持字符串、列表、Stream 等多种数据结构的操作,并内置了序列化/反序列化功能。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class RedisClient {
|
||||
-Client *redis.Client
|
||||
+Set(ctx, key, value, expiration) error
|
||||
+Get(ctx, key, value) error
|
||||
+Delete(ctx, key) error
|
||||
+Exists(ctx, key) (bool, error)
|
||||
+LPush(ctx, key, values...) error
|
||||
+RPopUnmarshal(ctx, key, value) error
|
||||
+XAdd(ctx, key, values) (string, error)
|
||||
+XReadUnmarshal(ctx, key, start, count, block) ([]map[string]interface{}, error)
|
||||
+Close() error
|
||||
}
|
||||
class RedisClient 的使用
|
||||
RedisClient 的使用 --> OrderPoolServiceImpl : "匹配订单"
|
||||
RedisClient 的使用 --> NuclearImpl : "核弹卡密"
|
||||
RedisClient 的使用 --> ProxyPool : "代理状态"
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go#L35-L37)
|
||||
|
||||
## 代理池状态存储机制
|
||||
|
||||
`internal/utils/proxy_pool.go` 实现了一个基于订单号和通道的代理策略(`OrderBasedProxyStrategy`)。它使用内存中的 `map[string]*ProxyInfo` 来缓存代理IP,并通过 `StartProxyPool()` 函数启动。该策略支持跨通道复用代理IP,以满足 `OrderPerIP` 配置的需求。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([GetProxy]) --> CheckCache["检查缓存 proxies[\"channel_orderID\"]"]
|
||||
CheckCache --> |存在且有效| ReturnCached["返回缓存代理"]
|
||||
CheckCache --> |不存在或过期| GetNew["获取新代理"]
|
||||
GetNew --> CallAPI["调用代理服务API"]
|
||||
CallAPI --> |成功| StoreCache["存入缓存 proxies[\"channel_orderID\"]"]
|
||||
StoreCache --> ReturnNew["返回新代理"]
|
||||
CallAPI --> |失败| Retry["重试最多3次"]
|
||||
Retry --> ReturnError["返回错误"]
|
||||
ReturnCached --> End([返回代理])
|
||||
ReturnNew --> End
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [internal/utils/proxy_pool.go](file://internal/utils/proxy_pool.go#L100-L150)
|
||||
|
||||
**本节源码**
|
||||
- [internal/utils/proxy_pool.go](file://internal/utils/proxy_pool.go#L100-L150)
|
||||
|
||||
## 键值设计规范
|
||||
|
||||
系统遵循清晰的键值命名规范,以确保数据的可维护性和可读性。Redis 键通常采用 `domain:subdomain:identifier` 的格式。
|
||||
|
||||
| 键前缀 | 用途 | 示例 | TTL |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `nuclear_random_ids:` | 核弹卡密的随机ID与指纹映射 | `nuclear_random_ids:abc123` | 永久 (0) |
|
||||
| `channel_orderID` | 代理池缓存键 | `appleCard_order_123` | 50-55秒 |
|
||||
| `customer_order_pool:` | 用户订单池 | `customer_order_pool:road123:100.00` | 动态 |
|
||||
| `produce_order_pool:` | 供应订单池 | `produce_order_pool:road123:100.00` | 动态 |
|
||||
|
||||
**本节源码**
|
||||
- [internal/service/supplier/third_party/pool/card_sender/nuclear.go](file://internal/service/supplier/third_party/pool/card_sender/nuclear.go#L48)
|
||||
- [internal/utils/proxy_pool.go](file://internal/utils/proxy_pool.go#L100)
|
||||
|
||||
## 数据库连接管理
|
||||
|
||||
数据库连接由 Beego ORM 在 `init()` 函数中统一管理。通过 `RegisterDataBase()` 注册默认数据库连接,并使用全局的 `orm.Debug` 标志控制SQL日志输出。所有数据访问操作都通过 `orm.NewOrm()` 获取的 ORM 实例进行。
|
||||
|
||||
**本节源码**
|
||||
- [internal/models/init.go](file://internal/models/init.go#L21-L55)
|
||||
|
||||
## 缓存穿透与击穿应对策略
|
||||
|
||||
系统通过以下策略应对缓存问题:
|
||||
- **缓存穿透**:对于 `nuclear_random_ids` 这类键,即使Redis中不存在,也会生成一个临时ID和指纹作为兜底,避免大量请求直接打到数据库。
|
||||
- **缓存击穿**:`OrderBasedProxyStrategy` 使用 `sync.RWMutex` 对 `proxies` 映射进行读写保护,在获取新代理时加写锁,确保并发安全,防止同一时间大量请求穿透到代理服务API。
|
||||
|
||||
**本节源码**
|
||||
- [internal/service/supplier/third_party/pool/card_sender/nuclear.go](file://internal/service/supplier/third_party/pool/card_sender/nuclear.go#L81)
|
||||
- [internal/utils/proxy_pool.go](file://internal/utils/proxy_pool.go#L100)
|
||||
|
||||
## 性能监控指标
|
||||
|
||||
系统通过 `otelTrace` 模块集成 OpenTelemetry,对关键操作进行监控。日志中记录了代理获取、Redis操作、数据库查询等耗时,可用于分析性能瓶颈。例如,`GetProxy` 函数被标记为一个Span,可以追踪其执行时间。
|
||||
|
||||
**本节源码**
|
||||
- [internal/utils/proxy_pool.go](file://internal/utils/proxy_pool.go#L301)
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go)
|
||||
220
.qoder/repowiki/zh/content/技术栈与依赖/消息队列与异步处理.md
Normal file
220
.qoder/repowiki/zh/content/技术栈与依赖/消息队列与异步处理.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# 消息队列与异步处理
|
||||
|
||||
<cite>
|
||||
**本文档引用文件**
|
||||
- [order_notify.go](file://internal/service/notify/order_notify.go)
|
||||
- [supplier_query.go](file://internal/schema/query/supplier_query.go)
|
||||
- [notify_info.go](file://internal/models/notify/notify_info.go)
|
||||
- [mq_config.go](file://internal/config/mq_config.go)
|
||||
- [send_message.go](file://internal/service/message/send_message.go)
|
||||
- [manager.go](file://internal/service/supplier/third_party/queue/manager.go)
|
||||
- [handlers.go](file://internal/service/supplier/third_party/queue/handlers.go)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [引言](#引言)
|
||||
2. [核心组件与架构](#核心组件与架构)
|
||||
3. [消息消费者启动流程](#消息消费者启动流程)
|
||||
4. [消息监听与处理机制](#消息监听与处理机制)
|
||||
5. [队列管理器与任务处理器协作机制](#队列管理器与任务处理器协作机制)
|
||||
6. [工作协程池设计](#工作协程池设计)
|
||||
7. [连接管理与消息确认](#连接管理与消息确认)
|
||||
8. [重试策略与死信队列](#重试策略与死信队列)
|
||||
9. [消费性能调优](#消费性能调优)
|
||||
10. [事件解耦与业务流程](#事件解耦与业务流程)
|
||||
|
||||
## 引言
|
||||
本文档详细描述了 `kami_gateway` 系统中基于 RabbitMQ 的异步任务处理机制。系统通过 `github.com/go-stomp/stomp/v3` 客户端实现消息队列通信,采用消息队列解耦核心支付流程,提升系统响应性与可扩展性。重点分析订单通知、渠道查询等关键事件的异步处理流程,并深入探讨队列管理器、任务处理器与工作协程之间的协作机制。
|
||||
|
||||
## 核心组件与架构
|
||||
系统通过 ActiveMQ(兼容 STOMP 协议)作为消息中间件,结合 Redis 队列与 Go 协程池实现多层级异步任务处理。主要组件包括:
|
||||
- **消息生产者**:通过 `SendMessage` 发布订单通知与查询请求
|
||||
- **消息消费者**:`CreateOrderNotifyConsumer` 与 `CreateSupplierOrderQueryCuConsumer` 监听队列
|
||||
- **队列管理器**:`QueueManager` 统一管理 Redis 队列生命周期
|
||||
- **任务处理器**:`DefaultTaskHandler` 调度具体业务逻辑
|
||||
- **工作协程池**:`WorkerPool` 并发执行任务
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "消息队列层"
|
||||
MQ[(ActiveMQ)]
|
||||
RedisQueue[(Redis Queue)]
|
||||
end
|
||||
subgraph "处理层"
|
||||
Consumer[消息消费者]
|
||||
QueueManager[队列管理器]
|
||||
TaskHandler[任务处理器]
|
||||
WorkerPool[工作协程池]
|
||||
end
|
||||
subgraph "业务层"
|
||||
OrderNotify[订单通知服务]
|
||||
SupplierQuery[渠道查询服务]
|
||||
BusinessLogic[业务逻辑]
|
||||
end
|
||||
Consumer --> |监听| MQ
|
||||
Consumer --> |入队| RedisQueue
|
||||
QueueManager --> |管理| RedisQueue
|
||||
QueueManager --> |调度| TaskHandler
|
||||
TaskHandler --> |提交| WorkerPool
|
||||
WorkerPool --> |执行| BusinessLogic
|
||||
BusinessLogic --> |结果| OrderNotify
|
||||
BusinessLogic --> |结果| SupplierQuery
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [manager.go](file://internal/service/supplier/third_party/queue/manager.go)
|
||||
- [handlers.go](file://internal/service/supplier/third_party/queue/handlers.go)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go)
|
||||
|
||||
## 消息消费者启动流程
|
||||
系统在初始化阶段启动多个消息消费者,分别监听不同的主题队列。
|
||||
|
||||
### 订单通知消费者
|
||||
`CreateOrderNotifyConsumer` 函数负责启动订单回调消息的监听。首先获取 ActiveMQ 连接,订阅 `order_notify` 队列,采用 `stomp.AckClient` 模式手动确认消息。一旦接收到消息,立即通过 `sendNotifyPool.Go` 提交至异步协程池处理,并立即执行 `Ack` 确认,确保消息不丢失。
|
||||
|
||||
### 渠道查询消费者
|
||||
`CreateSupplierOrderQueryCuConsumer` 启动订单查询任务的消费者,订阅 `order_query` 队列。接收到消息后,解析 `bankOrderId`,创建包含定时器的 `OrderQueryTask`,并将其加入延迟查询队列,随后立即确认消息。
|
||||
|
||||
**章节来源**
|
||||
- [order_notify.go](file://internal/service/notify/order_notify.go#L185-L217)
|
||||
- [supplier_query.go](file://internal/schema/query/supplier_query.go#L89-L116)
|
||||
|
||||
## 消息监听与处理机制
|
||||
消费者采用 `for range` 或 `select` 循环持续监听消息通道。所有消息体为 `bankOrderId` 字符串,消费者不进行复杂解析,仅提取订单号后触发后续异步流程。
|
||||
|
||||
消息处理采用“快速确认 + 异步执行”模式:
|
||||
1. 消费者接收到消息
|
||||
2. 解析出 `bankOrderId`
|
||||
3. 提交任务至协程池或 Redis 队列
|
||||
4. 立即调用 `conn.Ack(v)` 确认消息
|
||||
5. 异步协程执行具体业务逻辑
|
||||
|
||||
该模式有效避免因业务处理耗时导致消息积压或重复消费。
|
||||
|
||||
**章节来源**
|
||||
- [order_notify.go](file://internal/service/notify/order_notify.go#L185-L217)
|
||||
- [supplier_query.go](file://internal/schema/query/supplier_query.go#L89-L116)
|
||||
|
||||
## 队列管理器与任务处理器协作机制
|
||||
系统通过 `QueueManager` 实现对 Redis 队列的统一管理,其核心结构包含 `redisClient`、`queues` 映射、`registry` 处理器注册表、`taskFactory` 任务工厂等。
|
||||
|
||||
### 队列创建与获取
|
||||
`GetOrCreateQueue` 方法根据队列名与通道 ID 生成唯一键,若队列不存在则创建新的 `RedisQueue` 实例,并注册到 `queues` 映射中。
|
||||
|
||||
### 任务注册与调度
|
||||
`HandlerRegistry` 接口定义处理器注册与获取机制。`DefaultHandlerRegistry` 使用 `taskType:channelID` 作为键存储 `HandlerFunc`。`DefaultTaskHandler` 通过注册表查找对应处理器并执行。
|
||||
|
||||
### 任务入队流程
|
||||
`EnqueueTask` 方法首先获取或创建队列,然后调用 `RedisQueue.Enqueue` 将任务序列化后通过 `RPush` 写入 Redis 列表。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Producer as "生产者"
|
||||
participant Manager as "QueueManager"
|
||||
participant Queue as "RedisQueue"
|
||||
participant Registry as "HandlerRegistry"
|
||||
participant Handler as "TaskHandler"
|
||||
Producer->>Manager : EnqueueTask(task)
|
||||
Manager->>Manager : GetOrCreateQueue()
|
||||
Manager->>Queue : Enqueue(task)
|
||||
Queue->>Queue : MarshalJSON()
|
||||
Queue->>Redis : RPUSH(queueKey, data)
|
||||
Note over Queue,Redis : 任务入队成功
|
||||
loop 每秒轮询
|
||||
Queue->>Redis : LPOP(queueKey)
|
||||
Redis-->>Queue : task data
|
||||
Queue->>Handler : HandleTask(task)
|
||||
Handler->>Registry : GetHandler(taskType, channel)
|
||||
Registry-->>Handler : HandlerFunc
|
||||
Handler->>Handler : 执行业务逻辑
|
||||
end
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [manager.go](file://internal/service/supplier/third_party/queue/manager.go#L104-L113)
|
||||
- [handlers.go](file://internal/service/supplier/third_party/queue/handlers.go#L70-L103)
|
||||
- [queue.go](file://internal/service/supplier/third_party/queue/queue.go#L286-L310)
|
||||
|
||||
## 工作协程池设计
|
||||
`WorkerPool` 是一个通用的协程池实现,用于并发执行任务。
|
||||
|
||||
### 核心结构
|
||||
`WorkerPool` 包含以下关键字段:
|
||||
- `workers`:协程数量
|
||||
- `taskChan`:任务通道
|
||||
- `results`:结果通道
|
||||
- `ctx/cancel`:上下文控制
|
||||
- `metrics`:性能指标
|
||||
|
||||
### 任务执行流程
|
||||
`Start` 方法启动指定数量的 `worker` 协程。每个 `worker` 持续监听 `taskChan`,一旦接收到任务即调用 `task.Execute(ctx)` 执行。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> Submit["Submit(task)"]
|
||||
Submit --> CheckCtx{"ctx.Done()?"}
|
||||
CheckCtx --> |是| LogError["记录 worker pool 已停止"]
|
||||
CheckCtx --> |否| SendToChan["taskChan <- task"]
|
||||
SendToChan --> End([任务提交成功])
|
||||
subgraph Worker协程
|
||||
WorkerStart([协程启动]) --> ListenTask["监听 taskChan"]
|
||||
ListenTask --> GetTask{"获取 task"}
|
||||
GetTask --> Execute["task.Execute(ctx)"]
|
||||
Execute --> CheckErr{"执行出错?"}
|
||||
CheckErr --> |是| LogExecErr["记录执行失败"]
|
||||
CheckErr --> |否| Continue
|
||||
Continue --> ListenTask
|
||||
end
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go#L65-L71)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go#L74-L89)
|
||||
|
||||
## 连接管理与消息确认
|
||||
系统通过 `message.GetActiveMQConn()` 获取全局唯一的 ActiveMQ 连接实例,确保连接复用与资源节约。
|
||||
|
||||
所有消费者均采用 `stomp.AckClient` 模式,即客户端手动确认。消费者在成功提交异步任务后立即调用 `conn.Ack(v)` 确认消息。若确认失败,系统会记录错误日志但不会重新入队,依赖上游重试机制保障可靠性。
|
||||
|
||||
该模式平衡了性能与可靠性,避免因处理失败导致消息堆积。
|
||||
|
||||
**章节来源**
|
||||
- [order_notify.go](file://internal/service/notify/order_notify.go#L185-L217)
|
||||
- [send_message.go](file://internal/service/message/send_message.go#L1-L25)
|
||||
|
||||
## 重试策略与死信队列
|
||||
当前系统未显式配置死信队列(DLX/DLQ),但通过多层队列机制实现软性重试:
|
||||
|
||||
1. **ActiveMQ 层**:消费者未确认时消息保持在队列中
|
||||
2. **Redis 队列层**:`processQueue` 每秒轮询,失败任务保留在 Redis 列表中
|
||||
3. **业务逻辑层**:部分服务(如核销任务)内部实现重试逻辑
|
||||
|
||||
对于订单通知,系统通过 `GetNotifyInfosNotSuccess` 查询未成功通知的记录,由定时任务重新触发,形成补偿机制。
|
||||
|
||||
## 消费性能调优
|
||||
系统通过以下方式优化消费性能:
|
||||
|
||||
- **并发消费**:每个消费者可启动多个工作协程
|
||||
- **批量处理**:Redis 队列支持批量拉取(未在代码中体现)
|
||||
- **连接复用**:全局 ActiveMQ 连接减少握手开销
|
||||
- **异步解耦**:消息确认与业务处理分离,提升吞吐量
|
||||
- **资源限制**:协程池限制最大并发数,防止资源耗尽
|
||||
|
||||
`QueueManager` 支持为不同队列配置不同工作协程数,实现精细化性能控制。
|
||||
|
||||
## 事件解耦与业务流程
|
||||
系统通过消息队列实现核心支付流程的解耦:
|
||||
|
||||
### 订单通知事件
|
||||
支付完成后,系统调用 `SendMessage` 向 `order_notify` 队列发送 `bankOrderId`。消费者异步调用 `SendOrderNotify` 执行回调,更新 `notify_info` 表状态。该机制避免因第三方回调超时阻塞主流程。
|
||||
|
||||
### 渠道查询事件
|
||||
对于需要轮询结果的渠道,系统将 `bankOrderId` 发送至 `order_query` 队列。消费者创建带定时器的查询任务,实现延迟查询与自动重试。
|
||||
|
||||
`notify_info.go` 定义了通知记录的数据结构与数据库操作,包括插入、查询、更新等方法,是异步通知持久化的基础。
|
||||
|
||||
**章节来源**
|
||||
- [notify_info.go](file://internal/models/notify/notify_info.go#L1-L76)
|
||||
- [mq_config.go](file://internal/config/mq_config.go#L1-L59)
|
||||
- [send_message.go](file://internal/service/message/send_message.go#L1-L25)
|
||||
251
.qoder/repowiki/zh/content/数据模型/商户模型.md
Normal file
251
.qoder/repowiki/zh/content/数据模型/商户模型.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# 商户模型
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go)
|
||||
- [merchant_load_info.go](file://internal/models/merchant/merchant_load_info.go)
|
||||
- [pay_resp.go](file://internal/schema/response/pay_resp.go)
|
||||
- [merchant_hidden_config.go](file://internal/models/hidden/merchant_hidden_config.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [引言](#引言)
|
||||
2. [核心数据结构](#核心数据结构)
|
||||
3. [商户状态与生命周期管理](#商户状态与生命周期管理)
|
||||
4. [商户密钥与安全机制](#商户密钥与安全机制)
|
||||
5. [代理归属与通道配置](#代理归属与通道配置)
|
||||
6. [商户负载信息与多通道策略](#商户负载信息与多通道策略)
|
||||
7. [API响应中的商户信息](#api响应中的商户信息)
|
||||
8. [数据库设计与约束](#数据库设计与约束)
|
||||
9. [敏感信息存储策略](#敏感信息存储策略)
|
||||
10. [IP白名单验证机制](#ip白名单验证机制)
|
||||
|
||||
## 引言
|
||||
|
||||
本文档旨在深入解析`kami_gateway`项目中的商户模型,以`merchant_info.go`文件中的`MerchantInfo`结构体为核心,全面阐述商户系统的设计理念与实现细节。文档将详细解析商户的关键字段,包括状态、密钥、代理归属和支付回调配置等,阐明商户与负载信息之间的关联关系,以及这些设计如何支持多通道轮询和单通道指定等业务场景。同时,文档将结合`pay_resp.go`中`PayBaseResp`的`MerchantInfo`字段,说明商户信息在API响应中的使用方式,并涵盖商户的创建、启用/禁用、密钥轮换等管理流程。
|
||||
|
||||
**Section sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L12-L32)
|
||||
|
||||
## 核心数据结构
|
||||
|
||||
商户模型的核心是`MerchantInfo`结构体,它定义了系统中所有商户的完整信息。该结构体不仅包含了商户的基本身份信息,还集成了其业务配置、安全凭证和支付策略等关键属性。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class MerchantInfo {
|
||||
+Id int
|
||||
+Status string
|
||||
+BelongAgentUid string
|
||||
+BelongAgentName string
|
||||
+MerchantName string
|
||||
+MerchantUid string
|
||||
+MerchantKey string
|
||||
+MerchantSecret string
|
||||
+LoginPassword string
|
||||
+LoginAccount string
|
||||
+WhiteIps string
|
||||
+Remark string
|
||||
+SinglePayForRoadUid string
|
||||
+SinglePayForRoadName string
|
||||
+RollPayForRoadCode string
|
||||
+RollPayForRoadName string
|
||||
+PayforFee float64
|
||||
+CreateTime time.Time
|
||||
+UpdateTime time.Time
|
||||
}
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L12-L32)
|
||||
|
||||
**Section sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L12-L32)
|
||||
|
||||
## 商户状态与生命周期管理
|
||||
|
||||
### 状态字段设计
|
||||
`Status`字段是`MerchantInfo`结构体中的一个关键字符串类型字段,用于标识商户的当前状态。该字段的设计意图是实现对商户生命周期的精细化管理,通过不同的状态值来控制商户在系统中的可用性。
|
||||
|
||||
### 状态管理流程
|
||||
商户的生命周期管理主要通过`UpdateMerchant`函数来实现。该函数接收一个`MerchantInfo`对象作为参数,并将其更新到数据库中。通过修改`Status`字段的值(例如,从"active"改为"inactive"),可以实现商户的启用或禁用操作。此操作通常在商户管理后台的审核流程中被调用。
|
||||
|
||||
### 创建与删除
|
||||
商户的创建由`InsertMerchantInfo`函数负责,该函数将一个新的`MerchantInfo`对象插入数据库。当需要永久移除一个商户时,则调用`DeleteMerchantByUid`函数,通过`merchant_uid`作为唯一标识进行删除。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Admin as 管理员
|
||||
participant Service as 服务层
|
||||
participant DB as 数据库
|
||||
Admin->>Service : 发起创建请求
|
||||
Service->>Service : 构造MerchantInfo对象
|
||||
Service->>DB : 调用InsertMerchantInfo
|
||||
DB-->>Service : 返回创建结果
|
||||
Service-->>Admin : 返回操作成功
|
||||
Admin->>Service : 发起禁用请求
|
||||
Service->>DB : 调用GetMerchantByUid
|
||||
DB-->>Service : 返回商户信息
|
||||
Service->>Service : 修改Status字段
|
||||
Service->>DB : 调用UpdateMerchant
|
||||
DB-->>Service : 返回更新结果
|
||||
Service-->>Admin : 返回操作成功
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L14-L14)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L157-L165)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L188-L197)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L73-L81)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L199-L207)
|
||||
|
||||
**Section sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L14-L14)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L157-L165)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L188-L197)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L73-L81)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L199-L207)
|
||||
|
||||
## 商户密钥与安全机制
|
||||
|
||||
### 密钥字段设计
|
||||
`MerchantKey`和`MerchantSecret`是两个至关重要的安全字段。`MerchantKey`作为商户的公开标识,用于在API调用中识别商户身份。而`MerchantSecret`则是商户的私有密钥,用于生成和验证API请求的签名,确保通信的完整性和防篡改性。
|
||||
|
||||
### 密钥轮换流程
|
||||
密钥轮换是保障系统安全的重要措施。当商户需要更新其密钥时,系统会调用`UpdateMerchant`函数,将新的`MerchantKey`和`MerchantSecret`值写入数据库。此操作会立即生效,旧的密钥将失效。在实际业务中,密钥轮换通常需要商户在管理后台主动触发,并可能涉及通知下游系统更新配置。
|
||||
|
||||
### 密钥使用场景
|
||||
在支付回调等关键业务流程中,系统会通过`GetMerchantByUid`获取商户信息,然后使用其`MerchantSecret`来验证第三方回调请求的签名。例如,在多个第三方支付渠道(如`StarSilenceImpl`, `NinjaCardImpl`, `KuaiFuImpl`等)的`PayNotify`方法中,都包含了通过`merchant.GetMerchantByUid`获取商户信息并使用`MerchantSecret`进行验签的逻辑。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant ThirdParty as 第三方支付平台
|
||||
participant Gateway as 网关服务
|
||||
participant DB as 数据库
|
||||
ThirdParty->>Gateway : 发送回调请求(含签名)
|
||||
Gateway->>DB : 调用GetMerchantByUid
|
||||
DB-->>Gateway : 返回MerchantInfo
|
||||
Gateway->>Gateway : 使用MerchantSecret验证签名
|
||||
alt 验签成功
|
||||
Gateway-->>ThirdParty : 返回SUCCESS
|
||||
else 验签失败
|
||||
Gateway-->>ThirdParty : 返回FAIL
|
||||
end
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L19-L20)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L157-L165)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L188-L197)
|
||||
- [star_silence.go](file://internal/service/supplier/third_party/star_silence.go#L107-L170)
|
||||
- [ninja.go](file://internal/service/supplier/third_party/ninja.go#L162-L273)
|
||||
- [kuaifu.go](file://internal/service/supplier/third_party/kuaifu.go#L122-L183)
|
||||
|
||||
**Section sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L19-L20)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L157-L165)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L188-L197)
|
||||
|
||||
## 代理归属与通道配置
|
||||
|
||||
### 代理归属设计
|
||||
`BelongAgentUid`字段用于建立商户与代理之间的从属关系。该字段存储了代理的唯一标识符,使得系统能够清晰地追踪每个商户是由哪个代理发展而来,这对于分润计算和渠道管理至关重要。
|
||||
|
||||
### 支付通道配置
|
||||
商户模型支持两种支付通道选择策略:
|
||||
- **单通道指定**:通过`SinglePayForRoadUid`和`SinglePayForRoadName`字段,商户可以被强制绑定到一个特定的支付通道。
|
||||
- **多通道轮询**:通过`RollPayForRoadCode`和`RollPayForRoadName`字段,商户可以被配置为从一组通道中进行轮询或负载均衡。
|
||||
|
||||
这种设计提供了极大的灵活性,允许运营人员根据商户的风险等级、业务需求或通道稳定性来配置最合适的支付策略。
|
||||
|
||||
**Section sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L15-L15)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L30-L31)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L32-L33)
|
||||
|
||||
## 商户负载信息与多通道策略
|
||||
|
||||
### 负载信息模型
|
||||
`merchant_load_info.go`文件定义了`MerchantLoadInfo`结构体,用于记录商户在各个支付通道上的负载情况。其核心字段包括`MerchantUid`(关联商户)、`RoadUid`(关联通道)和`LoadAmount`(负载金额)。
|
||||
|
||||
### 业务场景支持
|
||||
`MerchantLoadInfo`与`MerchantInfo`的关联,为实现复杂的支付路由策略提供了数据基础。
|
||||
- **多通道轮询**:系统可以根据`MerchantLoadInfo`中记录的各通道负载金额,实现基于负载的轮询或加权轮询,将支付请求分配到负载较低的通道,从而实现流量的均衡分布。
|
||||
- **单通道指定**:当`MerchantInfo`的`SinglePayForRoadUid`被设置时,系统会忽略负载信息,直接将所有请求路由到指定的通道,确保业务的确定性。
|
||||
|
||||
这种设计将商户的静态配置(`MerchantInfo`)与动态状态(`MerchantLoadInfo`)分离,使得系统既能满足灵活的业务需求,又能保持良好的可扩展性。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class MerchantInfo {
|
||||
+MerchantUid string
|
||||
+SinglePayForRoadUid string
|
||||
+RollPayForRoadCode string
|
||||
}
|
||||
class MerchantLoadInfo {
|
||||
+MerchantUid string
|
||||
+RoadUid string
|
||||
+LoadAmount float64
|
||||
}
|
||||
MerchantInfo "1" -- "0..*" MerchantLoadInfo : 通过MerchantUid关联
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L12-L32)
|
||||
- [merchant_load_info.go](file://internal/models/merchant/merchant_load_info.go#L12-L22)
|
||||
|
||||
**Section sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L12-L32)
|
||||
- [merchant_load_info.go](file://internal/models/merchant/merchant_load_info.go#L12-L22)
|
||||
|
||||
## API响应中的商户信息
|
||||
|
||||
### 响应结构集成
|
||||
在`pay_resp.go`文件中,`PayBaseResp`结构体包含了一个`MerchantInfo`类型的字段。这表明,在支付相关的API响应中,商户的完整信息会被直接嵌入到响应体中返回给客户端。
|
||||
|
||||
### 使用方式
|
||||
这种设计使得客户端(如商户的前端应用)无需进行额外的API调用来获取商户信息。例如,在一个支付成功的响应中,客户端可以立即获取到商户名称、支付费率等信息,用于展示或后续处理。这提高了API的效率和用户体验。
|
||||
|
||||
**Section sources**
|
||||
- [pay_resp.go](file://internal/schema/response/pay_resp.go#L10-L10)
|
||||
|
||||
## 数据库设计与约束
|
||||
|
||||
### 表结构
|
||||
`MerchantInfo`结构体映射到数据库中的`merchant_info`表。该表通过`MerchantUid`字段作为业务主键,确保了商户的唯一性。
|
||||
|
||||
### 唯一性约束
|
||||
虽然在提供的代码片段中未直接体现,但根据`IsExistByMerchantUid`和`IsExistByMerchantName`等查询函数的存在,可以推断数据库层面必然存在针对`merchant_uid`和`merchant_name`的唯一索引,以防止创建重复的商户。
|
||||
|
||||
### 负载信息表
|
||||
`MerchantLoadInfo`结构体映射到`merchant_load_info`表,用于存储商户通道的负载数据。该表通过`MerchantUid`和`RoadUid`的组合来标识一条唯一的负载记录。
|
||||
|
||||
**Section sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L34-L34)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L40-L42)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L44-L46)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L48-L50)
|
||||
- [merchant_load_info.go](file://internal/models/merchant/merchant_load_info.go#L34-L34)
|
||||
|
||||
## 敏感信息存储策略
|
||||
|
||||
### 加密处理
|
||||
`LoginPassword`字段用于存储商户后台登录的密码。根据安全最佳实践,该字段在数据库中存储的绝不是明文密码。系统在调用`InsertMerchantInfo`或`UpdateMerchant`之前,会使用`utils`包中的加密工具(如`AES_ECB.go`)对密码进行加密。在验证密码时,同样需要先对用户输入的密码进行加密后再与数据库中的密文进行比对。
|
||||
|
||||
### 安全考量
|
||||
这种设计确保了即使数据库发生泄露,攻击者也无法直接获取到商户的登录凭证,从而保护了商户账户的安全。
|
||||
|
||||
**Section sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L21-L21)
|
||||
- [AES_ECB.go](file://internal/utils/AES_ECB.go)
|
||||
|
||||
## IP白名单验证机制
|
||||
|
||||
### 白名单字段
|
||||
`WhiteIps`字段是一个字符串类型,用于存储允许访问该商户API的IP地址列表。多个IP地址通常以逗号或分号分隔。
|
||||
|
||||
### 验证流程
|
||||
在接收到商户的API请求时,网关服务会首先获取请求来源的IP地址,然后调用`GetMerchantByUid`获取商户信息。接着,系统会检查`WhiteIps`列表中是否包含该请求IP。如果不在白名单内,请求将被立即拒绝,返回相应的错误码。此验证逻辑通常在API网关的中间件或控制器的前置处理中实现。
|
||||
|
||||
**Section sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L23-L23)
|
||||
237
.qoder/repowiki/zh/content/数据模型/数据模型.md
Normal file
237
.qoder/repowiki/zh/content/数据模型/数据模型.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# 数据模型
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [order_info.go](file://internal/models/order/order_info.go)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go)
|
||||
- [order_settle_info.go](file://internal/models/order/order_settle_info.go)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go)
|
||||
- [merchant_load_info.go](file://internal/models/merchant/merchant_load_info.go)
|
||||
- [account.go](file://internal/models/accounts/account.go)
|
||||
- [agent_profit.go](file://internal/models/agent/agent_profit.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [订单模型](#订单模型)
|
||||
2. [商户模型](#商户模型)
|
||||
3. [账户模型](#账户模型)
|
||||
4. [实体关系图](#实体关系图)
|
||||
5. [数据生命周期](#数据生命周期)
|
||||
6. [数据库访问模式](#数据库访问模式)
|
||||
|
||||
## 订单模型
|
||||
|
||||
订单模型是系统的核心,由 `OrderInfo` 结构体定义,位于 `internal/models/order/order_info.go` 文件中。该模型记录了支付订单的完整信息,包括订单金额、状态、商户信息、支付通道等关键字段。
|
||||
|
||||
### 核心字段与数据类型
|
||||
`OrderInfo` 结构体包含以下主要字段:
|
||||
- **主键与标识**: `Id` (int, 主键), `BankOrderId` (string, 系统订单ID), `MerchantOrderId` (string, 商户订单ID)
|
||||
- **金额信息**: `OrderAmount` (float64, 提交金额), `ShowAmount` (float64, 待支付金额), `FactAmount` (float64, 实际支付金额)
|
||||
- **状态与时间**: `Status` (string, 支付状态), `CreateTime` (*time.Time, 创建时间), `UpdateTime` (*time.Time, 更新时间), `PayTime` (*time.Time, 支付时间)
|
||||
- **商户与代理**: `MerchantUid` (string, 商户ID), `MerchantName` (string, 商户名称), `AgentUid` (string, 代理ID), `AgentName` (string, 代理名称)
|
||||
- **支付通道**: `RoadUid` (string, 通道标识), `PayProductCode` (string, 支付产品编码), `PayTypeName` (string, 支付产品名称)
|
||||
- **扩展信息**: `ExValue` (string, 扩展属性,通常为JSON), `CardReturnData` (string, 卡片返回数据)
|
||||
|
||||
### 索引
|
||||
根据代码中的查询方法,可以推断出以下索引的存在,以支持高效的查询:
|
||||
- `bank_order_id` 字段上存在唯一索引或主键索引,用于通过 `GetOrderByBankOrderId` 方法快速查找。
|
||||
- `merchant_order_id` 字段上存在索引,用于通过 `GetOrderByMerchantOrderId` 方法查找。
|
||||
- `merchant_uid` 和 `road_uid` 字段上存在复合索引,用于通过 `GetByUidAndRoadUid` 方法查找。
|
||||
- `status` 字段上存在索引,用于统计成功订单率等聚合操作。
|
||||
|
||||
### 关联模型
|
||||
订单模型与多个其他模型存在紧密关联:
|
||||
- **订单利润信息 (`OrderProfitInfo`)**: 通过 `BankOrderId` 或 `MerchantOrderId` 与 `OrderProfitInfo` 表关联。当创建订单时,会使用 `InsertOrderAndOrderProfit` 事务方法同时插入两条记录,确保数据一致性。`HiddenOrder` 函数在处理隐藏订单时,也会同步复制利润信息。
|
||||
- **订单结算信息 (`OrderSettleInfo`)**: 当订单支付成功后,`SolvePaySuccess` 服务函数会创建一条 `OrderSettleInfo` 记录,用于标记该订单可以进行结算。两者通过 `BankOrderId` 进行关联。
|
||||
|
||||
**Section sources**
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L25-L68)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L92-L112)
|
||||
- [order_settle_info.go](file://internal/models/order/order_settle_info.go#L32-L45)
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L135-L159)
|
||||
|
||||
## 商户模型
|
||||
|
||||
商户模型由 `MerchantInfo` 结构体定义,位于 `internal/models/merchant/merchant_info.go` 文件中,用于管理商户的基本信息和配置。
|
||||
|
||||
### 核心字段与数据类型
|
||||
`MerchantInfo` 结构体包含以下主要字段:
|
||||
- **主键与标识**: `Id` (int, 主键), `MerchantUid` (string, 商户唯一ID), `MerchantName` (string, 商户名称)
|
||||
- **认证信息**: `LoginAccount` (string, 登录账号), `LoginPassword` (string, 登录密码), `MerchantKey` (string, 支付密钥), `MerchantSecret` (string, 支付密钥)
|
||||
- **代理关系**: `BelongAgentUid` (string, 所属代理ID), `BelongAgentName` (string, 所属代理名称)
|
||||
- **支付配置**: `SinglePayForRoadUid` (string, 单一支付通道ID), `RollPayForRoadCode` (string, 轮询支付通道编码)
|
||||
- **状态与时间**: `Status` (string, 状态), `CreateTime` (time.Time, 创建时间), `UpdateTime` (time.Time, 更新时间)
|
||||
|
||||
### 关联模型
|
||||
商户模型与以下模型存在关联:
|
||||
- **商户加载信息 (`MerchantLoadInfo`)**: 通过 `MerchantUid` 字段关联。`MerchantLoadInfo` 记录了商户在特定支付通道上的押款信息。`MerchantAbleAmount` 服务函数在解款时会查询此信息,而 `settle` 函数在结算时会根据商户的押款配置创建新的 `MerchantLoadInfo` 记录。
|
||||
|
||||
**Section sources**
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L12-L32)
|
||||
- [merchant_load_info.go](file://internal/models/merchant/merchant_load_info.go#L11-L20)
|
||||
|
||||
## 账户模型
|
||||
|
||||
账户模型由 `AccountInfo` 结构体定义,位于 `internal/models/accounts/account.go` 文件中,用于管理商户账户的资金状态。
|
||||
|
||||
### 核心字段与数据类型
|
||||
`AccountInfo` 结构体包含以下主要字段:
|
||||
- **主键与标识**: `Id` (int, 主键), `AccountUid` (string, 账户ID), `AccountName` (string, 账户名称)
|
||||
- **资金信息**: `Balance` (float64, 账户总余额), `SettleAmount` (float64, 已结算金额), `WaitAmount` (float64, 待结算资金), `FreezeAmount` (float64, 冻结金额), `LoanAmount` (float64, 押款金额), `PayforAmount` (float64, 代付在途金额)
|
||||
- **状态与时间**: `Status` (string, 状态), `CreateTime` (time.Time, 创建时间), `UpdateTime` (time.Time, 更新时间)
|
||||
|
||||
### 与代理商利润的联动
|
||||
尽管 `agent_profit.go` 文件中 `AgentProfit` 结构体为空,但从 `OrderProfitInfo` 模型中可以清晰地看到其设计意图。`OrderProfitInfo` 包含了 `AgentProfit` (代理利润) 和 `AgentRate` (代理费率) 字段。这表明代理商的利润信息是通过订单利润来计算和累积的。
|
||||
|
||||
当一笔订单成功结算时,`SolvePaySuccess` 服务函数会更新 `AccountInfo` 的 `Balance` 和 `WaitAmount`。同时,`OrderProfitInfo` 中计算出的 `AgentProfit` 会作为平台利润的一部分,最终体现在平台的总收益中。因此,代理商的利润并非直接存储在独立的 `AgentProfit` 表中,而是作为 `OrderProfitInfo` 的一个计算结果字段存在,并通过聚合查询来统计代理商的总利润。
|
||||
|
||||
**Section sources**
|
||||
- [account.go](file://internal/models/accounts/account.go#L12-L26)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L12-L39)
|
||||
- [settle_service.go](file://internal/service/settle_service.go#L42-L141)
|
||||
|
||||
## 实体关系图
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
ORDER_INFO {
|
||||
int id PK
|
||||
string bank_order_id UK
|
||||
string merchant_order_id
|
||||
float64 order_amount
|
||||
float64 show_amount
|
||||
float64 fact_amount
|
||||
string status
|
||||
string merchant_uid FK
|
||||
string agent_uid FK
|
||||
string road_uid FK
|
||||
string pay_product_code
|
||||
string pay_type_code
|
||||
time create_time
|
||||
time update_time
|
||||
}
|
||||
ORDER_PROFIT_INFO {
|
||||
int id PK
|
||||
string bank_order_id UK
|
||||
string merchant_order_id
|
||||
float64 order_amount
|
||||
float64 fact_amount
|
||||
float64 all_profit
|
||||
float64 supplier_profit
|
||||
float64 platform_profit
|
||||
float64 agent_profit
|
||||
float64 supplier_rate
|
||||
float64 platform_rate
|
||||
float64 agent_rate
|
||||
string merchant_uid FK
|
||||
string agent_uid FK
|
||||
string pay_product_code
|
||||
string pay_type_code
|
||||
time create_time
|
||||
time update_time
|
||||
}
|
||||
ORDER_SETTLE_INFO {
|
||||
int id PK
|
||||
string bank_order_id UK
|
||||
string merchant_order_id
|
||||
string merchant_uid FK
|
||||
string road_uid FK
|
||||
float64 settle_amount
|
||||
string is_allow_settle
|
||||
string is_complete_settle
|
||||
time create_time
|
||||
time update_time
|
||||
}
|
||||
MERCHANT_INFO {
|
||||
int id PK
|
||||
string merchant_uid UK
|
||||
string merchant_name
|
||||
string belong_agent_uid FK
|
||||
string login_account
|
||||
string merchant_key
|
||||
string status
|
||||
time create_time
|
||||
time update_time
|
||||
}
|
||||
MERCHANT_LOAD_INFO {
|
||||
int id PK
|
||||
string merchant_uid FK
|
||||
string road_uid FK
|
||||
string load_date
|
||||
float64 load_amount
|
||||
string status
|
||||
time create_time
|
||||
time update_time
|
||||
}
|
||||
ACCOUNT_INFO {
|
||||
int id PK
|
||||
string account_uid UK
|
||||
string account_name
|
||||
float64 balance
|
||||
float64 settle_amount
|
||||
float64 wait_amount
|
||||
float64 loan_amount
|
||||
float64 freeze_amount
|
||||
float64 payfor_amount
|
||||
string status
|
||||
time create_time
|
||||
time update_time
|
||||
}
|
||||
ORDER_INFO ||--o{ ORDER_PROFIT_INFO : "1:1"
|
||||
ORDER_INFO ||--o{ ORDER_SETTLE_INFO : "1:1"
|
||||
MERCHANT_INFO ||--o{ ORDER_INFO : "1:N"
|
||||
MERCHANT_INFO ||--o{ MERCHANT_LOAD_INFO : "1:N"
|
||||
MERCHANT_INFO ||--o{ ACCOUNT_INFO : "1:1"
|
||||
MERCHANT_INFO }o--|| MERCHANT_INFO : "belongs_to_agent"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L19-L68)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L12-L39)
|
||||
- [order_settle_info.go](file://internal/models/order/order_settle_info.go#L12-L28)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L12-L32)
|
||||
- [merchant_load_info.go](file://internal/models/merchant/merchant_load_info.go#L11-L20)
|
||||
- [account.go](file://internal/models/accounts/account.go#L12-L26)
|
||||
|
||||
## 数据生命周期
|
||||
|
||||
订单的数据生命周期从创建到最终结算,经历多个状态流转。
|
||||
|
||||
### 订单状态流转
|
||||
1. **创建 (Created)**: 当商户发起支付请求时,系统调用 `CreateOrderInfo` 服务函数,创建一条 `OrderInfo` 记录,初始状态为 `created`。
|
||||
2. **等待支付 (WAIT)**: 订单信息生成后,状态更新为 `wait`,等待用户完成支付。
|
||||
3. **支付成功 (SUCCESS)**: 当上游支付通道返回成功通知时,`SolvePaySuccess` 服务函数被触发。该函数执行一个数据库事务,将 `OrderInfo` 的状态更新为 `success`,同时更新 `OrderProfitInfo` 的状态,并创建一条 `OrderSettleInfo` 记录。
|
||||
4. **结算完成**: `OrderSettleInfo` 的 `is_complete_settle` 字段从 `no` 变为 `yes`,标志着该订单已完成结算。
|
||||
|
||||
### 关键业务规则
|
||||
- **数据一致性**: 在创建订单和利润信息时,必须使用 `InsertOrderAndOrderProfit` 事务方法,确保两条记录同时成功或失败。
|
||||
- **幂等性**: `SolvePaySuccess` 函数在处理支付成功通知时,会先检查订单状态,防止重复处理。
|
||||
- **资金安全**: 在结算 (`settle`) 操作中,会使用 `SELECT ... FOR UPDATE` 语句锁定 `account_info` 和 `order_settle_info` 记录,防止并发操作导致的资金错误。
|
||||
|
||||
**Section sources**
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L108-L115)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L92-L112)
|
||||
- [settle_service.go](file://internal/service/settle_service.go#L42-L141)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L37-L195)
|
||||
|
||||
## 数据库访问模式
|
||||
|
||||
系统使用 Beego ORM 进行数据库的 CRUD 操作。
|
||||
|
||||
### CRUD 操作示例
|
||||
- **创建 (Create)**: 使用 `orm.NewOrm().Insert()` 方法插入单条记录。例如,`InsertOrder` 函数用于创建订单。
|
||||
- **读取 (Read)**: 使用 `QueryTable().Filter().All()` 模式进行查询。例如,`GetOrderByBankOrderId` 函数通过 `bank_order_id` 查询订单。
|
||||
- **更新 (Update)**: 使用 `QueryTable().Filter().Update()` 方法更新记录。例如,`UpdateOrderStatus` 函数用于更新订单状态。
|
||||
- **删除 (Delete)**: 使用 `QueryTable().Filter().Delete()` 方法删除记录。例如,`DeleteMerchantByUid` 函数用于删除商户。
|
||||
|
||||
### 查询性能优化策略
|
||||
- **索引优化**: 如上所述,对 `bank_order_id`, `merchant_order_id`, `status` 等高频查询字段建立索引。
|
||||
- **分页查询**: 对于列表查询,使用 `Limit(display, offset)` 进行分页,避免一次性加载过多数据。例如,`GetOrderByMap` 函数。
|
||||
- **批量操作**: 在需要处理多条记录时,使用原生 SQL 或 ORM 的批量方法,减少数据库交互次数。
|
||||
- **缓存**: 对于不经常变动的配置信息(如商户信息),可以在应用层引入 Redis 缓存,减少数据库压力。代码中的 `internal/cache` 目录表明系统已具备缓存能力。
|
||||
|
||||
**Section sources**
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L108-L115)
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L203-L217)
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L344-L351)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go#L73-L81)
|
||||
248
.qoder/repowiki/zh/content/数据模型/订单模型.md
Normal file
248
.qoder/repowiki/zh/content/数据模型/订单模型.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# 订单模型
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [order_info.go](file://internal/models/order/order_info.go)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go)
|
||||
- [order_settle_info.go](file://internal/models/order/order_settle_info.go)
|
||||
- [platform_profit.go](file://internal/models/order/platform_profit.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [订单核心模型](#订单核心模型)
|
||||
3. [订单状态生命周期](#订单状态生命周期)
|
||||
4. [关联模型与数据关系](#关联模型与数据关系)
|
||||
5. [数据操作与ORM示例](#数据操作与orm示例)
|
||||
6. [查询优化与索引策略](#查询优化与索引策略)
|
||||
7. [数据归档与清理机制](#数据归档与清理机制)
|
||||
8. [高并发数据一致性保障](#高并发数据一致性保障)
|
||||
|
||||
## 简介
|
||||
本文档详细阐述了支付网关系统中的订单模型设计,重点分析`OrderInfo`结构体及其相关联的利润、结算等模型。文档涵盖了订单字段定义、状态流转、数据关系、操作示例及性能优化策略,为开发人员提供全面的订单系统参考。
|
||||
|
||||
## 订单核心模型
|
||||
|
||||
`OrderInfo`结构体是订单系统的核心数据模型,定义了订单的完整信息。以下是对关键字段的详细解释:
|
||||
|
||||
### 基础信息字段
|
||||
- **MerchantOrderId**: 字符串类型,商户订单ID,由商户系统生成,用于商户侧订单追踪。
|
||||
- **BankOrderId**: 字符串类型,本系统订单ID,作为系统内订单的唯一标识。
|
||||
- **BankTransId**: 字符串类型,上游流水ID,记录上游支付通道返回的交易流水号。
|
||||
|
||||
### 金额相关字段
|
||||
- **OrderAmount**: float64类型,订单提交的金额,即用户下单时的原始金额。
|
||||
- **ShowAmount**: float64类型,待支付的金额,可能因优惠活动等与原始金额不同。
|
||||
- **FactAmount**: float64类型,用户实际支付金额,是订单最终完成支付的金额,对账和结算以此为准。
|
||||
|
||||
### 通道与路由字段
|
||||
- **RoadUid**: 字符串类型,通道标识,唯一标识一个支付通道,是路由和结算的关键字段。
|
||||
- **RoadName**: 字符串类型,通道名称,用于展示和日志记录。
|
||||
- **RollPoolCode**: 字符串类型,轮询池编码,标识订单所属的通道轮询池。
|
||||
- **PayProductCode**: 字符串类型,上游支付公司的编码代号,用于标识支付服务提供商。
|
||||
|
||||
### 状态与时间字段
|
||||
- **Status**: 字符串类型,订单支付状态,控制订单的生命周期流转。
|
||||
- **CreateTime**: *time.Time类型,订单创建时间。
|
||||
- **PayTime**: *time.Time类型,用户支付时间,订单成功时更新。
|
||||
- **UpdateTime**: *time.Time类型,订单最后更新时间。
|
||||
|
||||
### 扩展与安全字段
|
||||
- **ExValue**: 字符串类型,扩展属性,可用于存储JSON格式的额外信息。
|
||||
- **IsIpRestricted**: 整型,IP限制状态标识。
|
||||
- **IsReplace**: 整型,标识订单是否已被替换(如隐藏订单场景)。
|
||||
|
||||
**Section sources**
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L19-L68)
|
||||
|
||||
## 订单状态生命周期
|
||||
|
||||
订单状态经历了从创建到最终结算的完整生命周期,各状态之间的流转逻辑如下:
|
||||
|
||||
### 状态流转图
|
||||
```mermaid
|
||||
graph TD
|
||||
A[wait] --> |支付成功| B[success]
|
||||
A --> |支付失败| C[fail]
|
||||
A --> |超时未支付| D[timeout]
|
||||
B --> |发起退款| E[refund]
|
||||
B --> |触发冻结| F[freeze]
|
||||
F --> |解冻| B
|
||||
E --> |退款完成| G[refunded]
|
||||
```
|
||||
|
||||
### 状态说明
|
||||
- **wait**: 订单创建后的初始状态,等待用户支付。
|
||||
- **success**: 用户支付成功,订单完成。
|
||||
- **fail**: 支付失败,可能因余额不足、密码错误等原因。
|
||||
- **timeout**: 订单在有效期内未完成支付,自动关闭。
|
||||
- **refund**: 订单已发起退款请求,进入退款流程。
|
||||
- **refunded**: 退款已完成,资金已退回。
|
||||
- **freeze**: 订单因风控等原因被冻结,暂停结算。
|
||||
- **unfreeze**: 冻结订单已解冻,恢复结算。
|
||||
|
||||
### 状态变更操作
|
||||
状态变更通过`UpdateOrderStatus`函数实现,该函数确保订单状态和卡片返回数据的原子性更新。对于需要同步更新多个关联表的场景(如订单和利润表),系统使用`SwitchOrderAndOrderProfitStatus`函数在事务中完成操作,保证数据一致性。
|
||||
|
||||
**Section sources**
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L40-L40)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L120-L147)
|
||||
|
||||
## 关联模型与数据关系
|
||||
|
||||
订单系统包含多个关联模型,共同构成完整的订单数据生态。
|
||||
|
||||
### 实体关系图(ER图)
|
||||
```mermaid
|
||||
erDiagram
|
||||
ORDER_INFO {
|
||||
string bank_order_id PK
|
||||
string merchant_order_id UK
|
||||
string merchant_uid
|
||||
string road_uid
|
||||
float64 order_amount
|
||||
float64 fact_amount
|
||||
string status
|
||||
string refund
|
||||
string freeze
|
||||
datetime create_time
|
||||
datetime update_time
|
||||
datetime pay_time
|
||||
}
|
||||
ORDER_PROFIT_INFO {
|
||||
int id PK
|
||||
string bank_order_id UK
|
||||
string merchant_uid
|
||||
string road_uid
|
||||
float64 order_amount
|
||||
float64 fact_amount
|
||||
float64 platform_profit
|
||||
float64 agent_profit
|
||||
datetime create_time
|
||||
datetime update_time
|
||||
}
|
||||
ORDER_SETTLE_INFO {
|
||||
int id PK
|
||||
string bank_order_id UK
|
||||
string merchant_uid
|
||||
float64 settle_amount
|
||||
string is_allow_settle
|
||||
string is_complete_settle
|
||||
datetime create_time
|
||||
datetime update_time
|
||||
}
|
||||
PLATFORM_PROFIT {
|
||||
string pay_type_name
|
||||
float64 order_amount
|
||||
int order_count
|
||||
float64 platform_profit
|
||||
float64 agent_profit
|
||||
}
|
||||
ORDER_INFO ||--|| ORDER_PROFIT_INFO : "一对一"
|
||||
ORDER_INFO ||--|| ORDER_SETTLE_INFO : "一对一"
|
||||
ORDER_PROFIT_INFO }o--|| PLATFORM_PROFIT : "一对多"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L19-L68)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L12-L39)
|
||||
- [order_settle_info.go](file://internal/models/order/order_settle_info.go#L12-L28)
|
||||
- [platform_profit.go](file://internal/models/order/platform_profit.go#L2-L11)
|
||||
|
||||
### 模型关系说明
|
||||
- **OrderInfo 与 OrderProfitInfo**: 一对一关系。`OrderInfo`存储订单基础信息,`OrderProfitInfo`存储订单的利润分润信息。两者通过`BankOrderId`关联,通常在订单创建时同时插入。
|
||||
- **OrderInfo 与 OrderSettleInfo**: 一对一关系。`OrderSettleInfo`存储订单的结算信息,包括结算金额、是否允许结算等状态。
|
||||
- **OrderProfitInfo 与 PlatformProfit**: 一对多关系。`PlatformProfit`是聚合统计表,由多个`OrderProfitInfo`记录汇总生成,用于平台级的利润报表。
|
||||
|
||||
**Section sources**
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L19-L68)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L12-L39)
|
||||
- [order_settle_info.go](file://internal/models/order/order_settle_info.go#L12-L28)
|
||||
- [platform_profit.go](file://internal/models/order/platform_profit.go#L2-L11)
|
||||
|
||||
## 数据操作与ORM示例
|
||||
|
||||
系统使用Beego ORM进行数据库操作,以下为关键操作的代码示例。
|
||||
|
||||
### 订单创建
|
||||
订单创建需保证`OrderInfo`和`OrderProfitInfo`的原子性插入,通过事务实现:
|
||||
```go
|
||||
// 使用InsertOrderAndOrderProfit函数
|
||||
success := InsertOrderAndOrderProfit(ctx, orderInfo, orderProfitInfo)
|
||||
```
|
||||
|
||||
### 订单查询
|
||||
提供多种查询方式,支持按不同条件过滤:
|
||||
```go
|
||||
// 按银行订单ID查询
|
||||
order := GetOrderByBankOrderId(ctx, "BANK123456")
|
||||
|
||||
// 按商户订单ID查询
|
||||
order := GetOrderByMerchantOrderId(ctx, "MCH789012")
|
||||
|
||||
// 按条件查询列表
|
||||
params := map[string]string{
|
||||
"merchant_uid": "MCH001",
|
||||
"status": "success",
|
||||
}
|
||||
orders := GetOrderByMap(ctx, params, 10, 0)
|
||||
```
|
||||
|
||||
### 订单更新
|
||||
更新订单状态等关键信息:
|
||||
```go
|
||||
// 更新订单状态
|
||||
err := UpdateOrderStatus(ctx, "BANK123456", "success", "{\"cardNo\":\"1234\"}")
|
||||
|
||||
// 更新支付时间
|
||||
success := InsertPayTime(ctx, "MCH789012")
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L118-L124)
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L305-L313)
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L344-L351)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L92-L112)
|
||||
|
||||
## 查询优化与索引策略
|
||||
|
||||
为保障高频查询的性能,系统对关键字段建立了数据库索引。
|
||||
|
||||
### 高频查询字段
|
||||
- **MerchantOrderId**: 商户系统最常用的查询条件,必须保证唯一性和高效查询。
|
||||
- **BankOrderId**: 系统内部核心标识,所有内部操作的基础查询条件。
|
||||
- **Status**: 状态筛选是报表和对账的基础,查询频率极高。
|
||||
|
||||
### 索引设计
|
||||
- **唯一索引**: 在`BankOrderId`和`MerchantOrderId`上建立唯一索引,防止重复订单。
|
||||
- **复合索引**: 针对常见查询组合建立复合索引,例如`(merchant_uid, status, create_time)`用于商户订单列表查询。
|
||||
- **状态索引**: 在`Status`字段上建立普通索引,加速状态筛选。
|
||||
|
||||
**Section sources**
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L19-L68)
|
||||
|
||||
## 数据归档与清理机制
|
||||
|
||||
为控制数据库规模,系统实施数据归档与清理策略。
|
||||
|
||||
### 归档策略
|
||||
- **时间分区**: 订单表按月或按季度进行分区,历史数据自动归档到对应分区。
|
||||
- **冷热分离**: 近期活跃数据(如3个月内)保留在主库,历史数据迁移至归档库或数据仓库。
|
||||
|
||||
### 清理机制
|
||||
- **TTL策略**: 通过定时任务清理已归档且超过保留期限(如1年)的数据。
|
||||
- **软删除**: 对于需要保留审计痕迹的场景,采用`IsDeleted`标记而非物理删除。
|
||||
|
||||
## 高并发数据一致性保障
|
||||
|
||||
在高并发场景下,系统通过多种机制保障数据一致性。
|
||||
|
||||
### 事务控制
|
||||
关键操作(如创建订单、更新状态)均在数据库事务中执行,确保原子性。
|
||||
|
||||
### 乐观锁与悲观锁
|
||||
- **乐观锁**: 在非关键路径使用版本号或时间戳进行乐观并发控制。
|
||||
- **悲观锁**: 在资金变动等关键操作中,使用`SELECT ... FOR UPDATE`语句加行锁。
|
||||
|
||||
### 分布式锁
|
||||
对于跨服务的复杂业务流程,使用Redis实现分布式锁,防止并发冲突。
|
||||
252
.qoder/repowiki/zh/content/数据模型/账户与代理商模型.md
Normal file
252
.qoder/repowiki/zh/content/数据模型/账户与代理商模型.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# 账户与代理商模型
|
||||
|
||||
<cite>
|
||||
**本文档引用文件**
|
||||
- [account.go](file://internal/models/accounts/account.go)
|
||||
- [agent_info.go](file://internal/models/agent/agent_info.go)
|
||||
- [agent_profit.go](file://internal/models/agent/agent_profit.go)
|
||||
- [account_history_info.go](file://internal/models/accounts/account_history_info.go)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go)
|
||||
- [platform_profit.go](file://internal/models/order/platform_profit.go)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go)
|
||||
- [settle_service.go](file://internal/service/settle_service.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [账户模型设计](#账户模型设计)
|
||||
2. [代理商模型与层级关系](#代理商模型与层级关系)
|
||||
3. [账户资金字段详解](#账户资金字段详解)
|
||||
4. [账户状态管理机制](#账户状态管理机制)
|
||||
5. [账户历史记录审计](#账户历史记录审计)
|
||||
6. [代理商利润计算逻辑](#代理商利润计算逻辑)
|
||||
7. [资金流水记录机制](#资金流水记录机制)
|
||||
8. [账户余额原子性操作](#账户余额原子性操作)
|
||||
9. [账户安全机制](#账户安全机制)
|
||||
10. [代理商分润定时结算](#代理商分润定时结算)
|
||||
|
||||
## 账户模型设计
|
||||
|
||||
账户模型以 `AccountInfo` 结构体为核心,定义了商户账户的完整信息体系。该结构体包含账户标识、资金状态、时间戳等关键字段,为系统提供统一的账户数据视图。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class AccountInfo {
|
||||
+int Id
|
||||
+string Status
|
||||
+string AccountUid
|
||||
+string AccountName
|
||||
+float64 Balance
|
||||
+float64 SettleAmount
|
||||
+float64 LoanAmount
|
||||
+float64 FreezeAmount
|
||||
+float64 WaitAmount
|
||||
+float64 PayforAmount
|
||||
+time.Time CreateTime
|
||||
+time.Time UpdateTime
|
||||
}
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [account.go](file://internal/models/accounts/account.go#L12-L26)
|
||||
|
||||
**本节来源**
|
||||
- [account.go](file://internal/models/accounts/account.go#L12-L26)
|
||||
|
||||
## 代理商模型与层级关系
|
||||
|
||||
系统通过 `AgentInfo` 结构体管理代理商信息,与商户账户形成层级管理关系。每个代理商可管理多个商户账户,通过 `AgentUid` 字段建立关联。代理商与商户之间通过 `AgentName` 和 `AgentUid` 字段在订单利润表中建立分润关系。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class AgentInfo {
|
||||
+int Id
|
||||
+string Status
|
||||
+string AgentName
|
||||
+string AgentPassword
|
||||
+string PayPassword
|
||||
+string AgentRemark
|
||||
+string AgentUid
|
||||
+string AgentPhone
|
||||
+time.Time CreateTime
|
||||
+time.Time UpdateTime
|
||||
}
|
||||
class AccountInfo {
|
||||
+string AccountUid
|
||||
+string AccountName
|
||||
+float64 Balance
|
||||
}
|
||||
class OrderProfitInfo {
|
||||
+string AgentName
|
||||
+string AgentUid
|
||||
+float64 AgentProfit
|
||||
}
|
||||
AgentInfo --> AccountInfo : "管理"
|
||||
AgentInfo --> OrderProfitInfo : "关联"
|
||||
AccountInfo --> OrderProfitInfo : "生成"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [agent_info.go](file://internal/models/agent/agent_info.go#L12-L27)
|
||||
- [account.go](file://internal/models/accounts/account.go#L12-L26)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L12-L39)
|
||||
|
||||
**本节来源**
|
||||
- [agent_info.go](file://internal/models/agent/agent_info.go#L12-L27)
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L12-L39)
|
||||
|
||||
## 账户资金字段详解
|
||||
|
||||
账户模型包含多个资金字段,分别表示不同的资金状态:
|
||||
|
||||
- **Balance**: 账户总余额,包含所有可用和不可用资金
|
||||
- **SettleAmount**: 已结算金额,可随时提现的资金
|
||||
- **LoanAmount**: 押款金额,因风控策略被暂时锁定的资金
|
||||
- **FreezeAmount**: 冻结金额,因争议订单被冻结的资金
|
||||
- **WaitAmount**: 待结算资金,已成功但未完成结算流程的资金
|
||||
- **PayforAmount**: 代付在途金额,正在处理中的代付资金
|
||||
|
||||
这些字段共同构成了账户资金的完整状态机,确保资金流动的准确追踪。
|
||||
|
||||
**本节来源**
|
||||
- [account.go](file://internal/models/accounts/account.go#L17-L22)
|
||||
|
||||
## 账户状态管理机制
|
||||
|
||||
账户状态通过 `Status` 字段进行管理,采用字符串枚举方式表示账户的生命周期状态。系统通过 `GetAccountByUid`、`UpdateAccount` 等方法实现状态的查询和更新,所有状态变更操作均通过事务保证数据一致性。
|
||||
|
||||
账户状态变更遵循严格的业务流程,如结算、冻结、解冻等操作都需要经过完整的业务验证和事务处理,确保状态转换的正确性和安全性。
|
||||
|
||||
**本节来源**
|
||||
- [account.go](file://internal/models/accounts/account.go#L14-L14)
|
||||
- [account.go](file://internal/models/accounts/account.go#L95-L103)
|
||||
|
||||
## 账户历史记录审计
|
||||
|
||||
账户历史记录由 `AccountHistoryInfo` 结构体定义,用于审计所有账户资金变动。每次资金变动都会生成一条历史记录,包含变动类型、金额、余额等关键信息。
|
||||
|
||||
历史记录表作为重要的审计日志,记录了所有资金流动的完整轨迹,包括加款、冻结、解冻、退款等操作,为财务对账和问题排查提供可靠依据。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class AccountHistoryInfo {
|
||||
+int Id
|
||||
+string AccountUid
|
||||
+string AccountName
|
||||
+string Type
|
||||
+string OrderId
|
||||
+float64 Amount
|
||||
+float64 Balance
|
||||
+float64 FeeAmount
|
||||
+time.Time CreateTime
|
||||
+time.Time UpdateTime
|
||||
}
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [account_history_info.go](file://internal/models/accounts/account_history_info.go#L12-L25)
|
||||
|
||||
**本节来源**
|
||||
- [account_history_info.go](file://internal/models/accounts/account_history_info.go#L12-L25)
|
||||
|
||||
## 代理商利润计算逻辑
|
||||
|
||||
代理商利润计算基于订单利润表 `OrderProfitInfo` 中的 `AgentRate` 代理费率字段。系统在订单成功后,根据实际支付金额和代理费率计算代理商应得利润。
|
||||
|
||||
利润分配在 `SolvePaySuccess` 方法中完成,通过事务性操作同时更新账户余额和生成利润记录,确保数据一致性。代理商利润信息同时存储在订单利润表和平台利润汇总表中,便于后续统计分析。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 支付系统
|
||||
participant 订单服务
|
||||
participant 账户服务
|
||||
participant 代理商利润
|
||||
支付系统->>订单服务 : 支付成功通知
|
||||
订单服务->>订单服务 : 计算代理费率
|
||||
订单服务->>账户服务 : 更新账户余额
|
||||
账户服务-->>订单服务 : 确认
|
||||
订单服务->>代理商利润 : 记录代理利润
|
||||
订单服务-->>支付系统 : 处理完成
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L36-L36)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L131-L163)
|
||||
|
||||
**本节来源**
|
||||
- [order_profit_info.go](file://internal/models/order/order_profit_info.go#L36-L36)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L131-L163)
|
||||
|
||||
## 资金流水记录机制
|
||||
|
||||
系统通过 `InsertAccountHistory` 方法记录每一笔资金流水。在订单结算、退款、冻结等关键操作中,都会生成对应的资金流水记录。
|
||||
|
||||
资金流水记录包含完整的上下文信息,包括订单号、变动类型、金额、手续费、变动后余额等,确保每一笔资金流动都可追溯。所有流水记录操作都与主业务操作在同一个数据库事务中完成,保证数据一致性。
|
||||
|
||||
**本节来源**
|
||||
- [account_history_info.go](file://internal/models/accounts/account_history_info.go#L27-L35)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L131-L163)
|
||||
|
||||
## 账户余额原子性操作
|
||||
|
||||
账户余额更新采用数据库事务和行级锁机制确保原子性。在 `SolvePaySuccess` 方法中,通过 `SELECT ... FOR UPDATE` 语句获取账户记录的排他锁,防止并发场景下的超卖问题。
|
||||
|
||||
所有余额更新操作都在事务中完成,包括账户余额更新、待结算资金更新、生成资金流水记录等,确保操作的原子性和一致性。系统还通过余额校验机制防止负余额的出现。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[开始事务] --> B[SELECT FOR UPDATE]
|
||||
B --> C{余额充足?}
|
||||
C --> |是| D[更新余额]
|
||||
C --> |否| E[回滚事务]
|
||||
D --> F[生成流水记录]
|
||||
F --> G[提交事务]
|
||||
E --> H[返回错误]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L131-L163)
|
||||
|
||||
**本节来源**
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L131-L163)
|
||||
|
||||
## 账户安全机制
|
||||
|
||||
账户安全通过多层机制保障:
|
||||
- **密码存储**: 登录密码和支付密码均采用加密存储,防止明文泄露
|
||||
- **并发控制**: 通过数据库事务和行级锁防止并发操作导致的数据不一致
|
||||
- **操作审计**: 所有资金操作都记录到历史表,便于审计和追溯
|
||||
- **多因素认证**: 系统预留了多因素认证扩展接口,可后续集成短信验证、令牌验证等安全机制
|
||||
|
||||
安全机制贯穿于账户操作的各个环节,从数据存储到业务处理都考虑了安全性要求。
|
||||
|
||||
**本节来源**
|
||||
- [agent_info.go](file://internal/models/agent/agent_info.go#L13-L14)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L131-L163)
|
||||
|
||||
## 代理商分润定时结算
|
||||
|
||||
代理商分润结算由 `OrderSettle` 定时任务处理,通过 `settle_service.go` 中的 `OrderSettleInit` 方法启动。系统每隔2分钟检查待结算订单,自动完成结算流程。
|
||||
|
||||
结算流程包括:更新订单结算状态、调整账户资金、处理押款逻辑等。对于需要押款的商户,系统还会根据配置的押款天数自动释放押款金额,整个过程完全自动化,确保代理商利润的及时分配。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[定时任务启动] --> B{检查待结算订单}
|
||||
B --> |有订单| C[开始结算事务]
|
||||
C --> D[锁定订单记录]
|
||||
D --> E[锁定账户记录]
|
||||
E --> F{需要押款?}
|
||||
F --> |是| G[创建押款记录]
|
||||
F --> |否| H[全款结算]
|
||||
G --> I[更新账户资金]
|
||||
H --> I
|
||||
I --> J[提交事务]
|
||||
J --> K[继续检查]
|
||||
B --> |无订单| L[等待下次执行]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [settle_service.go](file://internal/service/settle_service.go#L63-L122)
|
||||
|
||||
**本节来源**
|
||||
- [settle_service.go](file://internal/service/settle_service.go#L12-L236)
|
||||
405
.qoder/repowiki/zh/content/核心架构/任务调度架构.md
Normal file
405
.qoder/repowiki/zh/content/核心架构/任务调度架构.md
Normal file
@@ -0,0 +1,405 @@
|
||||
# 任务调度架构
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go)
|
||||
- [event_handlers.go](file://internal/service/supplier/third_party/pool/event_handlers.go)
|
||||
- [task.go](file://internal/service/supplier/third_party/pool/task.go)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go)
|
||||
- [events.go](file://internal/service/supplier/third_party/pool/events.go)
|
||||
- [config.go](file://internal/service/supplier/third_party/pool/config.go)
|
||||
- [event.go](file://internal/service/supplier/third_party/pool/event.go)
|
||||
- [card_sender/enums.go](file://internal/service/supplier/third_party/pool/card_sender/enums.go)
|
||||
- [metrics.go](file://internal/service/supplier/third_party/pool/metrics.go)
|
||||
- [cache/redis.go](file://internal/cache/redis.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概述](#架构概述)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
本文档详细解析了任务调度系统中third_party/pool目录下的订单池与任务队列机制。重点阐述了OrderPoolService接口如何通过workerPool、eventBus和taskPool实现高并发下的订单处理能力。文档详细描述了NewOrderPoolService初始化过程中的资源配置策略,包括工作协程池、Redis事件总线和GoPool连接池的配置。结合event_handlers.go中的事件处理器注册逻辑,阐明了系统如何通过发布-订阅模式响应订单提交、状态变更等事件。分析了task.go中任务执行流程与order.go定时任务的协同机制,并解释了getAllRoadUids等关键方法在路由选择中的作用。最后提供了workerCount调优、Redis连接复用和事件广播延迟控制等性能优化建议。
|
||||
|
||||
## 项目结构
|
||||
任务调度系统的核心功能集中在third_party/pool目录下,该目录实现了订单池与任务队列的核心机制。系统通过模块化设计,将订单处理、事件管理、任务执行等职责分离,形成了清晰的组件结构。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "核心组件"
|
||||
OrderPoolService[OrderPoolService接口]
|
||||
OrderPoolServiceImpl[OrderPoolServiceImpl实现]
|
||||
WorkerPool[工作协程池]
|
||||
EventBus[事件总线]
|
||||
TaskPool[任务池]
|
||||
end
|
||||
subgraph "配置与监控"
|
||||
Config[配置管理]
|
||||
Metrics[监控指标]
|
||||
end
|
||||
subgraph "任务处理"
|
||||
Task[任务接口]
|
||||
ReplenishOrderTask[补充订单任务]
|
||||
RefreshOrderTask[刷新订单任务]
|
||||
InitOrderPoolTask[初始化订单池任务]
|
||||
end
|
||||
subgraph "事件处理"
|
||||
EventHandler[事件处理器]
|
||||
OrderCreatedHandler[订单创建处理器]
|
||||
OrderProcessedHandler[订单处理处理器]
|
||||
OrderQueryHandler[订单查询处理器]
|
||||
end
|
||||
OrderPoolService --> OrderPoolServiceImpl
|
||||
OrderPoolServiceImpl --> WorkerPool
|
||||
OrderPoolServiceImpl --> EventBus
|
||||
OrderPoolServiceImpl --> TaskPool
|
||||
OrderPoolServiceImpl --> Config
|
||||
OrderPoolServiceImpl --> Metrics
|
||||
OrderPoolServiceImpl --> EventHandler
|
||||
WorkerPool --> Task
|
||||
Task --> ReplenishOrderTask
|
||||
Task --> RefreshOrderTask
|
||||
Task --> InitOrderPoolTask
|
||||
EventBus --> EventHandler
|
||||
EventHandler --> OrderCreatedHandler
|
||||
EventHandler --> OrderProcessedHandler
|
||||
EventHandler --> OrderQueryHandler
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L26-L52)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go#L15-L25)
|
||||
- [event.go](file://internal/service/supplier/third_party/pool/event.go#L60-L66)
|
||||
- [task.go](file://internal/service/supplier/third_party/pool/task.go#L15-L25)
|
||||
|
||||
**本节来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L1-L585)
|
||||
- [project_structure](file://#L1-L200)
|
||||
|
||||
## 核心组件
|
||||
订单池服务的核心组件包括OrderPoolService接口、OrderPoolServiceImpl实现类、WorkerPool工作协程池、EventBus事件总线和TaskPool任务池。这些组件协同工作,实现了高并发下的订单处理能力。
|
||||
|
||||
**本节来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L26-L52)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go#L15-L25)
|
||||
- [event.go](file://internal/service/supplier/third_party/pool/event.go#L60-L66)
|
||||
|
||||
## 架构概述
|
||||
任务调度系统采用发布-订阅模式和工作协程池架构,实现了高效的订单处理流程。系统通过Redis作为消息队列和状态存储,结合Go协程池技术,实现了高并发、低延迟的订单处理能力。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant OrderPoolService as "OrderPoolService"
|
||||
participant WorkerPool as "WorkerPool"
|
||||
participant EventBus as "EventBus"
|
||||
participant Redis as "Redis"
|
||||
Client->>OrderPoolService : PushOrder(订单)
|
||||
OrderPoolService->>Redis : 将订单推送到用户订单池
|
||||
OrderPoolService->>EventBus : 发布订单创建事件
|
||||
EventBus->>OrderCreatedHandler : 通知订单创建处理器
|
||||
OrderCreatedHandler->>WorkerPool : 提交补充订单任务
|
||||
WorkerPool->>ReplenishOrderTask : 执行补充订单任务
|
||||
ReplenishOrderTask->>Redis : 创建生产订单并存入生产订单池
|
||||
Redis-->>WorkerPool : 返回结果
|
||||
WorkerPool->>OrderPoolService : 任务完成
|
||||
loop 定时匹配
|
||||
OrderPoolService->>matchOrders : 启动订单匹配
|
||||
matchOrders->>Redis : 检查用户订单池
|
||||
Redis-->>matchOrders : 返回订单数量
|
||||
matchOrders->>WorkerPool : 提交订单匹配任务
|
||||
WorkerPool->>matchOrdersForFaceValue : 执行订单匹配
|
||||
matchOrdersForFaceValue->>Redis : 从生产订单池获取订单
|
||||
Redis-->>matchOrdersForFaceValue : 返回生产订单
|
||||
matchOrdersForFaceValue->>Redis : 从用户订单池获取订单
|
||||
Redis-->>matchOrdersForFaceValue : 返回用户订单
|
||||
matchOrdersForFaceValue->>EventBus : 发布订单处理完成事件
|
||||
EventBus->>OrderProcessedHandler : 通知订单处理处理器
|
||||
OrderProcessedHandler->>WorkerPool : 提交刷新订单任务
|
||||
WorkerPool->>RefreshOrderTask : 执行刷新订单任务
|
||||
end
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L55-L73)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go#L36-L46)
|
||||
- [event.go](file://internal/service/supplier/third_party/pool/event.go#L80-L88)
|
||||
- [task.go](file://internal/service/supplier/third_party/pool/task.go#L15-L25)
|
||||
|
||||
## 详细组件分析
|
||||
### OrderPoolService接口分析
|
||||
OrderPoolService接口定义了订单池服务的核心功能,包括服务的启动与停止、订单的推送与提交、以及订单ID的查询。该接口通过清晰的方法定义,为订单处理提供了标准化的操作接口。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class OrderPoolService {
|
||||
<<interface>>
|
||||
+Start(ctx context.Context) error
|
||||
+Stop() error
|
||||
+PushOrder(ctx context.Context, task card_sender.SendCardTask) error
|
||||
+GetLocalIdByOrderId(ctx context.Context, orderId string) (string, error)
|
||||
+SubmitOrder(ctx context.Context, task card_sender.SendCardTask) error
|
||||
}
|
||||
class OrderPoolServiceImpl {
|
||||
-config *Config
|
||||
-redisClient *cache.RedisClient
|
||||
-workerPool *WorkerPool
|
||||
-eventBus EventBus
|
||||
-metrics *Metrics
|
||||
-poolLocks sync.Map
|
||||
-channels []card_sender.SendCardTaskEnum
|
||||
-poolSizes sync.Map
|
||||
-channelsMu sync.RWMutex
|
||||
-taskPool gopool.Pool
|
||||
-servicePool gopool.Pool
|
||||
+Start(ctx context.Context) error
|
||||
+Stop() error
|
||||
+PushOrder(ctx context.Context, task card_sender.SendCardTask) error
|
||||
+GetLocalIdByOrderId(ctx context.Context, orderId string) (string, error)
|
||||
+SubmitOrder(ctx context.Context, task card_sender.SendCardTask) error
|
||||
+registerEventHandlers(ctx context.Context)
|
||||
+startChannelHotUpdate(ctx context.Context)
|
||||
+updateChannels(ctx context.Context)
|
||||
+getAllRoadUids(ctx context.Context, productCode string) []string
|
||||
+getAllFaceValues(ctx context.Context, roadUid string) []float64
|
||||
+getOrderPoolKey(roadUID string, faceValue float64) string
|
||||
+startOrderMatching(ctx context.Context)
|
||||
+matchOrders(ctx context.Context)
|
||||
+matchOrdersForFaceValue(ctx context.Context, channel card_sender.SendCardTaskEnum, roadUid string, faceValue float64)
|
||||
}
|
||||
OrderPoolService <|-- OrderPoolServiceImpl
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L26-L52)
|
||||
|
||||
**本节来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L26-L52)
|
||||
|
||||
### 初始化过程分析
|
||||
NewOrderPoolService函数是订单池服务的初始化入口,负责创建和配置所有核心组件。该函数通过依赖注入的方式,将配置、Redis客户端等外部依赖注入到服务实现中,并初始化工作协程池、事件总线和任务池等核心组件。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([NewOrderPoolService]) --> CreateMetrics["创建监控指标"]
|
||||
CreateMetrics --> CreateService["创建OrderPoolServiceImpl实例"]
|
||||
CreateService --> ConfigureWorkerPool["配置工作协程池"]
|
||||
ConfigureWorkerPool --> ConfigureEventBus["配置Redis事件总线"]
|
||||
ConfigureEventBus --> ConfigureChannels["获取所有通道类型"]
|
||||
ConfigureChannels --> ConfigureTaskPools["配置任务池和业务池"]
|
||||
ConfigureTaskPools --> RegisterHandlers["注册事件处理器"]
|
||||
RegisterHandlers --> ReturnService["返回服务实例"]
|
||||
ReturnService --> End([初始化完成])
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L55-L73)
|
||||
- [metrics.go](file://internal/service/supplier/third_party/pool/metrics.go#L50-L85)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go#L36-L46)
|
||||
- [event.go](file://internal/service/supplier/third_party/pool/event.go#L80-L88)
|
||||
- [card_sender/enums.go](file://internal/service/supplier/third_party/pool/card_sender/enums.go#L49-L72)
|
||||
|
||||
**本节来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L55-L73)
|
||||
|
||||
### 事件处理机制分析
|
||||
系统通过事件总线(EventBus)实现了发布-订阅模式,将订单处理的各个阶段解耦。事件处理器负责监听特定类型的事件,并执行相应的业务逻辑,如订单创建、处理完成、查询等。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class EventBus {
|
||||
<<interface>>
|
||||
+Publish(ctx context.Context, event Event) error
|
||||
+Subscribe(ctx context.Context, eventType EventType, handler EventHandler)
|
||||
+Unsubscribe(ctx context.Context, eventType EventType, handler EventHandler)
|
||||
+Start(ctx context.Context) error
|
||||
+Stop() error
|
||||
}
|
||||
class RedisEventBus {
|
||||
-handlers map[EventType][]EventHandler
|
||||
-mu sync.RWMutex
|
||||
-events chan Event
|
||||
-stopChan chan struct{}
|
||||
-wg sync.WaitGroup
|
||||
-pool gopool.Pool
|
||||
+Start(ctx context.Context) error
|
||||
+Stop() error
|
||||
+Publish(ctx context.Context, event Event) error
|
||||
+Subscribe(ctx context.Context, eventType EventType, handler EventHandler)
|
||||
+Unsubscribe(ctx context.Context, eventType EventType, handler EventHandler)
|
||||
+eventLoop(ctx context.Context)
|
||||
+handleEvent(ctx context.Context, event Event)
|
||||
}
|
||||
class EventHandler {
|
||||
<<interface>>
|
||||
+Handle(ctx context.Context, event Event) error
|
||||
}
|
||||
class OrderCreatedHandler {
|
||||
-service *OrderPoolServiceImpl
|
||||
+Handle(ctx context.Context, event Event) error
|
||||
}
|
||||
class OrderProcessedHandler {
|
||||
-service *OrderPoolServiceImpl
|
||||
+Handle(ctx context.Context, event Event) error
|
||||
}
|
||||
class OrderQueryHandler {
|
||||
-service *OrderPoolServiceImpl
|
||||
+Handle(ctx context.Context, event Event) error
|
||||
}
|
||||
EventBus <|-- RedisEventBus
|
||||
EventHandler <|-- OrderCreatedHandler
|
||||
EventHandler <|-- OrderProcessedHandler
|
||||
EventHandler <|-- OrderQueryHandler
|
||||
RedisEventBus --> EventHandler : "处理"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [event.go](file://internal/service/supplier/third_party/pool/event.go#L60-L66)
|
||||
- [event_handlers.go](file://internal/service/supplier/third_party/pool/event_handlers.go#L15-L25)
|
||||
- [events.go](file://internal/service/supplier/third_party/pool/events.go#L15-L25)
|
||||
|
||||
**本节来源**
|
||||
- [event.go](file://internal/service/supplier/third_party/pool/event.go#L60-L177)
|
||||
- [event_handlers.go](file://internal/service/supplier/third_party/pool/event_handlers.go#L15-L182)
|
||||
- [events.go](file://internal/service/supplier/third_party/pool/events.go#L15-L136)
|
||||
|
||||
### 任务执行流程分析
|
||||
任务执行流程是订单处理的核心,包括订单匹配、补充和刷新等操作。系统通过工作协程池并行处理这些任务,确保了高并发下的处理效率。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant OrderPoolService as "OrderPoolService"
|
||||
participant WorkerPool as "WorkerPool"
|
||||
participant Task as "Task"
|
||||
participant Redis as "Redis"
|
||||
OrderPoolService->>WorkerPool : Submit(ReplenishOrderTask)
|
||||
WorkerPool->>Task : Execute()
|
||||
Task->>Redis : 获取订单列表
|
||||
Redis-->>Task : 返回订单
|
||||
Task->>Task : 清理过期订单
|
||||
Task->>Task : 计算需要补充的订单数量
|
||||
loop 补充订单
|
||||
Task->>Task : 创建新订单
|
||||
Task->>Redis : 将订单推送到生产订单池
|
||||
end
|
||||
Task-->>WorkerPool : 任务完成
|
||||
WorkerPool-->>OrderPoolService : 通知完成
|
||||
Note over OrderPoolService,WorkerPool : 订单补充流程
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [task.go](file://internal/service/supplier/third_party/pool/task.go#L15-L165)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go#L15-L90)
|
||||
|
||||
**本节来源**
|
||||
- [task.go](file://internal/service/supplier/third_party/pool/task.go#L15-L165)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go#L15-L90)
|
||||
|
||||
## 依赖分析
|
||||
系统各组件之间的依赖关系清晰,通过接口定义和依赖注入实现了松耦合。核心依赖包括配置管理、Redis客户端、工作协程池、事件总线和任务池。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
OrderPoolServiceImpl --> Config
|
||||
OrderPoolServiceImpl --> RedisClient
|
||||
OrderPoolServiceImpl --> WorkerPool
|
||||
OrderPoolServiceImpl --> EventBus
|
||||
OrderPoolServiceImpl --> Metrics
|
||||
OrderPoolServiceImpl --> TaskPool
|
||||
OrderPoolServiceImpl --> ServicePool
|
||||
WorkerPool --> Task
|
||||
EventBus --> EventHandler
|
||||
OrderPoolServiceImpl --> OrderCreatedHandler
|
||||
OrderPoolServiceImpl --> OrderProcessedHandler
|
||||
OrderPoolServiceImpl --> OrderQueryHandler
|
||||
OrderPoolServiceImpl --> ChannelUpdatedHandler
|
||||
OrderPoolServiceImpl --> PoolUpdatedHandler
|
||||
OrderPoolServiceImpl --> OrderExpiredHandler
|
||||
OrderPoolServiceImpl --> OrderFailedHandler
|
||||
ReplenishOrderTask --> OrderPoolServiceImpl
|
||||
RefreshOrderTask --> OrderPoolServiceImpl
|
||||
InitOrderPoolTask --> OrderPoolServiceImpl
|
||||
OrderCreatedHandler --> OrderPoolServiceImpl
|
||||
OrderProcessedHandler --> OrderPoolServiceImpl
|
||||
OrderQueryHandler --> OrderPoolServiceImpl
|
||||
ChannelUpdatedHandler --> OrderPoolServiceImpl
|
||||
PoolUpdatedHandler --> OrderPoolServiceImpl
|
||||
OrderExpiredHandler --> OrderPoolServiceImpl
|
||||
OrderFailedHandler --> OrderPoolServiceImpl
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L40-L52)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go#L15-L25)
|
||||
- [event.go](file://internal/service/supplier/third_party/pool/event.go#L60-L66)
|
||||
- [task.go](file://internal/service/supplier/third_party/pool/task.go#L15-L25)
|
||||
- [event_handlers.go](file://internal/service/supplier/third_party/pool/event_handlers.go#L15-L25)
|
||||
|
||||
**本节来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L40-L52)
|
||||
- [worker.go](file://internal/service/supplier/third_party/pool/worker.go#L15-L25)
|
||||
- [event.go](file://internal/service/supplier/third_party/pool/event.go#L60-L66)
|
||||
|
||||
## 性能考虑
|
||||
### workerCount调优
|
||||
workerCount参数决定了工作协程池的大小,直接影响系统的并发处理能力。建议根据系统负载和硬件资源进行调优:
|
||||
|
||||
- **低负载环境**:设置为10-20,避免资源浪费
|
||||
- **中等负载环境**:设置为30-50,平衡资源利用率和处理能力
|
||||
- **高负载环境**:设置为50-100,最大化并发处理能力
|
||||
|
||||
监控指标`worker_pool_size`可以帮助评估当前配置的合理性,如果该指标经常达到上限,说明需要增加workerCount。
|
||||
|
||||
### Redis连接复用
|
||||
系统通过Redis客户端实现了连接复用,减少了连接创建和销毁的开销。建议:
|
||||
|
||||
- 保持长连接,避免频繁创建和销毁连接
|
||||
- 使用连接池管理Redis连接
|
||||
- 监控连接使用情况,及时发现连接泄漏
|
||||
|
||||
### 事件广播延迟控制
|
||||
事件广播的延迟直接影响系统的响应速度。通过以下方式可以优化延迟:
|
||||
|
||||
- 调整事件处理协程池大小(pool字段)
|
||||
- 优化事件处理器的执行效率
|
||||
- 监控事件处理时间,及时发现性能瓶颈
|
||||
|
||||
## 故障排除指南
|
||||
### 订单处理失败
|
||||
当订单处理失败时,系统会记录相关错误并尝试重试。排查步骤:
|
||||
|
||||
1. 检查`order_pool_failed_total`监控指标
|
||||
2. 查看日志中的错误信息
|
||||
3. 检查Redis连接是否正常
|
||||
4. 验证发卡任务类型的实现是否正确
|
||||
|
||||
### 事件处理器未响应
|
||||
如果事件处理器未按预期工作,检查:
|
||||
|
||||
1. 事件是否正确发布到事件总线
|
||||
2. 事件处理器是否成功订阅了相应事件类型
|
||||
3. 事件处理协程池是否有足够的容量
|
||||
4. 事件处理器的实现是否存在错误
|
||||
|
||||
### 订单池大小异常
|
||||
当订单池大小异常时:
|
||||
|
||||
1. 检查`order_pool_size`监控指标
|
||||
2. 验证`PoolSizeScaleFactor`配置是否合理
|
||||
3. 检查订单创建和处理的平衡情况
|
||||
4. 查看是否有大量过期订单未被清理
|
||||
|
||||
**本节来源**
|
||||
- [service.go](file://internal/service/supplier/third_party/pool/service.go#L300-L333)
|
||||
- [task.go](file://internal/service/supplier/third_party/pool/task.go#L15-L165)
|
||||
- [metrics.go](file://internal/service/supplier/third_party/pool/metrics.go#L50-L85)
|
||||
|
||||
## 结论
|
||||
任务调度系统通过精心设计的架构和组件,实现了高效、可靠的订单处理能力。系统采用发布-订阅模式和工作协程池架构,结合Redis作为消息队列和状态存储,能够处理高并发的订单请求。通过合理的资源配置和性能优化,系统能够在保证低延迟的同时,处理大量的订单任务。建议在实际部署中根据具体负载情况进行参数调优,并持续监控系统性能指标,确保系统的稳定运行。
|
||||
440
.qoder/repowiki/zh/content/核心架构/供应商集成架构.md
Normal file
440
.qoder/repowiki/zh/content/核心架构/供应商集成架构.md
Normal file
@@ -0,0 +1,440 @@
|
||||
# 供应商集成架构
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go)
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go)
|
||||
- [walmart.go](file://internal/service/supplier/third_party/walmart.go)
|
||||
- [init.go](file://internal/service/supplier/third_party/init.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [接口定义与统一标准](#接口定义与统一标准)
|
||||
2. [适配器实现与继承关系](#适配器实现与继承关系)
|
||||
3. [核心方法实现分析](#核心方法实现分析)
|
||||
4. [注册与依赖注入机制](#注册与依赖注入机制)
|
||||
5. [接口驱动设计优势](#接口驱动设计优势)
|
||||
6. [新增支付渠道指南](#新增支付渠道指南)
|
||||
|
||||
## 接口定义与统一标准
|
||||
|
||||
供应商集成架构的核心是 `PayInterface` 接口,它为所有第三方支付渠道提供了统一的接入标准。该接口定义了处理支付流程所需的关键方法,确保了不同供应商的集成具有一致性和可预测性。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PayInterface {
|
||||
<<interface>>
|
||||
+Scan(context.Context, order.OrderInfo, road.RoadInfo, merchant.MerchantInfo) ScanData
|
||||
+PayNotify()
|
||||
+PayQuery(order.OrderInfo, road.RoadInfo) bool
|
||||
+PayQueryV2(order.OrderInfo, road.RoadInfo) supply_model.MsgModel
|
||||
+PayFor(payfor.PayforInfo) string
|
||||
+PayForNotify() string
|
||||
+PayForQuery(payfor.PayforInfo) (string, string)
|
||||
+BalanceQuery(road.RoadInfo) float64
|
||||
+HasDependencyHTML() bool
|
||||
}
|
||||
class ScanData {
|
||||
+Supplier string
|
||||
+PayType string
|
||||
+OrderNo string
|
||||
+BankNo string
|
||||
+OrderPrice string
|
||||
+FactPrice string
|
||||
+Status string
|
||||
+PayUrl string
|
||||
+Msg string
|
||||
+ReturnData string
|
||||
+UpStreamOrderNo string
|
||||
}
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go#L26-L36)
|
||||
|
||||
**Section sources**
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go#L1-L37)
|
||||
|
||||
### 接口方法说明
|
||||
|
||||
`PayInterface` 接口定义了以下核心方法:
|
||||
|
||||
- **Scan**: 处理扫码支付请求,接收订单、通道和商户信息,返回支付所需数据
|
||||
- **PayNotify**: 处理第三方支付平台的异步回调通知
|
||||
- **PayQuery**: 查询支付订单状态,返回布尔值表示是否成功
|
||||
- **PayQueryV2**: 增强版支付查询,返回更详细的 `MsgModel` 状态信息
|
||||
- **PayFor**: 处理代付业务
|
||||
- **PayForNotify**: 处理代付回调
|
||||
- **PayForQuery**: 查询代付订单状态
|
||||
- **BalanceQuery**: 查询供应商账户余额
|
||||
- **HasDependencyHTML**: 判断是否需要依赖特定的支付页面
|
||||
|
||||
## 适配器实现与继承关系
|
||||
|
||||
各供应商通过实现 `PayInterface` 接口来接入系统,同时继承 `web.Controller` 以获得Web请求处理能力。这种设计模式遵循了适配器模式,将不同供应商的特定实现适配到统一的接口标准上。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class AiboCardImpl {
|
||||
+HasDependencyHTML() bool
|
||||
+generateSign(ctx context.Context, params map[string]any, key string) string
|
||||
+SendCard(ctx context.Context, jsonStr string, cardInfo supplier.RedeemCardInfo, attach string) (bool, string)
|
||||
+Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData
|
||||
+PayNotify()
|
||||
+PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool
|
||||
+PayQueryV2(orderInfo order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel
|
||||
+PayFor(info payfor.PayforInfo) string
|
||||
+PayForQuery(payFor payfor.PayforInfo) (string, string)
|
||||
+BalanceQuery(roadInfo road.RoadInfo) float64
|
||||
+PayForNotify() string
|
||||
}
|
||||
class JDCardImpl {
|
||||
+HasDependencyHTML() bool
|
||||
+SendCard(ctx context.Context, jsonStr string, cardInfo supplier.RedeemCardInfo, attach string, merchantId string) (bool, string)
|
||||
+Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData
|
||||
+PayNotify()
|
||||
+PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool
|
||||
+PayQueryV2(orderInfo order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel
|
||||
+PayFor(info payfor.PayforInfo) string
|
||||
+PayForQuery(payFor payfor.PayforInfo) (string, string)
|
||||
+BalanceQuery(roadInfo road.RoadInfo) float64
|
||||
+PayForNotify() string
|
||||
}
|
||||
class TMAllGameImpl {
|
||||
+HasDependencyHTML() bool
|
||||
+SendCard(ctx context.Context, jsonStr string, cardInfo supplier.CardInfo, orderInfo order.OrderInfo) (bool, string)
|
||||
+Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData
|
||||
+PayNotify()
|
||||
+PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool
|
||||
+PayQueryV2(orderInfo order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel
|
||||
+PayFor(info payfor.PayforInfo) string
|
||||
+PayForQuery(payFor payfor.PayforInfo) (string, string)
|
||||
+BalanceQuery(roadInfo road.RoadInfo) float64
|
||||
+PayForNotify() string
|
||||
}
|
||||
class WalMartImpl {
|
||||
+HasDependencyHTML() bool
|
||||
+verifyCardNo(cardNo string) bool
|
||||
+sendEncrypt(ci SendCardInfo, secret string) string
|
||||
+notifyEncrypt(ci ResponseStruct, secret string) string
|
||||
+SendCard(ctx context.Context, jsonStr string, cardInfo supplier.RedeemCardInfo, attach string) (bool, string)
|
||||
+Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData
|
||||
+PayNotify()
|
||||
+PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool
|
||||
+PayQueryV2(orderInfo order.OrderInfo, roadInfo road.RoadInfo) supply_model.MsgModel
|
||||
+PayFor(info payfor.PayforInfo) string
|
||||
+PayForQuery(payFor payfor.PayforInfo) (string, string)
|
||||
+BalanceQuery(roadInfo road.RoadInfo) float64
|
||||
+PayForNotify() string
|
||||
}
|
||||
class web.Controller {
|
||||
<<base class>>
|
||||
}
|
||||
AiboCardImpl --|> web.Controller
|
||||
JDCardImpl --|> web.Controller
|
||||
TMAllGameImpl --|> web.Controller
|
||||
WalMartImpl --|> web.Controller
|
||||
AiboCardImpl ..|> PayInterface
|
||||
JDCardImpl ..|> PayInterface
|
||||
TMAllGameImpl ..|> PayInterface
|
||||
WalMartImpl ..|> PayInterface
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go#L39-L41)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L36-L38)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L33-L35)
|
||||
- [walmart.go](file://internal/service/supplier/third_party/walmart.go#L35-L37)
|
||||
|
||||
**Section sources**
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go#L1-L482)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L1-L434)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L1-L448)
|
||||
- [walmart.go](file://internal/service/supplier/third_party/walmart.go#L1-L480)
|
||||
|
||||
### 继承与实现机制
|
||||
|
||||
所有供应商适配器都遵循相同的继承和实现模式:
|
||||
|
||||
1. **继承 `web.Controller`**: 通过嵌入 `web.Controller` 结构体,适配器获得了处理HTTP请求的能力,包括获取请求参数、设置响应等
|
||||
2. **实现 `PayInterface`**: 适配器必须实现 `PayInterface` 接口的所有方法,确保了统一的调用接口
|
||||
3. **特定逻辑封装**: 每个适配器包含供应商特定的辅助方法,如签名生成、数据加密等
|
||||
|
||||
这种设计实现了"组合优于继承"的原则,通过结构体嵌入(composition)而非传统继承来复用 `web.Controller` 的功能。
|
||||
|
||||
## 核心方法实现分析
|
||||
|
||||
### Scan 方法实现
|
||||
|
||||
`Scan` 方法是处理支付请求的核心,各供应商的实现具有相似的处理流程,但包含特定于供应商的细节。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> Unmarshal["反序列化 ExValue 数据"]
|
||||
Unmarshal --> Validate{"数据有效?"}
|
||||
Validate --> |否| ReturnError["返回错误: 订单有误"]
|
||||
Validate --> |是| CallSendCard["调用 SendCard 发起支付"]
|
||||
CallSendCard --> CheckResult{"支付成功?"}
|
||||
CheckResult --> |否| ReturnFail["返回失败信息"]
|
||||
CheckResult --> |是| BuildScanData["构建 ScanData 结果"]
|
||||
BuildScanData --> SetStatus["设置状态为 '00'"]
|
||||
SetStatus --> SetOrderInfo["设置订单号、金额等信息"]
|
||||
SetOrderInfo --> SetReturnData["设置返回数据"]
|
||||
SetReturnData --> ReturnSuccess["返回成功结果"]
|
||||
ReturnError --> End([结束])
|
||||
ReturnFail --> End
|
||||
ReturnSuccess --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go#L118-L149)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L114-L145)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L132-L165)
|
||||
- [walmart.go](file://internal/service/supplier/third_party/walmart.go#L188-L219)
|
||||
|
||||
**Section sources**
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go#L118-L149)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L114-L145)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L132-L165)
|
||||
- [walmart.go](file://internal/service/supplier/third_party/walmart.go#L188-L219)
|
||||
|
||||
#### 实现共性
|
||||
|
||||
所有 `Scan` 方法的实现都遵循以下通用流程:
|
||||
|
||||
1. **上下文与追踪**: 使用 `otelTrace.Span` 创建分布式追踪上下文,便于监控和调试
|
||||
2. **数据解析**: 从 `orderInfo.ExValue` 中解析出卡密信息
|
||||
3. **支付发起**: 调用供应商特定的 `SendCard` 方法发起支付请求
|
||||
4. **结果处理**: 根据支付结果构建并返回 `ScanData` 对象
|
||||
|
||||
#### 供应商特定差异
|
||||
|
||||
尽管流程相似,但各供应商的实现存在特定差异:
|
||||
|
||||
- **爱博 (Aibo)**: 使用MD5签名,通过 `generateSign` 方法生成签名
|
||||
- **京东卡 (JD)**: 需要传递商户ID作为附加参数
|
||||
- **天猫游戏 (TMallGame)**: 根据 `ProductUid` 区分确认收货和好评两种渠道
|
||||
- **沃尔玛 (Walmart)**: 实现了卡号验证功能,防止特定字符的卡密
|
||||
|
||||
### PayNotify 方法实现
|
||||
|
||||
`PayNotify` 方法处理第三方支付平台的异步回调,确保支付结果的准确性和安全性。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> GetOrder["根据订单号获取订单信息"]
|
||||
GetOrder --> ValidateOrder{"订单存在?"}
|
||||
ValidateOrder --> |否| WriteFail["响应 FAIL"]
|
||||
ValidateOrder --> |是| GetRoad["获取通道信息"]
|
||||
GetRoad --> ValidateRoad{"通道有效?"}
|
||||
ValidateRoad --> |否| WriteFail
|
||||
ValidateRoad --> |是| VerifySign["验证签名"]
|
||||
VerifySign --> ValidSign{"签名有效?"}
|
||||
ValidSign --> |否| WriteFail
|
||||
ValidSign --> |是| ProcessStatus["处理支付状态"]
|
||||
ProcessStatus --> CheckStatus{"支付成功?"}
|
||||
CheckStatus --> |是| SolveSuccess["调用 SolvePaySuccess"]
|
||||
CheckStatus --> |否| SolveFail["调用 SolvePayFail"]
|
||||
SolveSuccess --> WriteSuccess["响应 SUCCESS"]
|
||||
SolveFail --> WriteSuccess
|
||||
WriteFail --> End([结束])
|
||||
WriteSuccess --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go#L168-L260)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L165-L212)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L167-L223)
|
||||
- [walmart.go](file://internal/service/supplier/third_party/walmart.go#L233-L260)
|
||||
|
||||
**Section sources**
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go#L168-L260)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L165-L212)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L167-L223)
|
||||
- [walmart.go](file://internal/service/supplier/third_party/walmart.go#L233-L260)
|
||||
|
||||
#### 安全性保障
|
||||
|
||||
各供应商在 `PayNotify` 实现中都包含了严格的安全验证:
|
||||
|
||||
- **爱博**: 使用 `generateSign` 方法重新计算签名并与回调签名对比
|
||||
- **天猫游戏**: 使用 `TmpEncrypt` 方法生成签名进行验证
|
||||
- **沃尔玛**: 使用专门的 `notifyEncrypt` 方法生成签名进行验证
|
||||
- **京东卡**: 虽然没有显式签名验证,但通过订单号匹配确保安全性
|
||||
|
||||
### PayQuery 方法实现
|
||||
|
||||
`PayQuery` 方法用于主动查询支付订单状态,实现了一致的查询逻辑。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> GetCardData["从 CardReturnData 获取订单ID"]
|
||||
GetCardData --> BuildParams["构建查询参数"]
|
||||
BuildParams --> AddCommonParams["添加公共参数: app_key, timestamp"]
|
||||
AddCommonParams --> GenerateSign["生成签名"]
|
||||
GenerateSign --> CreateRequest["创建HTTP请求"]
|
||||
CreateRequest --> SendRequest["发送请求到查询接口"]
|
||||
SendRequest --> ParseResponse["解析响应"]
|
||||
ParseResponse --> CheckCode{"code == 0?"}
|
||||
CheckCode --> |否| ReturnFalse["返回 false"]
|
||||
CheckCode --> |是| CheckStatus{"status == 9?"}
|
||||
CheckStatus --> |是| ReturnTrue["返回 true"]
|
||||
CheckStatus --> |否| ReturnFalse
|
||||
ReturnTrue --> End([结束])
|
||||
ReturnFalse --> End
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go#L262-L323)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L214-L275)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L225-L287)
|
||||
- [walmart.go](file://internal/service/supplier/third_party/walmart.go#L262-L322)
|
||||
|
||||
**Section sources**
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go#L262-L323)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L214-L275)
|
||||
- [t_mall_game.go](file://internal/service/supplier/third_party/t_mall_game.go#L225-L287)
|
||||
- [walmart.go](file://internal/service/supplier/third_party/walmart.go#L262-L322)
|
||||
|
||||
#### 查询逻辑一致性
|
||||
|
||||
所有供应商的 `PayQuery` 实现都表现出高度的一致性:
|
||||
|
||||
1. 使用相同的查询URL (`GetMFCardQueryUrl`)
|
||||
2. 使用相同的参数结构 (`order_id`, `app_key`, `timestamp`, `sign`)
|
||||
3. 使用相同的签名生成方法 (`GetMD5SignMF`)
|
||||
4. 使用相同的成功判断标准 (`code == 0 && status == 9`)
|
||||
|
||||
这种一致性大大简化了维护和扩展工作。
|
||||
|
||||
## 注册与依赖注入机制
|
||||
|
||||
系统通过注册表模式管理所有供应商适配器,实现了灵活的依赖注入和运行时查找。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class registerSupplier {
|
||||
<<singleton>>
|
||||
-registerSupplier map[string]supplier.PayInterface
|
||||
+GetPaySupplierByCode(code string) supplier.PayInterface
|
||||
}
|
||||
class supplierCode2Name {
|
||||
<<singleton>>
|
||||
-supplierCode2Name map[string]string
|
||||
}
|
||||
class init {
|
||||
+init()
|
||||
}
|
||||
init --> registerSupplier : "注册"
|
||||
init --> supplierCode2Name : "初始化"
|
||||
registerSupplier ..> PayInterface : "持有"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [init.go](file://internal/service/supplier/third_party/init.go#L151-L153)
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/service/supplier/third_party/init.go#L1-L197)
|
||||
|
||||
### 注册机制
|
||||
|
||||
供应商的注册在 `init.go` 文件的 `init()` 函数中完成,该函数在包初始化时自动执行:
|
||||
|
||||
```go
|
||||
func init() {
|
||||
registerSupplier["JD"] = new(JDCardImpl)
|
||||
registerSupplier["APPLE"] = new(AppleCardImpl)
|
||||
registerSupplier["MF178"] = new(MFCardV2Impl)
|
||||
// ... 其他供应商注册
|
||||
}
|
||||
```
|
||||
|
||||
这种设计确保了所有供应商在应用启动时就被正确注册,避免了运行时的初始化问题。
|
||||
|
||||
### 依赖注入方式
|
||||
|
||||
系统通过 `GetPaySupplierByCode` 函数提供依赖注入功能:
|
||||
|
||||
```go
|
||||
func GetPaySupplierByCode(code string) supplier.PayInterface {
|
||||
return registerSupplier[code]
|
||||
}
|
||||
```
|
||||
|
||||
这种基于代码的查找机制具有以下优势:
|
||||
|
||||
- **松耦合**: 调用方不需要知道具体的实现类型
|
||||
- **可扩展**: 添加新供应商只需在 `init()` 函数中注册,无需修改调用代码
|
||||
- **运行时灵活性**: 可以根据配置动态选择供应商
|
||||
|
||||
## 接口驱动设计优势
|
||||
|
||||
基于 `PayInterface` 的供应商集成架构带来了显著的优势,特别是在可扩展性、可维护性和测试便利性方面。
|
||||
|
||||
### 可扩展性
|
||||
|
||||
接口驱动设计使得新增支付渠道变得简单而安全:
|
||||
|
||||
1. **标准化接入**: 所有新供应商必须实现相同的接口,确保了系统的一致性
|
||||
2. **最小化影响**: 新增供应商不会影响现有代码,遵循开闭原则
|
||||
3. **快速集成**: 开发者只需关注特定供应商的实现细节,无需修改核心逻辑
|
||||
|
||||
### 可维护性
|
||||
|
||||
接口抽象大大提高了代码的可维护性:
|
||||
|
||||
1. **关注点分离**: 每个适配器只负责特定供应商的逻辑,职责清晰
|
||||
2. **易于调试**: 统一的接口使得问题定位更加容易
|
||||
3. **文档化契约**: 接口本身作为契约,明确了各组件的交互方式
|
||||
|
||||
### 测试便利性
|
||||
|
||||
接口设计为单元测试和集成测试提供了便利:
|
||||
|
||||
1. **可模拟性**: 可以轻松创建接口的模拟实现进行测试
|
||||
2. **隔离测试**: 可以独立测试每个适配器,无需依赖真实供应商
|
||||
3. **回归测试**: 修改一个适配器不会影响其他适配器的测试
|
||||
|
||||
## 新增支付渠道指南
|
||||
|
||||
### 标准步骤
|
||||
|
||||
新增支付渠道需要遵循以下标准步骤:
|
||||
|
||||
1. **创建适配器文件**: 在 `internal/service/supplier/third_party/` 目录下创建新的 `.go` 文件
|
||||
2. **定义结构体**: 创建新的结构体并嵌入 `web.Controller`
|
||||
3. **实现接口**: 实现 `PayInterface` 的所有方法
|
||||
4. **添加辅助方法**: 实现供应商特定的签名、加密等辅助方法
|
||||
5. **注册适配器**: 在 `init.go` 的 `init()` 函数中注册新适配器
|
||||
6. **更新映射**: 在 `supplierCode2Name` 映射中添加供应商名称
|
||||
|
||||
### 代码示例
|
||||
|
||||
```go
|
||||
// 1. 定义结构体
|
||||
type NewSupplierImpl struct {
|
||||
web.Controller
|
||||
}
|
||||
|
||||
// 2. 实现接口方法
|
||||
func (c *NewSupplierImpl) Scan(ctx context.Context, orderInfo order.OrderInfo, roadInfo road.RoadInfo, merchantInfo merchant.MerchantInfo) supplier.ScanData {
|
||||
// 实现扫码支付逻辑
|
||||
}
|
||||
|
||||
func (c *NewSupplierImpl) PayNotify() {
|
||||
// 实现回调处理逻辑
|
||||
}
|
||||
|
||||
func (c *NewSupplierImpl) PayQuery(orderInfo order.OrderInfo, roadInfo road.RoadInfo) bool {
|
||||
// 实现查询逻辑
|
||||
}
|
||||
|
||||
// 3. 在 init() 函数中注册
|
||||
func init() {
|
||||
registerSupplier["NEW_SUPPLIER"] = new(NewSupplierImpl)
|
||||
}
|
||||
```
|
||||
|
||||
通过遵循这些步骤,可以确保新支付渠道的集成既快速又可靠,同时保持系统的整体一致性和稳定性。
|
||||
183
.qoder/repowiki/zh/content/核心架构/支付服务架构.md
Normal file
183
.qoder/repowiki/zh/content/核心架构/支付服务架构.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# 支付服务架构
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [pay_service.go](file://internal/service/pay_service.go)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go)
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go)
|
||||
- [order_info.go](file://internal/models/order/order_info.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [核心组件](#核心组件)
|
||||
3. [支付请求处理流程](#支付请求处理流程)
|
||||
4. [支付成功处理机制](#支付成功处理机制)
|
||||
5. [金额不一致处理逻辑](#金额不一致处理逻辑)
|
||||
6. [数据交互模式](#数据交互模式)
|
||||
7. [异常处理与重试机制](#异常处理与重试机制)
|
||||
8. [幂等性保障](#幂等性保障)
|
||||
|
||||
## 简介
|
||||
本文档详细阐述了支付服务的架构设计,重点分析了支付请求从扫码控制器进入系统后的完整处理流程。文档聚焦于`pay_service.go`和`pay_solve.go`的实现机制,涵盖了订单创建、状态管理、金额校验和结果回调等核心环节。
|
||||
|
||||
**Section sources**
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L1-L50)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L1-L50)
|
||||
|
||||
## 核心组件
|
||||
|
||||
支付服务的核心组件包括扫码控制器(`scan_controller.go`)、支付服务(`pay_service.go`)、支付解决方案(`pay_solve.go`)以及订单信息模型(`order_info.go`)。这些组件协同工作,实现了完整的支付处理流程。
|
||||
|
||||
**Section sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L1-L50)
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L1-L50)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L1-L50)
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L1-L50)
|
||||
|
||||
## 支付请求处理流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant ScanController as "扫码控制器"
|
||||
participant PayService as "支付服务"
|
||||
participant OrderModel as "订单模型"
|
||||
participant PaySolve as "支付解决方案"
|
||||
Client->>ScanController : 发送扫码支付请求
|
||||
ScanController->>PayService : 验证商户信息
|
||||
PayService->>ScanController : 返回验证结果
|
||||
ScanController->>PayService : 选择支付通道
|
||||
PayService->>ScanController : 返回通道信息
|
||||
ScanController->>PayService : 生成订单记录
|
||||
PayService->>OrderModel : 创建订单
|
||||
OrderModel-->>PayService : 订单创建成功
|
||||
PayService-->>ScanController : 订单信息
|
||||
ScanController->>PaySolve : 处理支付成功
|
||||
PaySolve->>OrderModel : 更新订单状态
|
||||
OrderModel-->>PaySolve : 状态更新成功
|
||||
PaySolve->>ScanController : 处理结果
|
||||
ScanController->>Client : 返回支付结果
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L69-L316)
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L305-L359)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L37-L195)
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L370-L376)
|
||||
|
||||
**Section sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L69-L316)
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L305-L359)
|
||||
|
||||
## 支付成功处理机制
|
||||
|
||||
当支付成功时,系统通过`SolvePaySuccess`函数处理相关业务逻辑。该函数在一个数据库事务中执行,确保数据的一致性。处理流程包括更新订单状态、计算并更新账户余额、记录动账信息以及更新通道统计信息。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> CheckOrder["检查订单是否存在"]
|
||||
CheckOrder --> OrderExists{"订单存在?"}
|
||||
OrderExists --> |否| ReturnError["返回错误"]
|
||||
OrderExists --> |是| CheckStatus["检查订单状态"]
|
||||
CheckStatus --> IsSuccess{"已是成功状态?"}
|
||||
IsSuccess --> |是| ReturnSuccess["返回成功"]
|
||||
IsSuccess --> |否| UpdateOrder["更新订单信息"]
|
||||
UpdateOrder --> UpdateProfit["更新订单利润"]
|
||||
UpdateProfit --> UpdateAccount["更新账户余额"]
|
||||
UpdateAccount --> InsertHistory["插入动账记录"]
|
||||
InsertHistory --> UpdateRoad["更新通道信息"]
|
||||
UpdateRoad --> End([结束])
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L37-L195)
|
||||
|
||||
**Section sources**
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L37-L195)
|
||||
|
||||
## 金额不一致处理逻辑
|
||||
|
||||
当实际支付金额与订单金额不一致时,系统通过`SolvePaySuccessByAmountDifferent`函数处理此情况。该函数首先检查通道是否允许金额不一致的重发,然后根据重发次数决定是否继续处理。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> CheckAllow["检查通道是否允许金额不一致"]
|
||||
CheckAllow --> IsAllowed{"允许?"}
|
||||
IsAllowed --> |否| FailPayment["支付失败"]
|
||||
IsAllowed --> |是| CheckCount["检查重发次数"]
|
||||
CheckCount --> IsMax{"已达最大次数?"}
|
||||
IsMax --> |是| FailPayment
|
||||
IsMax --> |否| UpdateCount["更新重发次数"]
|
||||
UpdateCount --> UpdateAmount["更新订单金额"]
|
||||
UpdateAmount --> UpdateRecord["更新发送记录"]
|
||||
UpdateRecord --> SetWait["设置等待状态"]
|
||||
SetWait --> CallScan["调用Scan方法"]
|
||||
CallScan --> End([结束])
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [pay_solve.go](file://internal/service/supplier/third_party/init.go#L173-L195)
|
||||
|
||||
**Section sources**
|
||||
- [pay_solve.go](file://internal/service/supplier/third_party/init.go#L173-L195)
|
||||
|
||||
## 数据交互模式
|
||||
|
||||
服务层与模型层通过上下文(`context`)传递分布式追踪信息,确保整个请求链路的可追踪性。数据交互主要通过ORM操作完成,包括订单的创建、查询和更新。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class ScanController {
|
||||
+Scan()
|
||||
+SolveFailJSON()
|
||||
}
|
||||
class PayService {
|
||||
+GenerateRecord()
|
||||
+ChooseRoadV2()
|
||||
+GenerateSuccessData()
|
||||
}
|
||||
class PaySolve {
|
||||
+SolvePaySuccess()
|
||||
+SolvePayFail()
|
||||
+SolvePaySuccessByAmountDifferent()
|
||||
}
|
||||
class OrderInfo {
|
||||
+BankOrderId
|
||||
+OrderAmount
|
||||
+FactAmount
|
||||
+Status
|
||||
+UpdateOrderStatus()
|
||||
+InsertOrder()
|
||||
}
|
||||
ScanController --> PayService : "调用"
|
||||
PayService --> PaySolve : "调用"
|
||||
PaySolve --> OrderInfo : "数据操作"
|
||||
PayService --> OrderInfo : "数据操作"
|
||||
```
|
||||
|
||||
**Diagram sources **
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L69-L316)
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L305-L359)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L37-L195)
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L370-L376)
|
||||
|
||||
**Section sources**
|
||||
- [scan_controller.go](file://internal/controllers/scan_controller.go#L69-L316)
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L305-L359)
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L37-L195)
|
||||
- [order_info.go](file://internal/models/order/order_info.go#L370-L376)
|
||||
|
||||
## 异常处理与重试机制
|
||||
|
||||
系统实现了完善的异常处理机制,通过`SolvePayFail`函数处理支付失败的情况。该函数会更新订单状态为失败,并记录失败原因。同时,系统使用异步任务池处理回调通知,避免阻塞主流程。
|
||||
|
||||
**Section sources**
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L198-L254)
|
||||
|
||||
## 幂等性保障
|
||||
|
||||
为了确保操作的幂等性,系统在关键操作上采用了数据库事务和状态检查机制。例如,在处理支付成功时,会先检查订单是否已经是成功状态,避免重复处理。
|
||||
|
||||
**Section sources**
|
||||
- [pay_solve.go](file://internal/service/pay_solve.go#L37-L195)
|
||||
237
.qoder/repowiki/zh/content/核心架构/核心架构.md
Normal file
237
.qoder/repowiki/zh/content/核心架构/核心架构.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# 核心架构
|
||||
|
||||
<cite>
|
||||
**本文档中引用的文件**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go)
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go)
|
||||
- [payfor_info.go](file://internal/models/payfor/payfor_info.go)
|
||||
- [order_info.go](file://internal/models/order/order_info.go)
|
||||
- [road_info.go](file://internal/models/road/road_info.go)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go)
|
||||
- [account.go](file://internal/models/accounts/account.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概述](#架构概述)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
本文档旨在深入分析支付服务(payfor_service.go)和支付解决方案(payfor_solve.go)的实现,解释它们如何协同工作来处理支付请求。文档详细描述了供应商接口(supplier_interface.go)的设计模式,这是一个关键的扩展点,允许系统集成多种第三方支付渠道(如Apple、京东卡、沃尔玛)。同时,文档说明了系统如何通过实现此接口来添加新的支付供应商,并涵盖订单池和任务队列的处理机制。
|
||||
|
||||
## 项目结构
|
||||
本项目采用分层架构设计,主要分为配置、部署、内部服务和模型等目录。核心业务逻辑位于`internal`目录下,包括缓存、配置、常量、控制器、数据传输对象(DTO)、模型、OpenTelemetry追踪、代理、路由、模式、服务、Swagger文档、任务和工具等子模块。支付相关的核心逻辑集中在`internal/service/pay_for`和`internal/service/supplier`目录中。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "核心服务"
|
||||
PayForService[payfor_service.go]
|
||||
PayForSolve[payfor_solve.go]
|
||||
SupplierInterface[supplier_interface.go]
|
||||
end
|
||||
subgraph "第三方供应商实现"
|
||||
Aibo[aibo.go]
|
||||
Jd[jd.go]
|
||||
Apple[apple.go]
|
||||
Walmart[walmart.go]
|
||||
end
|
||||
subgraph "数据模型"
|
||||
PayforInfo[payfor_info.go]
|
||||
OrderInfo[order_info.go]
|
||||
RoadInfo[road_info.go]
|
||||
MerchantInfo[merchant_info.go]
|
||||
Account[account.go]
|
||||
end
|
||||
PayForService --> SupplierInterface
|
||||
PayForSolve --> PayforInfo
|
||||
SupplierInterface --> Aibo
|
||||
SupplierInterface --> Jd
|
||||
PayForService --> PayforInfo
|
||||
PayforInfo --> Account
|
||||
PayForService --> MerchantInfo
|
||||
PayForService --> RoadInfo
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go)
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go)
|
||||
- [payfor_info.go](file://internal/models/payfor/payfor_info.go)
|
||||
- [order_info.go](file://internal/models/order/order_info.go)
|
||||
- [road_info.go](file://internal/models/road/road_info.go)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go)
|
||||
- [account.go](file://internal/models/accounts/account.go)
|
||||
|
||||
**本节来源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go)
|
||||
|
||||
## 核心组件
|
||||
支付服务(payfor_service.go)负责处理支付请求的入口逻辑,包括签名验证、金额检查、订单重复性检查和支付通道选择。支付解决方案(payfor_solve.go)则负责处理支付结果的更新,包括支付成功和支付失败的处理。供应商接口(supplier_interface.go)定义了所有第三方支付供应商必须实现的方法,确保了系统的可扩展性和一致性。
|
||||
|
||||
**本节来源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go#L1-L330)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go#L1-L144)
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go#L1-L37)
|
||||
|
||||
## 架构概述
|
||||
系统采用接口驱动的设计模式,通过定义清晰的接口来解耦核心业务逻辑和第三方支付供应商的实现。这种设计模式带来了显著的可维护性和可扩展性优势。当需要添加新的支付供应商时,只需实现供应商接口并注册到系统中,而无需修改核心业务逻辑。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Client[客户端] --> PayForService[支付服务]
|
||||
PayForService --> PayForSolve[支付解决方案]
|
||||
PayForService --> SupplierInterface[供应商接口]
|
||||
SupplierInterface --> Aibo[爱博支付]
|
||||
SupplierInterface --> Jd[京东卡支付]
|
||||
SupplierInterface --> Apple[苹果支付]
|
||||
SupplierInterface --> Walmart[沃尔玛支付]
|
||||
PayForSolve --> Database[(数据库)]
|
||||
PayForService --> Database
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go)
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go)
|
||||
|
||||
## 详细组件分析
|
||||
### 支付服务分析
|
||||
支付服务组件负责处理支付请求的完整生命周期,从接收请求到最终的支付结果查询。它首先验证请求的签名和参数,然后检查订单的重复性,最后选择合适的支付通道并发起支付请求。
|
||||
|
||||
#### 支付服务类图
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PayForService {
|
||||
+AutoPayFor(ctx, params, giveType) PayForResponse
|
||||
+findPayForRoad(ctx, p) bool
|
||||
+MerchantSelf(ctx, p) bool
|
||||
+SendPayFor(ctx, p) bool
|
||||
+PayForResultQuery(ctx, params) string
|
||||
+BalanceQuery(ctx, params) string
|
||||
+checkSettAmount(ctx, settAmount) (bool, string)
|
||||
}
|
||||
class PayForSolve {
|
||||
+PayForFail(ctx, p) bool
|
||||
+PayForSuccess(ctx, p) bool
|
||||
}
|
||||
class SupplierInterface {
|
||||
<<interface>>
|
||||
+Scan(ctx, orderInfo, roadInfo, merchantInfo) ScanData
|
||||
+PayNotify()
|
||||
+PayQuery(orderInfo, roadInfo) bool
|
||||
+PayQueryV2(orderInfo, roadInfo) MsgModel
|
||||
+PayFor(payforInfo) string
|
||||
+PayForNotify() string
|
||||
+PayForQuery(payforInfo) (string, string)
|
||||
+BalanceQuery(roadInfo) float64
|
||||
+HasDependencyHTML() bool
|
||||
}
|
||||
PayForService --> PayForSolve : "使用"
|
||||
PayForService --> SupplierInterface : "依赖"
|
||||
SupplierInterface <|-- AiboImpl : "实现"
|
||||
SupplierInterface <|-- JdImpl : "实现"
|
||||
SupplierInterface <|-- AppleImpl : "实现"
|
||||
SupplierInterface <|-- WalmartImpl : "实现"
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go#L1-L330)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go#L1-L144)
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go#L1-L37)
|
||||
|
||||
### 供应商接口分析
|
||||
供应商接口是系统的关键扩展点,它定义了所有第三方支付供应商必须实现的方法。通过这种接口驱动的设计,系统可以轻松集成新的支付渠道,而无需修改核心业务逻辑。
|
||||
|
||||
#### 供应商接口序列图
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant PayForService as "支付服务"
|
||||
participant Supplier as "供应商接口"
|
||||
participant Aibo as "爱博实现"
|
||||
participant Jd as "京东卡实现"
|
||||
Client->>PayForService : 发起支付请求
|
||||
PayForService->>PayForService : 验证签名和参数
|
||||
PayForService->>PayForService : 检查订单重复性
|
||||
PayForService->>Supplier : 选择支付通道
|
||||
Supplier->>Aibo : 调用Scan方法
|
||||
Aibo-->>Supplier : 返回支付二维码
|
||||
Supplier-->>PayForService : 返回支付结果
|
||||
PayForService-->>Client : 返回支付响应
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go#L1-L330)
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go#L1-L37)
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go#L1-L481)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L1-L433)
|
||||
|
||||
**本节来源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go#L1-L330)
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go#L1-L37)
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go#L1-L481)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L1-L433)
|
||||
|
||||
## 依赖分析
|
||||
系统通过清晰的依赖关系实现了高内聚低耦合的设计。支付服务依赖于支付解决方案和供应商接口,而具体的供应商实现则依赖于供应商接口。数据模型层为所有服务提供数据支持,确保了数据的一致性和完整性。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
PayForService --> PayForSolve
|
||||
PayForService --> SupplierInterface
|
||||
PayForService --> PayforInfo
|
||||
PayForService --> MerchantInfo
|
||||
PayForService --> RoadInfo
|
||||
PayForService --> Account
|
||||
PayForSolve --> PayforInfo
|
||||
PayForSolve --> Account
|
||||
SupplierInterface --> Aibo
|
||||
SupplierInterface --> Jd
|
||||
SupplierInterface --> Apple
|
||||
SupplierInterface --> Walmart
|
||||
PayforInfo --> Account
|
||||
```
|
||||
|
||||
**图源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go)
|
||||
- [aibo.go](file://internal/service/supplier/third_party/aibo.go)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go)
|
||||
- [payfor_info.go](file://internal/models/payfor/payfor_info.go)
|
||||
- [merchant_info.go](file://internal/models/merchant/merchant_info.go)
|
||||
- [road_info.go](file://internal/models/road/road_info.go)
|
||||
- [account.go](file://internal/models/accounts/account.go)
|
||||
|
||||
**本节来源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go)
|
||||
|
||||
## 性能考虑
|
||||
系统在设计时充分考虑了性能因素。通过使用数据库事务确保数据一致性,采用缓存机制减少数据库访问,以及使用异步处理提高响应速度。此外,系统还实现了支付通道的轮询机制,确保在高并发场景下的稳定性和可靠性。
|
||||
|
||||
## 故障排除指南
|
||||
当支付请求失败时,首先检查请求的签名是否正确,然后验证支付金额是否符合要求。如果问题仍然存在,检查订单是否重复,以及支付通道是否可用。对于第三方支付供应商的问题,需要检查供应商的API文档和状态码说明。
|
||||
|
||||
**本节来源**
|
||||
- [payfor_service.go](file://internal/service/pay_for/payfor_service.go#L1-L330)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go#L1-L144)
|
||||
|
||||
## 结论
|
||||
本文档详细分析了支付服务和支付解决方案的实现,以及供应商接口的设计模式。通过接口驱动的设计,系统实现了高度的可扩展性和可维护性。未来可以进一步优化支付通道的选择算法,提高支付成功率,并增强系统的监控和告警能力。
|
||||
180
.qoder/repowiki/zh/content/监控与故障排除.md
Normal file
180
.qoder/repowiki/zh/content/监控与故障排除.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# 监控与故障排除
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [logs.go](file://internal/otelTrace/logs.go)
|
||||
- [init.go](file://internal/otelTrace/init.go)
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go)
|
||||
- [simple.go](file://internal/otelTrace/simple.go)
|
||||
- [consts.go](file://internal/otelTrace/consts.go)
|
||||
- [utils.go](file://internal/otelTrace/utils.go)
|
||||
- [span.go](file://internal/otelTrace/span.go)
|
||||
- [pay_solve_test.go](file://internal/service/pay_solve_test.go)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go)
|
||||
- [pay_service.go](file://internal/service/pay_service.go)
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go)
|
||||
- [router.go](file://internal/routers/router.go)
|
||||
- [metrics.go](file://internal/service/supplier/third_party/pool/metrics.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [分布式追踪](#分布式追踪)
|
||||
2. [日志诊断](#日志诊断)
|
||||
3. [关键指标与Prometheus集成](#关键指标与prometheus集成)
|
||||
4. [常见问题排查](#常见问题排查)
|
||||
5. [性能监控与优化](#性能监控与优化)
|
||||
6. [告警配置](#告警配置)
|
||||
|
||||
## 分布式追踪
|
||||
|
||||
本系统使用OpenTelemetry(otelTrace包)实现分布式追踪,通过`otelTrace.InitTracer()`函数初始化追踪器,配置了生产环境优化的Trace导出器、资源标识、采样策略和批量处理器。追踪器使用gRPC协议将数据发送到指定的收集器,支持gzip压缩以减少网络传输量。
|
||||
|
||||
系统通过`otelTrace.Middleware`中间件实现请求级别的追踪。该中间件在请求处理开始时创建一个Span,从请求头中提取上游的trace context,并注入到当前context中。Span记录了HTTP方法、URL、状态码、响应大小、处理时长等关键性能指标。当请求处理时间超过5秒时,会自动标记为慢请求并记录警告日志。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant Middleware as "追踪中间件"
|
||||
participant Span as "Span"
|
||||
participant Exporter as "导出器"
|
||||
Client->>Middleware : 发起HTTP请求
|
||||
Middleware->>Span : 创建Span并提取上下文
|
||||
Span->>Span : 记录HTTP基础信息
|
||||
Middleware->>Client : 处理业务逻辑
|
||||
Span->>Span : 记录响应状态码和处理时长
|
||||
Span->>Exporter : 结束Span并导出
|
||||
Exporter->>Collector : 发送追踪数据
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L21-L140)
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L1-L257)
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L1-L142)
|
||||
|
||||
## 日志诊断
|
||||
|
||||
系统使用`otelTrace.Logger`进行日志记录,该Logger集成了OpenTelemetry的日志导出功能。日志信息不仅会输出到标准输出,还会通过OTLP协议发送到日志收集器,实现日志的集中管理和分析。
|
||||
|
||||
`otelTrace.logs.go`文件定义了`CustomLogger`结构体,它包装了Zap日志库,并提供了`WithContext`方法,可以将OpenTelemetry的context与日志记录关联起来。这使得在查看日志时,可以轻松地关联到相应的追踪信息,实现日志与追踪的联动分析。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class CustomLogger {
|
||||
+logger *zap.Logger
|
||||
+WithContext(ctx context.Context) *zap.Logger
|
||||
}
|
||||
class Logger {
|
||||
+CustomLogger
|
||||
}
|
||||
Logger --> CustomLogger : "全局实例"
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [logs.go](file://internal/otelTrace/logs.go#L1-L29)
|
||||
- [init.go](file://internal/otelTrace/init.go#L249-L256)
|
||||
|
||||
**Section sources**
|
||||
- [logs.go](file://internal/otelTrace/logs.go#L1-L29)
|
||||
- [init.go](file://internal/otelTrace/init.go#L249-L256)
|
||||
|
||||
## 关键指标与Prometheus集成
|
||||
|
||||
系统通过`otelTrace.InitTracer()`函数配置了Metrics导出器,将关键指标通过OTLP协议发送到收集器。同时,`internal/service/supplier/third_party/pool/metrics.go`文件定义了`Metrics`结构体,用于监控订单处理相关的指标。
|
||||
|
||||
这些指标包括:
|
||||
- `order_pool_processed_total`: 已处理的订单总数
|
||||
- `order_pool_failed_total`: 处理失败的订单总数
|
||||
- `order_pool_processing_seconds`: 订单处理时间分布
|
||||
- `order_pool_size`: 当前订单池大小
|
||||
- `order_queue_length`: 当前订单队列长度
|
||||
- `order_query_seconds`: 订单查询时间分布
|
||||
- `order_query_failed_total`: 订单查询失败总数
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[业务代码] --> B[记录指标]
|
||||
B --> C{指标类型}
|
||||
C --> |计数器| D[OrderProcessed]
|
||||
C --> |计数器| E[OrderFailed]
|
||||
C --> |直方图| F[ProcessingTime]
|
||||
C --> |仪表盘| G[PoolSize]
|
||||
C --> |仪表盘| H[OrderQueueLength]
|
||||
D --> I[Prometheus]
|
||||
E --> I
|
||||
F --> I
|
||||
G --> I
|
||||
H --> I
|
||||
I --> J[监控面板]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [metrics.go](file://internal/service/supplier/third_party/pool/metrics.go#L1-L163)
|
||||
|
||||
**Section sources**
|
||||
- [init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
- [metrics.go](file://internal/service/supplier/third_party/pool/metrics.go#L1-L163)
|
||||
|
||||
## 常见问题排查
|
||||
|
||||
### 支付请求超时
|
||||
|
||||
当支付请求超时时,首先检查`otelTrace.Middleware`中记录的Span,查看处理时长是否超过5秒。如果超过,说明存在慢请求。可以通过以下步骤排查:
|
||||
|
||||
1. 检查数据库连接池是否耗尽,查看`order_pool_size`和`order_queue_length`指标。
|
||||
2. 检查Redis连接是否正常,查看`TotalRedisErrors`指标。
|
||||
3. 检查上游供应商接口是否响应缓慢,查看`order_query_seconds`指标。
|
||||
|
||||
### 回调失败
|
||||
|
||||
当回调失败时,首先检查`order_controller.go`中的`OrderUpdate`方法。该方法处理手动修正订单状态的请求。可以通过以下步骤排查:
|
||||
|
||||
1. 检查`bankOrderId`是否正确,通过`order.GetOrderByBankOrderId`查询订单信息。
|
||||
2. 检查`solveType`是否正确,确保是`SUCCESS`或`FAIL`。
|
||||
3. 检查`service.SolvePaySuccess`或`service.SolvePayFail`方法的执行结果。
|
||||
|
||||
### 数据库连接池耗尽
|
||||
|
||||
当数据库连接池耗尽时,系统会记录相关错误日志。可以通过以下步骤排查:
|
||||
|
||||
1. 检查`otelTrace.Logger`中的错误日志,查找`database connection pool exhausted`等关键词。
|
||||
2. 检查`order_pool_size`和`order_queue_length`指标,查看订单池和队列的大小。
|
||||
3. 检查`CurrentChannelConcurrency`、`CurrentRoadConcurrency`和`CurrentFaceValueConcurrency`指标,查看当前并发数。
|
||||
|
||||
**Section sources**
|
||||
- [order_controller.go](file://internal/controllers/order_controller.go#L1-L226)
|
||||
- [payfor_solve.go](file://internal/service/pay_for/payfor_solve.go#L1-L144)
|
||||
- [pay_service.go](file://internal/service/pay_service.go#L1-L448)
|
||||
|
||||
## 性能监控与优化
|
||||
|
||||
系统通过`otelTrace.Middleware`和`Metrics`结构体实现了全面的性能监控。关键性能指标包括HTTP响应时间、订单处理时间、数据库查询时间等。
|
||||
|
||||
对于慢查询的识别,系统在`otelTrace.Middleware`中设置了5秒的阈值。当请求处理时间超过5秒时,会自动记录警告日志,并标记为慢请求。开发者可以通过分析这些日志,定位性能瓶颈。
|
||||
|
||||
优化建议:
|
||||
1. 优化数据库查询,添加必要的索引。
|
||||
2. 使用缓存减少数据库访问。
|
||||
3. 优化上游供应商接口调用,减少网络延迟。
|
||||
4. 调整订单池大小和队列长度,平衡性能和资源使用。
|
||||
|
||||
**Section sources**
|
||||
- [middleware.go](file://internal/otelTrace/middleware.go#L21-L140)
|
||||
- [metrics.go](file://internal/service/supplier/third_party/pool/metrics.go#L1-L163)
|
||||
|
||||
## 告警配置
|
||||
|
||||
系统通过`otelTrace.monitorExporterHealth`函数实现了导出器健康状态的监控。该函数每30秒检查一次导出失败次数,如果失败次数超过10次,会记录警告日志。
|
||||
|
||||
告警规则建议:
|
||||
1. 当`exportFailures`超过10次时,发送告警通知。
|
||||
2. 当`order_query_failed_total`在5分钟内增加超过100次时,发送告警通知。
|
||||
3. 当`order_pool_processing_seconds`的95分位数超过5秒时,发送告警通知。
|
||||
4. 当`order_pool_size`接近最大值时,发送告警通知。
|
||||
|
||||
**Section sources**
|
||||
- [utils.go](file://internal/otelTrace/utils.go#L58-L60)
|
||||
- [metrics.go](file://internal/service/supplier/third_party/pool/metrics.go#L1-L163)
|
||||
315
.qoder/repowiki/zh/content/第三方支付渠道集成.md
Normal file
315
.qoder/repowiki/zh/content/第三方支付渠道集成.md
Normal file
@@ -0,0 +1,315 @@
|
||||
# 第三方支付渠道集成
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go)
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go)
|
||||
- [apple_shark.go](file://internal/service/supplier/third_party/apple_shark.go)
|
||||
- [batch_six.go](file://internal/service/supplier/third_party/batch_six.go)
|
||||
- [eggplant.go](file://internal/service/supplier/third_party/eggplant.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [介绍](#介绍)
|
||||
2. [核心接口定义](#核心接口定义)
|
||||
3. [核心方法实现分析](#核心方法实现分析)
|
||||
4. [HTTP请求与响应处理](#http请求与响应处理)
|
||||
5. [错误处理与重试机制](#错误处理与重试机制)
|
||||
6. [供应商认证机制](#供应商认证机制)
|
||||
7. [API限流与数据格式](#api限流与数据格式)
|
||||
8. [新供应商集成步骤](#新供应商集成步骤)
|
||||
9. [单元测试编写](#单元测试编写)
|
||||
10. [常见陷阱与最佳实践](#常见陷阱与最佳实践)
|
||||
|
||||
## 介绍
|
||||
本文档旨在为开发者提供一份详细的第三方支付渠道集成指南。通过分析现有支付供应商(如Apple、京东)的实现,指导开发者如何创建新的支付供应商集成。文档涵盖了从接口实现、HTTP通信、错误处理到系统注册的完整流程。
|
||||
|
||||
## 核心接口定义
|
||||
所有第三方支付供应商必须实现`PayInterface`接口,该接口定义了支付系统所需的核心功能。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class PayInterface {
|
||||
+Scan(context.Context, order.OrderInfo, road.RoadInfo, merchant.MerchantInfo) ScanData
|
||||
+PayNotify()
|
||||
+PayQuery(order.OrderInfo, road.RoadInfo) bool
|
||||
+PayQueryV2(order.OrderInfo, road.RoadInfo) supply_model.MsgModel
|
||||
+PayFor(payfor.PayforInfo) string
|
||||
+PayForNotify() string
|
||||
+PayForQuery(payfor.PayforInfo) (string, string)
|
||||
+BalanceQuery(road.RoadInfo) float64
|
||||
+HasDependencyHTML() bool
|
||||
}
|
||||
class ScanData {
|
||||
+Supplier string
|
||||
+PayType string
|
||||
+OrderNo string
|
||||
+BankNo string
|
||||
+OrderPrice string
|
||||
+FactPrice string
|
||||
+Status string
|
||||
+PayUrl string
|
||||
+Msg string
|
||||
+ReturnData string
|
||||
+UpStreamOrderNo string
|
||||
}
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go#L26-L36)
|
||||
|
||||
**本节来源**
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go#L1-L37)
|
||||
|
||||
## 核心方法实现分析
|
||||
### Scan方法实现
|
||||
`Scan`方法是支付流程的起点,负责处理扫码支付请求。通过对比Apple和京东的实现,可以发现通用的处理模式。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 开发者 as 开发者
|
||||
participant Scan as Scan方法
|
||||
participant SendCard as SendCard方法
|
||||
participant 供应商 as 第三方供应商
|
||||
开发者->>Scan : 发起支付请求
|
||||
Scan->>Scan : 解析订单数据
|
||||
Scan->>SendCard : 调用SendCard发送请求
|
||||
SendCard->>供应商 : 发送支付请求
|
||||
供应商-->>SendCard : 返回响应
|
||||
SendCard-->>Scan : 返回处理结果
|
||||
Scan-->>开发者 : 返回支付结果
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L152-L183)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L114-L145)
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L152-L183)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L114-L145)
|
||||
|
||||
### PayNotify方法实现
|
||||
`PayNotify`方法处理来自第三方供应商的支付回调通知,是确保交易状态同步的关键。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 供应商 as 第三方供应商
|
||||
participant PayNotify as PayNotify方法
|
||||
participant 订单服务 as 订单服务
|
||||
participant 系统 as 支付系统
|
||||
供应商->>PayNotify : 发送回调通知
|
||||
PayNotify->>PayNotify : 验证订单存在性
|
||||
PayNotify->>订单服务 : 获取订单信息
|
||||
订单服务-->>PayNotify : 返回订单信息
|
||||
PayNotify->>系统 : 更新订单状态
|
||||
系统-->>PayNotify : 返回处理结果
|
||||
PayNotify-->>供应商 : 返回确认响应
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L202-L240)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L165-L212)
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L202-L240)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L165-L212)
|
||||
|
||||
### PayQuery方法实现
|
||||
`PayQuery`方法用于查询订单状态,确保交易的最终一致性。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 系统 as 支付系统
|
||||
participant PayQuery as PayQuery方法
|
||||
participant 供应商 as 第三方供应商
|
||||
系统->>PayQuery : 发起查询请求
|
||||
PayQuery->>PayQuery : 构建查询参数
|
||||
PayQuery->>供应商 : 发送查询请求
|
||||
供应商-->>PayQuery : 返回查询结果
|
||||
PayQuery->>PayQuery : 解析响应数据
|
||||
PayQuery-->>系统 : 返回查询结果
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L242-L305)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L214-L275)
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L242-L305)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L214-L275)
|
||||
|
||||
## HTTP请求与响应处理
|
||||
### 请求构建与发送
|
||||
支付供应商通过HTTP请求与第三方系统通信,使用`httplib`库进行请求构建和发送。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始]) --> 构建参数["构建请求参数"]
|
||||
构建参数 --> 设置URL["设置请求URL"]
|
||||
设置URL --> 设置超时["设置超时时间"]
|
||||
设置超时 --> 设置头["设置请求头"]
|
||||
设置头 --> 序列化["序列化请求体"]
|
||||
序列化 --> 发送请求["发送HTTP请求"]
|
||||
发送请求 --> 处理响应["处理响应数据"]
|
||||
处理响应 --> 结束([结束])
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L77-L150)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L53-L112)
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L77-L150)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L53-L112)
|
||||
|
||||
### 响应解析与处理
|
||||
收到响应后,系统需要解析JSON数据并根据业务逻辑进行处理。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
接收响应([接收HTTP响应]) --> 检查错误["检查网络错误"]
|
||||
检查错误 --> 解析JSON["解析JSON响应"]
|
||||
解析JSON --> 验证结构["验证响应结构"]
|
||||
验证结构 --> 提取数据["提取业务数据"]
|
||||
提取数据 --> 处理状态["处理状态码"]
|
||||
处理状态 --> 返回结果["返回处理结果"]
|
||||
```
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L77-L150)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L53-L112)
|
||||
|
||||
## 错误处理与重试机制
|
||||
### 错误处理策略
|
||||
系统采用分层的错误处理策略,确保异常情况下的稳定性。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
发生错误([发生错误]) --> 记录日志["记录错误日志"]
|
||||
记录日志 --> 分类错误["分类错误类型"]
|
||||
分类错误 --> 网络错误{"网络错误?"}
|
||||
网络错误 --> |是| 重试["执行重试逻辑"]
|
||||
网络错误 --> |否| 业务错误{"业务错误?"}
|
||||
业务错误 --> |是| 返回用户["返回用户友好信息"]
|
||||
业务错误 --> |否| 系统错误["返回系统错误"]
|
||||
重试 --> 检查重试次数["检查重试次数"]
|
||||
检查重试次数 --> 达到上限{"达到上限?"}
|
||||
达到上限 --> |是| 返回失败["返回失败"]
|
||||
达到上限 --> |否| 重新发送["重新发送请求"]
|
||||
```
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L77-L150)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L53-L112)
|
||||
|
||||
### 重试机制实现
|
||||
通过`req.Retries(3)`设置重试次数,确保网络不稳定时的请求可靠性。
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L77-L150)
|
||||
|
||||
## 供应商认证机制
|
||||
### 签名生成
|
||||
供应商通过特定算法生成签名,确保请求的完整性和安全性。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
开始([开始]) --> 拼接参数["拼接参数字符串"]
|
||||
拼接参数 --> 生成签名["生成MD5签名"]
|
||||
生成签名 --> 添加签名["将签名添加到请求参数"]
|
||||
添加签名 --> 发送请求["发送带签名的请求"]
|
||||
```
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L77-L150)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L53-L112)
|
||||
|
||||
## API限流与数据格式
|
||||
### 限流处理
|
||||
系统通过配置和超时设置来应对API限流。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
发送请求([发送请求]) --> 设置超时["设置30秒超时"]
|
||||
设置超时 --> 检查响应["检查响应时间"]
|
||||
检查响应 --> 超时{"超时?"}
|
||||
超时 --> |是| 重试["执行重试"]
|
||||
超时 --> |否| 正常处理["正常处理响应"]
|
||||
```
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L77-L150)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L53-L112)
|
||||
|
||||
### 数据格式规范
|
||||
所有请求和响应都遵循统一的JSON格式规范。
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L77-L150)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L53-L112)
|
||||
|
||||
## 新供应商集成步骤
|
||||
### 创建供应商结构体
|
||||
创建新的供应商实现结构体,继承必要的控制器。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
创建结构体([创建结构体]) --> 继承Controller["继承web.Controller"]
|
||||
继承Controller --> 实现接口["实现PayInterface接口"]
|
||||
实现接口 --> 添加字段["添加供应商特定字段"]
|
||||
```
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L14-L16)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L14-L16)
|
||||
|
||||
### 实现核心方法
|
||||
按照接口定义实现所有必需的方法。
|
||||
|
||||
**本节来源**
|
||||
- [supplier_interface.go](file://internal/service/supplier/supplier_interface.go#L26-L36)
|
||||
|
||||
### 添加到系统
|
||||
将新的供应商实现文件添加到third_party目录,并在系统中注册。
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go)
|
||||
|
||||
## 单元测试编写
|
||||
参考现有的`*_test.go`文件编写单元测试,确保代码质量。
|
||||
|
||||
**本节来源**
|
||||
- [apple_test.go](file://internal/service/supplier/third_party/apple_test.go)
|
||||
- [jd_test.go](file://internal/service/supplier/third_party/jd_test.go)
|
||||
|
||||
## 常见陷阱与最佳实践
|
||||
### 交易幂等性保证
|
||||
通过订单号和状态检查确保交易的幂等性。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
接收请求([接收支付请求]) --> 检查订单["检查订单是否存在"]
|
||||
检查订单 --> 已存在{"订单已存在?"}
|
||||
已存在 --> |是| 检查状态["检查订单状态"]
|
||||
检查状态 --> 已完成{"已完成?"}
|
||||
已完成 --> |是| 返回成功["返回成功"]
|
||||
已完成 --> |否| 处理请求["继续处理请求"]
|
||||
已存在 --> |否| 创建订单["创建新订单"]
|
||||
```
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go#L202-L240)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go#L165-L212)
|
||||
|
||||
### 最佳实践总结
|
||||
1. 始终记录详细的日志信息
|
||||
2. 实现完整的错误处理
|
||||
3. 遵循统一的代码风格
|
||||
4. 充分测试边界情况
|
||||
5. 确保交易的幂等性
|
||||
|
||||
**本节来源**
|
||||
- [apple.go](file://internal/service/supplier/third_party/apple.go)
|
||||
- [jd.go](file://internal/service/supplier/third_party/jd.go)
|
||||
181
.qoder/repowiki/zh/content/部署与配置/Docker部署指南.md
Normal file
181
.qoder/repowiki/zh/content/部署与配置/Docker部署指南.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Docker部署指南
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [Dockerfile](file://deploy/Dockerfile)
|
||||
- [docker-compose.yaml](file://deploy/docker-compose.yaml)
|
||||
- [docker-compose-local.yaml](file://deploy/docker-compose-local.yaml)
|
||||
- [main.go](file://main.go)
|
||||
- [app.conf](file://conf/app.conf)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [多阶段构建详解](#多阶段构建详解)
|
||||
2. [Docker Compose配置解析](#docker-compose配置解析)
|
||||
3. [本地部署步骤](#本地部署步骤)
|
||||
4. [常见问题排查](#常见问题排查)
|
||||
|
||||
## 多阶段构建详解
|
||||
|
||||
本项目采用多阶段Docker构建策略,通过两个阶段实现高效、安全的镜像构建。
|
||||
|
||||
### 构建阶段(Builder Stage)
|
||||
|
||||
第一阶段使用`golang:1.24`作为基础镜像,专门用于编译Go应用程序。该阶段设置了关键的环境变量以优化构建过程:
|
||||
|
||||
- `GO111MODULE=on`:启用Go Modules依赖管理
|
||||
- `GOPROXY=https://goproxy.cn,direct`:配置国内代理镜像加速依赖下载
|
||||
- `CGO_ENABLED=0`:禁用CGO以生成静态二进制文件
|
||||
- `GOOS=linux` 和 `GOARCH=amd64`:指定目标操作系统和架构
|
||||
|
||||
构建过程首先将整个项目代码复制到工作目录`/build`,然后执行`go mod tidy`清理依赖并使用`go build`命令生成经过优化的静态二进制文件`main`(使用`-ldflags="-s -w"`去除调试信息以减小体积)。
|
||||
|
||||
### 运行阶段(Runtime Stage)
|
||||
|
||||
第二阶段采用轻量级的`alpine:latest`镜像作为运行环境,显著减小最终镜像大小。该阶段进行了以下关键配置:
|
||||
|
||||
1. **环境变量设置**:配置了时区`TZ=Asia/Shanghai`及多个服务相关的环境变量
|
||||
2. **系统配置**:
|
||||
- 配置阿里云Alpine镜像源以加速包下载
|
||||
- 安装`tzdata`和`curl`等必要工具
|
||||
- 设置中国时区
|
||||
3. **证书配置**:
|
||||
- 安装标准CA证书包
|
||||
- 添加Comodo AAA证书以支持特定HTTPS连接
|
||||
- 更新证书信任链
|
||||
|
||||
最后,从构建阶段复制编译好的二进制文件`main`、配置文件目录`conf/`和数据目录`data/`到运行环境,并设置容器启动命令为`./main`,暴露端口12309。
|
||||
|
||||
**Section sources**
|
||||
- [Dockerfile](file://deploy/Dockerfile#L1-L43)
|
||||
|
||||
## Docker Compose配置解析
|
||||
|
||||
### 生产环境配置
|
||||
|
||||
`docker-compose.yaml`文件定义了生产环境的服务配置:
|
||||
|
||||
- **构建配置**:指定构建上下文为项目根目录(`..`),使用`./deploy/Dockerfile`作为Dockerfile
|
||||
- **容器配置**:
|
||||
- 容器名称:`kami_gateway`
|
||||
- 镜像标签:`kami_gateway:$VERSION`(支持版本变量)
|
||||
- 重启策略:`always`(确保容器异常退出后自动重启)
|
||||
- **网络配置**:连接到外部网络`1panel-network`,实现与其他服务的通信
|
||||
- **端口映射**:
|
||||
- `127.0.0.1:22309:12309`:主服务端口,仅限本地访问
|
||||
- `127.0.0.1:22390:12390`:监控服务端口,仅限本地访问
|
||||
- **卷挂载**:
|
||||
- 配置目录:`/data/kami/gateway/conf/` → `/app/conf/`
|
||||
- 日志目录:`/data/kami/gateway/logs/` → `/app/logs/`
|
||||
|
||||
### 本地开发配置
|
||||
|
||||
`docker-compose-local.yaml`提供了简化的本地开发配置,主要差异包括:
|
||||
- 使用固定标签`latest`
|
||||
- 端口映射为`12309:12309`,允许外部访问
|
||||
- 简化的服务名称`gateway_kami`
|
||||
|
||||
**Section sources**
|
||||
- [docker-compose.yaml](file://deploy/docker-compose.yaml#L1-L23)
|
||||
- [docker-compose-local.yaml](file://deploy/docker-compose-local.yaml#L1-L17)
|
||||
|
||||
## 本地部署步骤
|
||||
|
||||
### 1. 克隆代码库
|
||||
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd kami_gateway
|
||||
```
|
||||
|
||||
### 2. 准备配置文件
|
||||
|
||||
确保`conf/app.conf`中的配置正确,特别是数据库、Redis和MQ连接信息。根据需要修改以下关键配置:
|
||||
- `httpport = 12309`:服务监听端口
|
||||
- 数据库连接参数
|
||||
- Redis连接参数
|
||||
- MQ连接参数
|
||||
|
||||
### 3. 构建并启动服务
|
||||
|
||||
```bash
|
||||
# 进入部署目录
|
||||
cd deploy
|
||||
|
||||
# 设置版本环境变量(可选)
|
||||
export VERSION=v1.0.0
|
||||
|
||||
# 构建并启动服务
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### 4. 验证服务状态
|
||||
|
||||
```bash
|
||||
# 查看容器状态
|
||||
docker ps | grep kami_gateway
|
||||
|
||||
# 查看服务日志
|
||||
docker logs kami_gateway
|
||||
|
||||
# 测试服务连通性
|
||||
curl http://127.0.0.1:22309/health
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L1-L77)
|
||||
- [main.go](file://main.go#L23-L57)
|
||||
|
||||
## 常见问题排查
|
||||
|
||||
### 端口冲突
|
||||
|
||||
**症状**:容器无法启动,日志显示"port is already allocated"
|
||||
|
||||
**解决方案**:
|
||||
1. 检查端口占用情况:
|
||||
```bash
|
||||
netstat -tlnp | grep :22309
|
||||
lsof -i :22309
|
||||
```
|
||||
2. 修改`docker-compose.yaml`中的主机端口映射,例如改为`127.0.0.1:32309:12309`
|
||||
3. 重启服务
|
||||
|
||||
### 镜像构建失败
|
||||
|
||||
**症状**:`go mod tidy`或`go build`命令失败
|
||||
|
||||
**解决方案**:
|
||||
1. 确保网络连接正常,特别是对`goproxy.cn`的访问
|
||||
2. 清理Go模块缓存:
|
||||
```bash
|
||||
go clean -modcache
|
||||
```
|
||||
3. 检查`go.mod`文件完整性
|
||||
4. 确保Docker有足够的内存资源
|
||||
|
||||
### 容器无法启动
|
||||
|
||||
**症状**:容器启动后立即退出
|
||||
|
||||
**排查步骤**:
|
||||
1. 查看详细日志:
|
||||
```bash
|
||||
docker logs kami_gateway
|
||||
```
|
||||
2. 检查配置文件挂载是否正确
|
||||
3. 验证数据库、Redis等依赖服务是否正常运行
|
||||
4. 检查文件权限,确保容器内进程有足够权限访问挂载目录
|
||||
|
||||
### 证书问题
|
||||
|
||||
**症状**:HTTPS连接失败,证书验证错误
|
||||
|
||||
**解决方案**:
|
||||
1. 确认`Dockerfile`中证书安装步骤执行成功
|
||||
2. 检查`update-ca-certificates`命令输出
|
||||
3. 如需添加自定义证书,可将证书文件挂载到容器并手动添加
|
||||
|
||||
**Section sources**
|
||||
- [Dockerfile](file://deploy/Dockerfile#L1-L43)
|
||||
- [docker-compose.yaml](file://deploy/docker-compose.yaml#L1-L23)
|
||||
132
.qoder/repowiki/zh/content/部署与配置/环境变量管理.md
Normal file
132
.qoder/repowiki/zh/content/部署与配置/环境变量管理.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# 环境变量管理
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [Dockerfile](file://deploy/Dockerfile#L1-L44)
|
||||
- [docker-compose.yaml](file://deploy/docker-compose.yaml#L1-L24)
|
||||
- [docker-compose-local.yaml](file://deploy/docker-compose-local.yaml#L1-L18)
|
||||
- [build.sh](file://build.sh#L1-L2)
|
||||
- [app.conf](file://conf/app.conf#L1-L78)
|
||||
- [config.go](file://internal/config/config.go#L1-L45)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [预设环境变量](#预设环境变量)
|
||||
3. [环境变量覆盖机制](#环境变量覆盖机制)
|
||||
4. [构建脚本与版本控制](#构建脚本与版本控制)
|
||||
5. [实际应用案例](#实际应用案例)
|
||||
6. [环境变量与配置文件优先级](#环境变量与配置文件优先级)
|
||||
7. [总结](#总结)
|
||||
|
||||
## 简介
|
||||
`kami_gateway` 项目通过环境变量实现灵活的运行时配置,支持开发、测试和生产等多环境部署。本项目利用 Docker 和 docker-compose 提供了完整的容器化部署方案,并通过环境变量机制实现配置的动态注入与覆盖。环境变量不仅用于服务地址配置,还涉及代理服务、认证信息等关键参数,是系统可配置性的核心组成部分。
|
||||
|
||||
**Section sources**
|
||||
- [Dockerfile](file://deploy/Dockerfile#L1-L44)
|
||||
- [docker-compose.yaml](file://deploy/docker-compose.yaml#L1-L24)
|
||||
|
||||
## 预设环境变量
|
||||
在 `Dockerfile` 中通过 `ENV` 指令预设了一系列环境变量,这些变量为系统提供了默认配置:
|
||||
|
||||
- `serverName`: 服务器名称,默认值为"默认"
|
||||
- `gatewayAddr`: 网关地址,初始为空
|
||||
- `portalAddr`: 门户地址,初始为空
|
||||
- `shopAddr`: 商城地址,初始为空
|
||||
- `proxy`: 代理地址,初始为空
|
||||
- `proxyName`: 代理名称,默认为"qkgo"
|
||||
- `proxyUrl`: 代理获取URL,默认指向 qg.net 的代理服务
|
||||
- `proxyAuthKey`: 代理认证密钥,默认为"7ASQH2BI"
|
||||
- `proxyAuthPwd`: 代理认证密码,默认为"34D6652FE7B6"
|
||||
|
||||
这些环境变量在容器构建时被设置,为应用提供了基础的运行配置。
|
||||
|
||||
**Section sources**
|
||||
- [Dockerfile](file://deploy/Dockerfile#L10-L19)
|
||||
|
||||
## 环境变量覆盖机制
|
||||
`kami_gateway` 支持通过多种方式覆盖 Dockerfile 中预设的环境变量,实现不同环境的灵活配置。
|
||||
|
||||
在 `docker-compose.yaml` 文件中,虽然没有显式定义 `environment` 字段来覆盖环境变量,但通过外部卷挂载机制实现了配置的外部化:
|
||||
```yaml
|
||||
volumes:
|
||||
- /data/kami/gateway/conf/:/app/conf/
|
||||
- /data/kami/gateway/logs/:/app/logs/
|
||||
```
|
||||
这种设计允许通过挂载外部配置文件来间接影响环境变量的行为。
|
||||
|
||||
而在 `docker-compose-local.yaml` 中,通过简化配置实现了本地开发环境的快速部署,保留了构建上下文但简化了网络和端口配置。
|
||||
|
||||
运行时可以通过 `docker run` 命令的 `-e` 参数或在 `docker-compose.yml` 的 `environment` 字段中直接覆盖这些变量,例如:
|
||||
```bash
|
||||
docker run -e serverName="开发环境" -e gatewayAddr="http://dev.gateway:12309" kami_gateway
|
||||
```
|
||||
|
||||
**Section sources**
|
||||
- [docker-compose.yaml](file://deploy/docker-compose.yaml#L1-L24)
|
||||
- [docker-compose-local.yaml](file://deploy/docker-compose-local.yaml#L1-L18)
|
||||
|
||||
## 构建脚本与版本控制
|
||||
`build.sh` 脚本定义了项目的构建过程,其中包含了与环境变量相关的构建参数:
|
||||
|
||||
```bash
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
|
||||
```
|
||||
|
||||
虽然该脚本本身不直接处理环境变量,但在 `docker-compose.yaml` 中使用了 `$VERSION` 环境变量来控制镜像标签:
|
||||
```yaml
|
||||
image: kami_gateway:$VERSION
|
||||
```
|
||||
|
||||
这种设计允许在构建时通过传递不同的 `VERSION` 值来生成不同标签的镜像,实现版本控制。例如,可以通过以下命令构建特定版本的镜像:
|
||||
```bash
|
||||
VERSION=v1.2.0 docker-compose build
|
||||
```
|
||||
|
||||
这使得 CI/CD 流程能够根据 Git 标签或构建号自动创建相应版本的镜像,提高了部署的可追溯性和一致性。
|
||||
|
||||
**Section sources**
|
||||
- [build.sh](file://build.sh#L1-L2)
|
||||
- [docker-compose.yaml](file://deploy/docker-compose.yaml#L6-L7)
|
||||
|
||||
## 实际应用案例
|
||||
环境变量在 `kami_gateway` 中有多种实际应用场景。
|
||||
|
||||
### 动态数据库配置
|
||||
虽然数据库连接信息主要在 `app.conf` 中定义,但可以通过环境变量实现动态配置。例如,可以修改配置读取逻辑,从环境变量中获取数据库连接参数:
|
||||
```go
|
||||
dbhost := os.Getenv("MYSQL_HOST")
|
||||
dbport := os.Getenv("MYSQL_PORT")
|
||||
```
|
||||
这样可以在不同环境中使用不同的数据库实例,而无需修改配置文件。
|
||||
|
||||
### 代理服务切换
|
||||
通过 `proxyUrl`、`proxyAuthKey` 和 `proxyAuthPwd` 环境变量,可以轻松切换不同的代理服务提供商。在开发环境中可以使用测试代理,在生产环境中切换到付费的高稳定性代理服务。
|
||||
|
||||
### 多环境部署
|
||||
通过组合使用不同的环境变量,可以实现一套代码在多个环境中的部署:
|
||||
- 开发环境:`serverName="开发"`,`gatewayAddr="http://localhost:12309"`
|
||||
- 测试环境:`serverName="测试"`,`gatewayAddr="http://test.gateway:12309"`
|
||||
- 生产环境:`serverName="生产"`,`gatewayAddr="https://api.gateway.com"`
|
||||
|
||||
这种机制确保了配置的灵活性和安全性,敏感信息可以通过环境变量而非配置文件进行管理。
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L1-L78)
|
||||
- [Dockerfile](file://deploy/Dockerfile#L10-L19)
|
||||
|
||||
## 环境变量与配置文件优先级
|
||||
在 `kami_gateway` 中,环境变量与 `app.conf` 配置文件之间存在明确的优先级关系。
|
||||
|
||||
根据 `internal/config/config.go` 中的配置读取逻辑,系统使用 beego 框架的 `web.AppConfig.String()` 方法读取配置值。beego 框架默认支持环境变量覆盖配置文件中的值,即当同名环境变量存在时,会优先使用环境变量的值。
|
||||
|
||||
例如,`app.conf` 中定义了 `httpport = 12309`,但可以通过设置 `httpport` 环境变量来覆盖此值。这种设计模式使得关键配置既可以在配置文件中提供默认值,又可以通过环境变量在部署时进行灵活调整。
|
||||
|
||||
对于 `shop::key` 这样的配置项,`GetMerchantKey()` 函数直接从配置中读取,这意味着可以通过环境变量 `shop__key`(beego 将双下划线转换为配置节分隔符)来覆盖商户密钥,增强了配置的安全性。
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L1-L78)
|
||||
- [config.go](file://internal/config/config.go#L1-L45)
|
||||
|
||||
## 总结
|
||||
`kami_gateway` 项目通过精心设计的环境变量机制,实现了高度灵活的配置管理。从 Dockerfile 中的预设变量,到 docker-compose 的部署配置,再到运行时的动态覆盖,形成了完整的配置管理体系。这种设计不仅支持多环境部署,还提高了系统的可维护性和安全性。通过将敏感信息和环境特定配置从代码中分离,项目能够更好地适应不同的部署场景,同时保持代码库的整洁和一致性。
|
||||
213
.qoder/repowiki/zh/content/部署与配置/部署与配置.md
Normal file
213
.qoder/repowiki/zh/content/部署与配置/部署与配置.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# 部署与配置
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [Dockerfile](file://deploy/Dockerfile)
|
||||
- [docker-compose.yaml](file://deploy/docker-compose.yaml)
|
||||
- [docker-compose-local.yaml](file://deploy/docker-compose-local.yaml)
|
||||
- [app.conf](file://conf/app.conf)
|
||||
- [build.sh](file://build.sh)
|
||||
- [cfg_model.go](file://internal/config/cfg_model.go)
|
||||
- [config.go](file://internal/config/config.go)
|
||||
- [proxy.go](file://internal/config/proxy.go)
|
||||
- [mq_config.go](file://internal/config/mq_config.go)
|
||||
- [main.go](file://main.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概述](#架构概述)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖分析](#依赖分析)
|
||||
7. [性能考虑](#性能考虑)
|
||||
8. [故障排除指南](#故障排除指南)
|
||||
9. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
本文档提供基于Docker和Docker Compose的完整部署与配置指南,涵盖本地开发和生产环境的设置。文档详细说明如何通过环境变量覆盖配置文件中的默认值,并提供部署后的验证步骤。
|
||||
|
||||
## 项目结构
|
||||
项目采用分层结构,主要包含配置、部署脚本、内部业务逻辑和主程序入口。部署相关文件集中于`deploy`目录,配置文件位于`conf`目录。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "配置"
|
||||
appConf[app.conf]
|
||||
end
|
||||
subgraph "部署"
|
||||
Dockerfile[Dockerfile]
|
||||
dockerCompose[docker-compose.yaml]
|
||||
dockerComposeLocal[docker-compose-local.yaml]
|
||||
buildScript[build.sh]
|
||||
end
|
||||
subgraph "源码"
|
||||
main[main.go]
|
||||
internal[internal/]
|
||||
end
|
||||
Dockerfile --> main
|
||||
dockerCompose --> Dockerfile
|
||||
dockerComposeLocal --> Dockerfile
|
||||
appConf --> main
|
||||
buildScript --> main
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [Dockerfile](file://deploy/Dockerfile#L1-L44)
|
||||
- [docker-compose.yaml](file://deploy/docker-compose.yaml#L1-L24)
|
||||
- [app.conf](file://conf/app.conf#L1-L78)
|
||||
|
||||
**本节来源**
|
||||
- [deploy](file://deploy)
|
||||
- [conf](file://conf)
|
||||
|
||||
## 核心组件
|
||||
核心组件包括配置管理、代理池、消息队列和数据库连接。系统通过`main.go`初始化各项服务,包括代理池、缓存、消息消费者和队列系统。
|
||||
|
||||
**本节来源**
|
||||
- [main.go](file://main.go#L1-L59)
|
||||
- [config.go](file://internal/config/config.go#L1-L45)
|
||||
|
||||
## 架构概述
|
||||
系统采用微服务架构,通过Beego框架提供HTTP服务,使用Redis作为缓存,MySQL作为持久化存储,ActiveMQ作为消息队列。代理池用于外部请求的负载均衡。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Client[客户端] --> Gateway[网关服务]
|
||||
Gateway --> Redis[(Redis缓存)]
|
||||
Gateway --> MySQL[(MySQL数据库)]
|
||||
Gateway --> MQ[(消息队列)]
|
||||
Gateway --> ProxyPool[代理池]
|
||||
ProxyPool --> ExternalAPI[外部API]
|
||||
Gateway --> Backend[后端服务]
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [main.go](file://main.go#L1-L59)
|
||||
- [cfg_model.go](file://internal/config/cfg_model.go#L1-L141)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 配置管理分析
|
||||
系统配置通过`app.conf`文件和环境变量双重管理,环境变量优先级高于配置文件。
|
||||
|
||||
#### 配置类图
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Config {
|
||||
+GetMFCardQueryUrl() string
|
||||
+GetAppleCardSubmitUrl() string
|
||||
+GetAppleNotifyUrl() string
|
||||
+GetWalMartNotifyUrl() string
|
||||
+GetJDCardSubmitUrl() string
|
||||
+GetJDNotifyUrl() string
|
||||
+GetDomain() string
|
||||
+GetServerId() string
|
||||
+GetForbiddenBackendHost() string
|
||||
+ShopAddr() string
|
||||
+GatewayAddr() string
|
||||
}
|
||||
class RedisConfig {
|
||||
+Host string
|
||||
+Port string
|
||||
+Password string
|
||||
+DB int
|
||||
}
|
||||
class ProxyInfo {
|
||||
+Url string
|
||||
+AuthKey string
|
||||
+AuthPwd string
|
||||
}
|
||||
class MQConfig {
|
||||
+GetMQAddress() string
|
||||
+GetProxy() string
|
||||
+GetProxyConfig() []string
|
||||
}
|
||||
Config --> RedisConfig : "包含"
|
||||
Config --> ProxyInfo : "包含"
|
||||
MQConfig --> Config : "依赖"
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [cfg_model.go](file://internal/config/cfg_model.go#L1-L141)
|
||||
- [proxy.go](file://internal/config/proxy.go#L1-L18)
|
||||
- [mq_config.go](file://internal/config/mq_config.go#L1-L60)
|
||||
|
||||
### 代理池分析
|
||||
代理池在系统启动时初始化,支持通过环境变量动态配置代理服务器列表。
|
||||
|
||||
#### 代理池初始化流程图
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([程序启动]) --> InitProxyPool["proxy.InitProxyPool()"]
|
||||
InitProxyPool --> GetProxyConfig["config.GetProxyConfig()"]
|
||||
GetProxyConfig --> CheckEnv["检查proxy环境变量"]
|
||||
CheckEnv --> |存在| SplitProxies["按逗号分割代理列表"]
|
||||
CheckEnv --> |不存在| EmptyList["返回空列表"]
|
||||
SplitProxies --> TrimSpaces["去除空格"]
|
||||
TrimSpaces --> FilterEmpty["过滤空字符串"]
|
||||
FilterEmpty --> Initialize["初始化代理池"]
|
||||
Initialize --> ReturnSuccess["返回nil"]
|
||||
EmptyList --> Initialize
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [main.go](file://main.go#L1-L59)
|
||||
- [proxy.go](file://internal/config/proxy.go#L1-L18)
|
||||
- [mq_config.go](file://internal/config/mq_config.go#L1-L60)
|
||||
|
||||
### 消息队列分析
|
||||
系统使用消息队列处理订单查询和通知,通过独立的goroutine消费消息。
|
||||
|
||||
#### 消息处理序列图
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Main as "main()"
|
||||
participant Notify as "notify"
|
||||
participant Query as "query"
|
||||
participant Service as "service"
|
||||
participant Queue as "queue"
|
||||
Main->>Notify : go CreateOrderNotifyConsumer()
|
||||
Main->>Query : go CreateSupplierOrderQueryCuConsumer()
|
||||
Main->>Service : go OrderSettleInit()
|
||||
Main->>Queue : queue.Init()
|
||||
Main->>third_party : StartOrderPool()
|
||||
Note over Notify,Queue : 启动消息消费者和队列系统
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [main.go](file://main.go#L1-L59)
|
||||
|
||||
## 依赖分析
|
||||
系统依赖Go模块、Docker环境和外部服务。通过`go.mod`管理Go依赖,通过Docker容器化部署。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[本项目] --> B[beego/v2]
|
||||
A --> C[go-sql-driver/mysql]
|
||||
A --> D[alpine:latest]
|
||||
A --> E[golang:1.24]
|
||||
B --> F[其他Beego组件]
|
||||
C --> G[MySQL协议]
|
||||
```
|
||||
|
||||
**图示来源**
|
||||
- [Dockerfile](file://deploy/Dockerfile#L1-L44)
|
||||
- [go.mod](file://go.mod#L1-L20)
|
||||
|
||||
**本节来源**
|
||||
- [Dockerfile](file://deploy/Dockerfile#L1-L44)
|
||||
|
||||
## 性能考虑
|
||||
系统通过缓存、代理池和消息队列提高性能。Redis缓存减少数据库访问,代理池实现请求负载均衡,消息队列解耦业务逻辑。
|
||||
|
||||
## 故障排除指南
|
||||
常见问题包括数据库连接失败、Redis连接失败和代理池初始化失败。检查相应的配置项和网络连接。
|
||||
|
||||
**本节来源**
|
||||
- [main.go](file://main.go#L1-L59)
|
||||
- [cache/redis.go](file://internal/cache/redis.go#L18-L28)
|
||||
|
||||
## 结论
|
||||
本文档提供了完整的部署和配置指南,涵盖了从构建到运行的全过程。通过合理的配置管理和组件设计,系统具有良好的可维护性和扩展性。
|
||||
298
.qoder/repowiki/zh/content/部署与配置/配置文件详解.md
Normal file
298
.qoder/repowiki/zh/content/部署与配置/配置文件详解.md
Normal file
@@ -0,0 +1,298 @@
|
||||
# 配置文件详解
|
||||
|
||||
<cite>
|
||||
**Referenced Files in This Document**
|
||||
- [app.conf](file://conf/app.conf)
|
||||
- [config.go](file://internal/config/config.go)
|
||||
- [proxy.go](file://internal/config/proxy.go)
|
||||
- [proxy_pool.go](file://internal/utils/proxy_pool.go)
|
||||
- [config.go](file://internal/proxy/config.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [应用配置](#应用配置)
|
||||
2. [HTTP端口配置](#http端口配置)
|
||||
3. [日志配置](#日志配置)
|
||||
4. [数据库配置](#数据库配置)
|
||||
5. [Redis配置](#redis配置)
|
||||
6. [消息队列配置](#消息队列配置)
|
||||
7. [第三方服务API配置](#第三方服务api配置)
|
||||
8. [代理配置](#代理配置)
|
||||
9. [环境变量与配置文件的协同](#环境变量与配置文件的协同)
|
||||
10. [生产与测试环境对比](#生产与测试环境对比)
|
||||
|
||||
## 应用配置
|
||||
|
||||
`app.conf`配置文件中的`[appname]`区块定义了应用的基本信息,包括应用名称和运行模式。
|
||||
|
||||
```ini
|
||||
appname = jhgateway
|
||||
runmode = prod
|
||||
```
|
||||
|
||||
- `appname`:设置应用名称为`jhgateway`,该名称用于标识系统实例
|
||||
- `runmode`:设置运行模式为`prod`(生产环境),系统根据此模式加载相应的配置和行为
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L1-L2)
|
||||
|
||||
## HTTP端口配置
|
||||
|
||||
`[httpport]`区块配置了HTTP服务的监听端口和网络地址。
|
||||
|
||||
```ini
|
||||
httpport = 12309
|
||||
RemoteAddr = 0.0.0.0
|
||||
RemotePort = 12309
|
||||
HTTPAddr = 0.0.0.0
|
||||
```
|
||||
|
||||
- `httpport`:设置HTTP服务监听端口为12309
|
||||
- `RemoteAddr`和`HTTPAddr`:设置为`0.0.0.0`,表示监听所有网络接口
|
||||
- `RemotePort`:远程服务端口,与`httpport`保持一致
|
||||
|
||||
这些配置共同定义了应用的网络访问入口,客户端通过`http://<服务器IP>:12309`访问服务。
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L2-L6)
|
||||
|
||||
## 日志配置
|
||||
|
||||
`[logs]`区块详细配置了系统的日志记录行为。
|
||||
|
||||
```ini
|
||||
[logs]
|
||||
level =7
|
||||
filepath= ./logs/jhmerchant.log
|
||||
separate="["emergency","alert","critical","error","warning","notice","info","debug"]"
|
||||
maxdays=10
|
||||
```
|
||||
|
||||
- `level`:日志级别设置为7,对应DEBUG级别,记录所有级别的日志信息
|
||||
- `filepath`:日志文件保存路径为`./logs/jhmerchant.log`
|
||||
- `separate`:配置需要分离记录的日志类型,包含从紧急到调试的所有级别
|
||||
- `maxdays`:日志文件最大保存天数为10天,超过此期限的日志将被自动清理
|
||||
|
||||
日志级别0-7分别对应:
|
||||
- 0: emergency (紧急)
|
||||
- 1: alert (警报)
|
||||
- 2: critical (严重)
|
||||
- 3: error (错误)
|
||||
- 4: warning (警告)
|
||||
- 5: notice (通知)
|
||||
- 6: info (信息)
|
||||
- 7: debug (调试)
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L9-L15)
|
||||
|
||||
## 数据库配置
|
||||
|
||||
`[mysql]`区块配置了MySQL数据库的连接参数。
|
||||
|
||||
```ini
|
||||
[mysql]
|
||||
dbhost = 127.0.0.1
|
||||
dbport = 3306
|
||||
dbuser = root
|
||||
dbpasswd = Woaizixkie!123
|
||||
dbbase = kami
|
||||
debug = true
|
||||
```
|
||||
|
||||
- `dbhost`:数据库主机地址为`127.0.0.1`(本地)
|
||||
- `dbport`:数据库端口为3306
|
||||
- `dbuser`:数据库用户名为`root`
|
||||
- `dbpasswd`:数据库密码为`Woaizixkie!123`
|
||||
- `dbbase`:连接的数据库名为`kami`
|
||||
- `debug`:启用调试模式,便于开发和问题排查
|
||||
|
||||
这些配置用于建立与MySQL数据库的连接,支持系统数据的持久化存储。
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L17-L24)
|
||||
|
||||
## Redis配置
|
||||
|
||||
`[redis]`区块配置了Redis缓存服务的连接信息。
|
||||
|
||||
```ini
|
||||
[redis]
|
||||
host = 127.0.0.1:6379
|
||||
db = 0
|
||||
password = ""
|
||||
```
|
||||
|
||||
- `host`:Redis服务地址和端口为`127.0.0.1:6379`
|
||||
- `db`:使用的Redis数据库编号为0
|
||||
- `password`:Redis密码为空,表示无需认证
|
||||
|
||||
Redis主要用于缓存频繁访问的数据,提高系统性能和响应速度。
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L26-L30)
|
||||
|
||||
## 消息队列配置
|
||||
|
||||
`[mq]`区块配置了消息队列服务的连接参数。
|
||||
|
||||
```ini
|
||||
[mq]
|
||||
host = 127.0.0.1
|
||||
port = 61613
|
||||
```
|
||||
|
||||
- `host`:消息队列服务主机为`127.0.0.1`
|
||||
- `port`:消息队列服务端口为61613
|
||||
|
||||
消息队列用于实现系统组件间的异步通信,提高系统的可扩展性和可靠性。
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L32-L35)
|
||||
|
||||
## 第三方服务API配置
|
||||
|
||||
配置文件中包含了多个第三方服务的API端点和回调地址配置。
|
||||
|
||||
### MF服务配置
|
||||
```ini
|
||||
[mf]
|
||||
submit_card_url = http://test.shop.center.mf178.cn/userapi/card/submit_card
|
||||
query_card_url = http://test.shop.center.mf178.cn/userapi/card/order_info
|
||||
```
|
||||
|
||||
### Apple卡服务配置
|
||||
```ini
|
||||
[appleCard]
|
||||
submit_card_url = http://kami_backend:12401/api/cardInfo/appleCard/submit
|
||||
query_card_url = http://kami_backend:12401/api/cardInfo/appleCard/query
|
||||
notify_url = http://kami_gateway:12309/appleCard/notify
|
||||
```
|
||||
|
||||
### 京东卡服务配置
|
||||
```ini
|
||||
[jdCard]
|
||||
submit_card_url = http://kami_backend:12401/api/cardInfo/jdCard/submit
|
||||
query_card_url = http://kami_backend:12401/cardInfo/jdCard/query
|
||||
notify_url = http://kami_gateway:12309/jdCard/notify
|
||||
```
|
||||
|
||||
### 其他服务配置
|
||||
```ini
|
||||
[tMallGame]
|
||||
submit_card_url=http://test.shop.center.mf178.cn/recharge/tMallGame/order/submit
|
||||
notify_url=http://test.shop.center.mf178.cn/api/recharge/tMallGame/order/notify
|
||||
query_card_url=http://test.shop.center.mf178.cn/userapi/card/order_info
|
||||
```
|
||||
|
||||
这些配置定义了与各个第三方服务交互的API端点,包括:
|
||||
- `submit_card_url`:提交卡密的接口地址
|
||||
- `query_card_url`:查询卡密状态的接口地址
|
||||
- `notify_url`:接收第三方服务回调通知的地址
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L37-L77)
|
||||
|
||||
## 代理配置
|
||||
|
||||
`[proxy]`区块配置了代理服务的相关参数。
|
||||
|
||||
```ini
|
||||
[proxy]
|
||||
proxies = []
|
||||
```
|
||||
|
||||
- `proxies`:代理数组,当前为空,表示不使用静态代理列表
|
||||
|
||||
代理配置与环境变量`proxyUrl`协同工作,用于获取动态代理IP,实现请求的IP轮换,避免被目标网站封禁。
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L77-L78)
|
||||
|
||||
## 环境变量与配置文件的协同
|
||||
|
||||
系统通过环境变量与配置文件的协同工作来实现灵活的代理配置。`internal/config/proxy.go`文件中定义了代理信息的获取逻辑:
|
||||
|
||||
```go
|
||||
func GetProxyInfo() ProxyInfo {
|
||||
return ProxyInfo{
|
||||
Url: env.Get("proxyUrl", "https://share.proxy.qg.net/get?key=7ASQH2BI&num=2&area=&isp=0&format=txt&seq=\\r\\n&distinct=false"),
|
||||
AuthKey: env.Get("proxyAuthKey", "7ASQH2BI"),
|
||||
AuthPwd: env.Get("proxyAuthPwd", "34D6652FE7B6"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `proxyUrl`:代理服务API地址,从环境变量获取,若不存在则使用默认值
|
||||
- `proxyAuthKey`和`proxyAuthPwd`:代理认证密钥和密码,同样优先从环境变量获取
|
||||
|
||||
这种设计实现了配置的灵活性:
|
||||
1. 生产环境中通过环境变量设置敏感信息,避免硬编码在配置文件中
|
||||
2. 开发环境中使用默认值,简化配置
|
||||
3. 可以根据不同环境动态调整代理服务
|
||||
|
||||
代理系统的工作流程如下:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[请求开始] --> B{获取代理}
|
||||
B --> C[检查缓存代理]
|
||||
C --> |存在且有效| D[使用缓存代理]
|
||||
C --> |不存在或失效| E[调用代理API]
|
||||
E --> F[获取新代理IP]
|
||||
F --> G[验证代理可用性]
|
||||
G --> |可用| H[使用新代理]
|
||||
G --> |不可用| I[重试获取]
|
||||
H --> J[执行HTTP请求]
|
||||
D --> J
|
||||
J --> K[请求完成]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [proxy.go](file://internal/config/proxy.go#L10-L17)
|
||||
- [proxy_pool.go](file://internal/utils/proxy_pool.go#L108-L120)
|
||||
|
||||
**Section sources**
|
||||
- [proxy.go](file://internal/config/proxy.go#L10-L17)
|
||||
- [proxy_pool.go](file://internal/utils/proxy_pool.go#L108-L120)
|
||||
|
||||
## 生产与测试环境对比
|
||||
|
||||
### 生产环境配置特点
|
||||
```ini
|
||||
runmode = prod
|
||||
dbpasswd = Woaizixkie!123
|
||||
proxyUrl = https://secure.proxy.enterprise.com/api
|
||||
```
|
||||
|
||||
- 使用生产数据库和密码
|
||||
- 启用生产模式,关闭调试信息
|
||||
- 使用企业级代理服务
|
||||
- 日志级别可能调整为较低级别(如3-4),减少日志量
|
||||
|
||||
### 测试环境配置特点
|
||||
```ini
|
||||
runmode = dev
|
||||
dbpasswd = test123
|
||||
proxyUrl = https://test.proxy.service.com/api
|
||||
```
|
||||
|
||||
- 使用测试数据库和简单密码
|
||||
- 启用开发模式,便于调试
|
||||
- 使用测试代理服务
|
||||
- 日志级别设置为7(DEBUG),记录详细信息
|
||||
|
||||
### 配置项对系统行为的影响
|
||||
|
||||
| 配置项 | 影响范围 | 生产环境建议 | 测试环境建议 |
|
||||
|--------|--------|------------|------------|
|
||||
| `runmode` | 系统行为模式 | prod | dev |
|
||||
| `level` | 日志详细程度 | 3-4 (error-warning) | 7 (debug) |
|
||||
| `maxdays` | 存储空间占用 | 30-90天 | 7-14天 |
|
||||
| `debug` | 性能与安全性 | false | true |
|
||||
| `proxyUrl` | 请求IP轮换 | 高质量代理服务 | 测试代理服务 |
|
||||
|
||||
通过合理配置这些参数,可以确保系统在不同环境中以最优方式运行,既保证生产环境的稳定性和安全性,又满足测试环境的调试需求。
|
||||
|
||||
**Section sources**
|
||||
- [app.conf](file://conf/app.conf#L1-L78)
|
||||
178
.qoder/repowiki/zh/content/项目概述.md
Normal file
178
.qoder/repowiki/zh/content/项目概述.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# 项目概述
|
||||
|
||||
<cite>
|
||||
**本文档引用的文件**
|
||||
- [main.go](file://main.go)
|
||||
- [CLAUDE.md](file://CLAUDE.md)
|
||||
- [internal/controllers/base_controller.go](file://internal/controllers/base_controller.go)
|
||||
- [internal/models/init.go](file://internal/models/init.go)
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go)
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go)
|
||||
- [internal/service/supplier/third_party/pool.go](file://internal/service/supplier/third_party/pool.go)
|
||||
- [internal/schema/query/supplier_query.go](file://internal/schema/query/supplier_query.go)
|
||||
- [internal/service/notify/order_notify.go](file://internal/service/notify/order_notify.go)
|
||||
- [internal/service/settle_service.go](file://internal/service/settle_service.go)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [项目简介](#项目简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心功能](#核心功能)
|
||||
4. [系统架构](#系统架构)
|
||||
5. [技术栈](#技术栈)
|
||||
6. [启动流程](#启动流程)
|
||||
7. [数据流示例](#数据流示例)
|
||||
8. [高级特性](#高级特性)
|
||||
|
||||
## 项目简介
|
||||
kami_gateway 是一个基于 Go 语言和 Beego 框架构建的高性能支付网关系统。该项目旨在为各种支付方式(包括礼品卡和其他支付渠道)提供支付处理、订单管理和供应商集成服务。系统采用 MVC 模式进行架构设计,具有良好的可扩展性和可维护性。
|
||||
|
||||
**Section sources**
|
||||
- [CLAUDE.md](file://CLAUDE.md#L1-L20)
|
||||
|
||||
## 项目结构
|
||||
项目采用分层架构,主要分为以下几个目录:
|
||||
- `conf`: 配置文件目录
|
||||
- `deploy`: 部署相关文件
|
||||
- `internal`: 核心业务逻辑
|
||||
- `controllers`: HTTP 请求处理器
|
||||
- `models`: 数据模型和数据库操作
|
||||
- `service`: 业务逻辑层
|
||||
- `dto`: 数据传输对象
|
||||
- `routers`: 路由配置
|
||||
- `config`: 配置管理
|
||||
- `cache`: 缓存管理
|
||||
- `otelTrace`: 分布式追踪
|
||||
- `utils`: 工具函数
|
||||
|
||||
**Section sources**
|
||||
- [main.go](file://main.go#L1-L57)
|
||||
|
||||
## 核心功能
|
||||
kami_gateway 提供了以下核心功能:
|
||||
1. **多渠道支付处理**: 支持多种支付渠道的集成和处理
|
||||
2. **订单调度**: 实现订单的创建、查询和状态管理
|
||||
3. **代付请求**: 处理代付相关的业务逻辑
|
||||
4. **回调通知**: 实现订单状态变更的回调通知机制
|
||||
|
||||
**Section sources**
|
||||
- [CLAUDE.md](file://CLAUDE.md#L50-L70)
|
||||
|
||||
## 系统架构
|
||||
系统采用 MVC 模式,各层之间的关系如下:
|
||||
- **控制器层 (Controllers)**: 负责处理 HTTP 请求,调用服务层进行业务处理
|
||||
- **服务层 (Service)**: 实现核心业务逻辑,协调模型层进行数据操作
|
||||
- **模型层 (Models)**: 负责与数据库交互,提供数据访问接口
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[客户端] --> B[控制器]
|
||||
B --> C[服务层]
|
||||
C --> D[模型层]
|
||||
D --> E[数据库]
|
||||
C --> F[第三方供应商]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/controllers/base_controller.go](file://internal/controllers/base_controller.go#L6-L8)
|
||||
- [internal/models/init.go](file://internal/models/init.go#L0-L42)
|
||||
|
||||
## 技术栈
|
||||
kami_gateway 采用了以下技术栈:
|
||||
- **Beego v2**: Web 框架,提供 MVC 模式支持
|
||||
- **OpenTelemetry**: 分布式追踪,用于监控系统性能和调试
|
||||
- **Redis**: 缓存和会话管理,提高系统响应速度
|
||||
- **MySQL**: 主数据库,存储系统核心数据
|
||||
- **RabbitMQ**: 消息队列,用于异步任务处理
|
||||
|
||||
这些技术的组合使得 kami_gateway 具有高性能、高可用性和良好的可扩展性。
|
||||
|
||||
**Section sources**
|
||||
- [CLAUDE.md](file://CLAUDE.md#L90-L110)
|
||||
|
||||
## 启动流程
|
||||
系统的启动流程从 `main.go` 文件开始,主要步骤如下:
|
||||
1. 获取 MQ 地址配置
|
||||
2. 初始化代理池
|
||||
3. 初始化分布式追踪
|
||||
4. 启动各种消费者和定时任务
|
||||
5. 启动缓存和代理池
|
||||
6. 初始化队列系统
|
||||
7. 启动 Beego 服务器
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[开始] --> B[获取MQ地址]
|
||||
B --> C[初始化代理池]
|
||||
C --> D[初始化分布式追踪]
|
||||
D --> E[启动消费者]
|
||||
E --> F[启动缓存]
|
||||
F --> G[初始化队列系统]
|
||||
G --> H[启动Beego服务器]
|
||||
H --> I[结束]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [main.go](file://main.go#L23-L57)
|
||||
|
||||
## 数据流示例
|
||||
以一个支付请求为例,完整的数据流如下:
|
||||
1. 客户端发送支付请求到控制器
|
||||
2. 控制器调用服务层处理支付逻辑
|
||||
3. 服务层调用模型层进行数据库操作
|
||||
4. 服务层调用第三方供应商接口
|
||||
5. 第三方供应商返回结果
|
||||
6. 服务层更新订单状态
|
||||
7. 服务层触发回调通知
|
||||
8. 控制器返回响应给客户端
|
||||
|
||||
**Section sources**
|
||||
- [internal/service/pay_solve.go](file://internal/service/pay_solve.go#L37-L195)
|
||||
- [internal/service/notify/order_notify.go](file://internal/service/notify/order_notify.go#L41-L109)
|
||||
|
||||
## 高级特性
|
||||
### 分布式追踪
|
||||
系统集成了 OpenTelemetry,实现了分布式追踪功能。通过在关键代码路径上添加追踪点,可以监控系统的性能和调试问题。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[请求开始] --> B[控制器]
|
||||
B --> C[服务层]
|
||||
C --> D[数据库]
|
||||
D --> E[第三方供应商]
|
||||
E --> F[服务层]
|
||||
F --> G[控制器]
|
||||
G --> H[响应结束]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/otelTrace/init.go](file://internal/otelTrace/init.go#L29-L205)
|
||||
|
||||
### 异步任务队列
|
||||
系统使用基于 Redis 的异步任务队列来处理耗时操作,如订单查询和回调通知。这种机制提高了系统的响应速度和吞吐量。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[生产者] --> |添加任务| B[Redis队列]
|
||||
B --> |取出任务| C[消费者]
|
||||
C --> D[处理任务]
|
||||
D --> E[更新状态]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/service/supplier/third_party/pool.go](file://internal/service/supplier/third_party/pool.go#L16-L25)
|
||||
- [internal/schema/query/supplier_query.go](file://internal/schema/query/supplier_query.go#L89-L116)
|
||||
|
||||
### 定时任务
|
||||
系统实现了多种定时任务,如订单结算和商户负载计算。这些任务通过定时器触发,确保系统数据的及时更新。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[定时器] --> |触发| B[订单结算]
|
||||
A --> |触发| C[商户负载计算]
|
||||
B --> D[更新订单状态]
|
||||
C --> E[更新商户信息]
|
||||
```
|
||||
|
||||
**Diagram sources**
|
||||
- [internal/service/settle_service.go](file://internal/service/settle_service.go#L219-L235)
|
||||
1
.qoder/repowiki/zh/meta/repowiki-metadata.json
Normal file
1
.qoder/repowiki/zh/meta/repowiki-metadata.json
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user