API 设计
HTTP JSON · Gin 路由 · 本地 JWT 验签 · 统一错误码 · 幂等键 · 后台 RBAC
定位
后端没有独立 API Gateway,也没有“网关 → 微服务”的调用链。api-server 和 admin-api 都是 Go Gin 进程,HTTP 层只负责路由、中间件、参数校验和响应封装,业务逻辑进入 internal/* 模块 service。
| 进程 | 面向对象 | 端口 | 路由前缀 |
|---|---|---|---|
api-server | 用户小程序、配送端 | 8080 | /api/* |
admin-api | 运营后台 | 8081 | /admin/* |
请求链路
中间件顺序
text
Request
-> RequestID / TraceID
-> AccessLog
-> Recovery
-> CORS
-> CSRF / Origin check(写接口)
-> RateLimit
-> Auth(按路由要求启用)
-> RBAC(后台路由)
-> Idempotency(指定 mutation 路由)
-> Handler鉴权策略
| 类型 | 方案 |
|---|---|
| 用户小程序 | 微信 code 登录,后端签发 JWT |
| 配送端 | 账号密码或运营创建账号,后端签发 JWT |
| 运营后台 | 账号密码 + RBAC,敏感操作写审计日志 |
| token 失效 | JWT 短有效期 + Valkey token_version:{user_id} + token_blacklist:{jti} |
go
func VerifyToken(ctx context.Context, tokenString string) (*Claims, error) {
claims, err := ParseAndVerifyJWT(tokenString, jwtSecret)
if err != nil {
return nil, ErrInvalidToken
}
currentVersion, err := cache.GetInt(ctx, "token_version:"+claims.UserID)
if err == nil && claims.Version < currentVersion {
return nil, ErrTokenRevoked
}
if cache.Exists(ctx, "token_blacklist:"+claims.ID) {
return nil, ErrTokenRevoked
}
return claims, nil
}用户端路由
text
# 认证
POST /api/auth/login/wechat auth.WechatLogin
POST /api/auth/bind-phone auth.BindPhone
POST /api/auth/refresh auth.RefreshToken
POST /api/auth/logout auth.Logout
# 商品与溯源
GET /api/products catalog.ListProducts
GET /api/products/:id catalog.GetProduct
GET /api/products/:id/trace catalog.GetTrace
GET /api/products/search catalog.SearchProducts
# 用户与地址
GET /api/user/profile user.GetProfile
PUT /api/user/profile user.UpdateProfile
GET /api/user/addresses user.ListAddresses
POST /api/user/addresses user.AddAddress
PUT /api/user/addresses/:id user.UpdateAddress
DELETE /api/user/addresses/:id user.DeleteAddress
# 订单
POST /api/orders/preview order.PreviewOrder
POST /api/orders order.CreateOrder
GET /api/orders order.ListOrders
GET /api/orders/:order_no order.GetOrder
POST /api/orders/:order_no/cancel order.CancelOrder
# 支付
POST /api/payments/wechat/notify payment.WechatNotify
GET /api/payments/:order_no payment.QueryPayment
POST /api/payments/refund payment.Refund(后台优先,用户端可后置)
# 优惠券
GET /api/coupons coupon.ListUserCoupons
GET /api/coupons/available coupon.ListAvailable
# 文件
POST /api/assets/upload-credential asset.GetUploadCredential
POST /api/assets/upload-callback asset.OnUploadComplete
# 售后
POST /api/after-sales order.CreateAfterSale
GET /api/after-sales/:id order.GetAfterSale配送端路由
text
POST /api/driver/login auth.DriverLogin
GET /api/delivery/tasks delivery.ListTasks
GET /api/delivery/tasks/:id delivery.GetTask
POST /api/delivery/tasks/:id/claim delivery.ClaimTask
POST /api/delivery/tasks/:id/release delivery.ReleaseTask
POST /api/delivery/tasks/:id/pickup delivery.MarkPickedUp
POST /api/delivery/tasks/:id/deliver delivery.MarkDelivered
GET /api/delivery/tasks/locations delivery.GetTaskLocations配送端返回脱敏地址、脱敏联系人、完整坐标。完整手机号和详细门牌不在配送端接口暴露。
后台路由
后台统一走 /admin/*,必须登录且通过 RBAC。
text
# 商品
GET /admin/products catalog.AdminListProducts
POST /admin/products catalog.AdminCreateProduct
PUT /admin/products/:id catalog.AdminUpdateProduct
POST /admin/products/:id/on-shelf catalog.AdminOnShelf
POST /admin/products/:id/off-shelf catalog.AdminOffShelf
# 库存
GET /admin/inventory inventory.AdminListStock
POST /admin/inventory/inbound inventory.AdminInbound
POST /admin/inventory/adjust inventory.AdminAdjust
GET /admin/inventory/movements inventory.AdminListMovements
# 订单
GET /admin/orders order.AdminListOrders
GET /admin/orders/:order_no order.AdminGetOrder
POST /admin/orders/:order_no/cancel order.AdminCancelOrder
POST /admin/orders/:order_no/prepare order.AdminMarkPreparing
# 支付与退款
GET /admin/payments payment.AdminListPayments
GET /admin/refunds payment.AdminListRefunds
POST /admin/refunds/:refund_no/approve payment.AdminApproveRefund
POST /admin/refunds/:refund_no/reject payment.AdminRejectRefund
# 配送
GET /admin/delivery/tasks delivery.AdminListTasks
POST /admin/delivery/tasks/:id/assign delivery.AdminAssignTask
# 优惠券
GET /admin/coupons/templates coupon.AdminListTemplates
POST /admin/coupons/templates coupon.AdminCreateTemplate
POST /admin/coupons/issue coupon.AdminIssue
# 审计
GET /admin/audit-logs audit.ListLogs后台敏感接口必须要求 reason 字段,写入 audit_logs。
统一响应
成功响应:
json
{
"data": {
"order_no": "DW202605220001"
},
"trace_id": "01J..."
}错误响应:
json
{
"code": "ORDER_INVALID_STATUS",
"message": "order cannot be cancelled after delivery started",
"trace_id": "01J..."
}| HTTP 状态 | 使用场景 |
|---|---|
| 400 | 参数格式错误、业务输入不合法 |
| 401 | 未登录或 token 失效 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 409 | 状态冲突、库存不足、重复操作 |
| 429 | 触发限流 |
| 500 | 未预期服务端错误 |
幂等键
适用接口:
| 接口 | 幂等维度 |
|---|---|
POST /api/orders | X-Idempotency-Key + user_id |
POST /api/payments/wechat/notify | wechat_txn_id / notify_id |
POST /api/payments/refund | refund_no / order_no |
| 后台库存调整 | operation_id / audit target |
text
前端生成 X-Idempotency-Key
-> 中间件查 Valkey idempotency:{user_id}:{key}
-> 未命中则查 PostgreSQL idempotency_keys
-> 执行业务
-> 保存 response 到 Valkey + PostgreSQL
-> 重复请求直接返回首次 response幂等记录 TTL:Valkey 24 小时,PostgreSQL 7 天后定期清理。
安全基线
| 项目 | 要求 |
|---|---|
| SQL 注入 | 全部参数化查询,禁止字符串拼 SQL |
| 密码 | argon2id 或 bcrypt,禁止明文和可逆加密 |
| 手机号/地址 | 加密存储,hash 辅助查询,日志脱敏 |
| 支付回调 | 微信平台证书验签、回调解密、原文留存 |
| CORS | 只允许已知域名和小程序来源 |
| CSRF | 后台写接口检查 Origin/Referer |
| pprof | 仅本机或内网访问,生产默认关闭公网入口 |
| 限流 | 登录、下单、支付、退款接口分级限流 |
| 审计 | 改价、退款、取消订单、库存调整、发券必须审计 |