# API Go 重构蓝图 本文档面向后续将 `www/new_sdk/application/api` 迁移到 Go 的落地设计,重点是模块边界、接口抽象、迁移顺序和风险控制。 ## 1. 推荐目录结构 ```text cmd/ api/ main.go internal/ bootstrap/ # 配置、DB、Redis、日志、依赖装配 config/ # app、game、payment、complex、third-party 配置 http/ router/ # 路由注册 middleware/ # 协议解密、鉴权、会话、限流、日志 handler/ # HTTP controller,保持薄层 response/ # 统一响应、错误码、SDK 加密输出 protocol/ codec/ # AES/JSON/兼容 imeil 等字段 sign/ # MD5 sign、appid/key 校验 token/ # auth_code/session token 编解码 domain/ account/ # 登录、注册、账号绑定、实名 session/ # token、Redis 会话 game/ # 游戏、区服、角色、版本、开服表 payment/ # 订单、支付渠道、回调、平台币 complex/ # 联运/渠道适配器 coupon/ # 优惠券 gift/ # 礼包、福利、客服中心 mlbb/ # MLBB 独立协议 thirdparty/ # 对外数据 API repository/ # MySQL 仓储 integration/ payment/ # ali、wx、yunshanfu、dinpay、shande 等 identity/ # 实名、未成年、防沉迷 notify/ # CP 通知、钉钉告警 dataapi/ # dy、yql 等外部接口 pkg/ decimal/ # 金额处理封装,避免 float 误差 ``` 原则:HTTP 层只负责协议、参数和响应;业务规则沉到 domain service;MySQL/Redis 访问全部经 repository;第三方接口全部经 integration。 ## 2. 中间件拆分 当前 `controller\Api` 承担了请求解密、签名、应用校验、返回加密、登录校验等多种职责。Go 版本建议拆成以下中间件: ```text RequestIDMiddleware RecoverMiddleware AccessLogMiddleware SDKCodecMiddleware # AES-128-ECB 解密 data、兼容 imeil、响应加密 AppAuthMiddleware # appid、AppKey、MD5 sign 校验 SessionMiddleware # auth_code/token 解析、Redis 会话校验 RateLimitMiddleware # 按 appid、ip、用户或接口限流 ``` 保留兼容点: - 客户端参数名、路由名、错误码、加密方式先保持不变。 - `imeil` 这类历史字段不要迁移时强行改名,可在 Go 内部归一成 `imei`。 - 未登录接口与需登录接口要由路由显式声明,不再依赖控制器父类隐式判断。 ## 3. 核心接口设计 ### 3.1 SDK 协议 ```go type Codec interface { DecodeRequest(ctx context.Context, req *http.Request) (*SDKRequest, error) EncodeResponse(ctx context.Context, app AppConfig, body any) (*SDKResponse, error) } type Signer interface { Verify(params map[string]string, appKey string, sign string) error } type SessionVerifier interface { Verify(ctx context.Context, authCode string) (*Session, error) Issue(ctx context.Context, userID int64, appID string) (*Session, error) Revoke(ctx context.Context, authCode string) error } ``` ### 3.2 账号域 ```go type AccountService interface { Login(ctx context.Context, cmd LoginCommand) (*LoginResult, error) Register(ctx context.Context, cmd RegisterCommand) (*RegisterResult, error) QuickRegister(ctx context.Context, cmd QuickRegisterCommand) (*RegisterResult, error) BindAccount(ctx context.Context, cmd BindAccountCommand) error ChangePassword(ctx context.Context, cmd ChangePasswordCommand) error SubmitIdentity(ctx context.Context, cmd IdentityCommand) (*IdentityResult, error) } ``` 账号迁移要特别关注同一用户在 `Member`、`User`、token、实名、防沉迷、渠道用户标识之间的映射关系。 ### 3.3 支付域 ```go type PaymentProvider interface { CreatePayment(ctx context.Context, order PaymentOrder) (*PaymentIntent, error) VerifyNotify(ctx context.Context, req NotifyRequest) (*NormalizedNotify, error) SuccessResponse() HTTPResponse FailResponse(reason string) HTTPResponse } type OrderService interface { CreateGameOrder(ctx context.Context, cmd CreateOrderCommand) (*PaymentOrder, error) MarkPaid(ctx context.Context, notify NormalizedNotify) (*PaymentOrder, error) Cancel(ctx context.Context, orderNo string) error GetOrder(ctx context.Context, orderNo string) (*PaymentOrder, error) ListOrders(ctx context.Context, query OrderQuery) ([]PaymentOrder, error) } ``` 支付状态机建议固定为: ```text created -> paying -> paid -> delivering -> delivered created -> canceled paid/delivering -> deliver_failed -> delivering -> delivered ``` 回调处理必须满足: - 按 `order_no` 或第三方交易号做幂等。 - 金额用整数分或 decimal,禁止使用 float 做最终判断。 - 更新订单状态、发放资产、通知 CP 要么在一个事务里完成,要么使用事务加 outbox。 - CP 通知失败不能导致第三方支付回调一直失败,应单独重试和告警。 ### 3.4 联运渠道域 ```go type ComplexAdapter interface { Channel() string CheckLogin(ctx context.Context, cmd ComplexLoginCommand, cfg ComplexGameConfig) (*ComplexLoginResult, error) VerifyPayNotify(ctx context.Context, req ComplexNotifyRequest, cfg ComplexGameConfig) (*NormalizedComplexNotify, error) SuccessResponse() HTTPResponse FailResponse(reason string) HTTPResponse } type SpecialParamProvider interface { SpecialParams(ctx context.Context, cmd SpecialParamCommand, cfg ComplexGameConfig) (map[string]any, error) } type H5PayVerifier interface { VerifyH5Pay(ctx context.Context, req ComplexH5PayRequest, cfg ComplexGameConfig) error } ``` 每个 `complex/*.php` 对应一个 Go adapter,先迁移高流量渠道。渠道差异只允许存在 adapter 内,业务层接收统一后的 `NormalizedComplexNotify`。 ## 4. 模块边界 | Go 模块 | 承接现有逻辑 | 迁移重点 | | --- | --- | --- | | `protocol` | `controller\Api`、签名、AES、响应格式 | 兼容老 SDK | | `account` | `User`、`V1\User`、实名相关控制器 | token、实名、防沉迷 | | `game` | `Game`、`Role`、`Server`、`Version`、`OpenServer` | 游戏、区服、角色缓存 | | `payment` | `Pay`、`PayNotify`、`GamePayService`、`PayService` | 订单状态、幂等、金额 | | `complex` | `Complex`、`PayNotifyComplex`、`complex/*` | 渠道适配器抽象 | | `coupon` | `Coupon`、`MemberCouponService` | 领取、使用、过期 | | `gift` | `Gift`、`Welfare`、`Service` | 礼包码、福利、客服 | | `mlbb` | `api/controller/mlbb/*`、`service/mlbb/*` | 独立路由和验签 | | `thirdparty` | `Third`, `DataApi`, `YqlApi`, `DyApi` | 外部数据接口隔离 | ## 5. 迁移阶段 ### 阶段 0:确认流量与契约 - 统计当前线上实际调用接口、渠道回调、支付渠道占比。 - 为 AES、sign、token、登录、支付回调准备 golden case。 - 将敏感配置从代码中迁出,统一进入环境变量或配置中心。 ### 阶段 1:协议层先行 - 在 Go 中实现 `SDKCodecMiddleware`、`AppAuthMiddleware`、统一错误码。 - 使用历史请求样本做加解密、验签、响应格式测试。 - 先不改业务,仅保证 Go 能读懂老 SDK 请求。 ### 阶段 2:只读和低风险接口 - 迁移版本、开服表、游戏信息、礼包列表、订单查询等低风险接口。 - 通过网关或 Nginx 按接口灰度转发。 - 开启新旧响应对比日志,确认字段兼容。 ### 阶段 3:账号与会话 - 迁移登录、注册、token 校验、实名接口。 - Redis key 先兼容旧格式,后续再逐步改成 Go 命名规范。 - 账号写入接口需要有回滚方案和完整审计日志。 ### 阶段 4:支付核心 - 先迁移订单创建,再迁移支付回调。 - 每个支付渠道单独上线,保留 PHP 回调兜底。 - 订单状态机和幂等逻辑必须在 Go 侧统一,禁止各渠道自行更新状态。 ### 阶段 5:联运渠道 - 选取 3 到 5 个高流量 `complex` 渠道优先迁移。 - 每个渠道建立登录和支付回调契约测试。 - adapter 上线后保留 PHP fallback,观察回调成功率和金额差异。 ### 阶段 6:收敛与下线 - 完成全量接口迁移后,冻结 PHP 新需求。 - 清理重复 Redis key、废弃支付渠道、无流量接口。 - 将 API 文档、错误码、渠道协议沉淀为 Go 项目内的 contract tests。 ## 6. 测试策略 | 测试类型 | 重点 | | --- | --- | | Golden tests | AES、sign、auth_code、响应格式、历史字段兼容 | | Unit tests | 账号规则、订单状态机、金额计算、渠道验签 | | Repository tests | MySQL 查询、事务、唯一键、分页 | | Integration tests | Redis 会话、支付 provider、实名 provider、CP 通知 | | Contract tests | 每个 complex adapter 的登录与支付回调样本 | | Shadow tests | 新旧接口响应对比,只记录不影响线上 | 支付和联运回调不建议直接双写。更稳妥的方式是先做回调样本重放和只读影子验证,确认后再灰度切流。 ## 7. 运维与可观测性 - 日志字段固定包含 `request_id`、`appid`、`user_id`、`game_id`、`order_no`、`channel`、`route`。 - 支付回调、CP 通知、实名失败、渠道登录失败要有独立指标。 - CP 通知使用队列或 outbox 重试,失败进入告警。 - 第三方接口要设置超时、重试上限、熔断和降级响应。 - 管理敏感配置时区分普通配置和 secret,代码仓库不再保存私钥、商户密钥、回调 token。 ## 8. Go 技术建议 - Web 框架可选 Gin、Chi 或 Hertz;如果团队偏简单稳定,优先 Chi/Gin。 - ORM 可选 GORM 或 SQLBoiler;支付和订单建议显式事务,避免隐藏更新。 - Redis 使用 go-redis。 - 金额使用整数分或 shopspring/decimal。 - 配置使用 Viper 或自研轻量 loader,但 secret 必须从环境变量或配置中心读取。 - 错误码用枚举和集中映射,不在 handler 中散落字符串。 ## 9. 优先级建议 | 优先级 | 内容 | 原因 | | --- | --- | --- | | P0 | 协议层、错误码、配置和日志 | 所有接口依赖 | | P0 | 支付订单状态机和幂等模型 | 风险最高 | | P1 | 登录、注册、会话、实名 | 核心用户链路 | | P1 | 高流量支付渠道 | 直接影响收入 | | P2 | 高流量 complex adapter | 联运差异大 | | P2 | 游戏、礼包、优惠券、客服 | 业务完整性 | | P3 | 低流量 Third/Data API | 可按需迁移 | ## 10. 落地判断 这个项目适合迁移到 Go,但不适合一次性重写。推荐采用“协议兼容 + 分域迁移 + 支付谨慎灰度”的方式推进。最关键的不是框架选择,而是把当前隐含在控制器、service、complex adapter 里的规则变成明确的接口、状态机、契约测试和可观测指标。