Ledger API 设计规范
概述
Ledger 模块采用 API First 的设计理念,通过 Protocol Buffers 定义统一的 API 契约,同时支持 gRPC 和 HTTP RESTful 两种协议。本文档详细规范了 API 的设计原则、接口定义、错误处理、版本管理等各个方面。
设计原则
- 契约优先: 通过 Protobuf 定义明确的 API 契约
- 双协议支持: gRPC (内部服务) + HTTP (外部客户端)
- RESTful 设计: HTTP API 遵循 REST 架构风格
- 版本兼容: 向后兼容的 API 版本管理
- 标准化: 统一的错误码、状态码和响应格式
技术栈
- Protocol Buffers 3: API 契约定义
- gRPC: 高性能内部服务通信
- gRPC-Gateway: HTTP RESTful API 自动生成
- OpenAPI/Swagger: API 文档自动生成
- Buf: 现代化 Protobuf 工具链
API 架构设计
1. 协议层次架构
2. API 分层设计
3. 服务拆分策略
Protocol Buffers 设计
1. 文件组织和命名规范
protobuf
// 文件路径: proto/ledger/v1/ledger_service.proto
syntax = "proto3";
package ledger.v1;
// Go 包路径规范
option go_package = "github.com/FixIterate/lz-stash/gen/ledger/v1;ledgerv1";
// C# 命名空间
option csharp_namespace = "FixIterate.Ledger.V1";
// PHP 命名空间
option php_namespace = "FixIterate\\Ledger\\V1";
// 导入其他 proto 文件
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/field_mask.proto";
import "common/v1/types.proto";
import "common/v1/money.proto";2. 服务定义规范
protobuf
// 账本管理服务
service LedgerService {
// 创建账本
rpc CreateLedger(CreateLedgerRequest) returns (CreateLedgerResponse) {
option (google.api.http) = {
post: "/api/v1/ledgers"
body: "*"
};
}
// 获取账本详情
rpc GetLedger(GetLedgerRequest) returns (GetLedgerResponse) {
option (google.api.http) = {
get: "/api/v1/ledgers/{id}"
};
}
// 列出账本 (支持分页和筛选)
rpc ListLedgers(ListLedgersRequest) returns (ListLedgersResponse) {
option (google.api.http) = {
get: "/api/v1/ledgers"
};
}
// 更新账本 (支持部分更新)
rpc UpdateLedger(UpdateLedgerRequest) returns (UpdateLedgerResponse) {
option (google.api.http) = {
patch: "/api/v1/ledgers/{id}"
body: "*"
};
}
// 删除账本 (软删除)
rpc DeleteLedger(DeleteLedgerRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
delete: "/api/v1/ledgers/{id}"
};
}
// 批量操作
rpc BatchCreateLedgers(BatchCreateLedgersRequest) returns (BatchCreateLedgersResponse) {
option (google.api.http) = {
post: "/api/v1/ledgers:batch"
body: "*"
};
}
// 归档账本
rpc ArchiveLedger(ArchiveLedgerRequest) returns (ArchiveLedgerResponse) {
option (google.api.http) = {
post: "/api/v1/ledgers/{id}:archive"
body: "*"
};
}
}
// 交易记录服务
service TransactionService {
// 创建交易记录
rpc CreateTransaction(CreateTransactionRequest) returns (CreateTransactionResponse) {
option (google.api.http) = {
post: "/api/v1/ledgers/{ledger_id}/transactions"
body: "*"
};
}
// 快速记账 (移动端优化)
rpc QuickCreateTransaction(QuickCreateTransactionRequest) returns (QuickCreateTransactionResponse) {
option (google.api.http) = {
post: "/api/v1/transactions:quick"
body: "*"
};
}
// 批量创建交易记录
rpc BatchCreateTransactions(BatchCreateTransactionsRequest) returns (BatchCreateTransactionsResponse) {
option (google.api.http) = {
post: "/api/v1/ledgers/{ledger_id}/transactions:batch"
body: "*"
};
}
// 获取交易记录
rpc GetTransaction(GetTransactionRequest) returns (GetTransactionResponse) {
option (google.api.http) = {
get: "/api/v1/transactions/{id}"
};
}
// 列出交易记录 (高级筛选)
rpc ListTransactions(ListTransactionsRequest) returns (ListTransactionsResponse) {
option (google.api.http) = {
get: "/api/v1/ledgers/{ledger_id}/transactions"
};
}
// 搜索交易记录
rpc SearchTransactions(SearchTransactionsRequest) returns (SearchTransactionsResponse) {
option (google.api.http) = {
get: "/api/v1/transactions:search"
};
}
// 更新交易记录
rpc UpdateTransaction(UpdateTransactionRequest) returns (UpdateTransactionResponse) {
option (google.api.http) = {
patch: "/api/v1/transactions/{id}"
body: "*"
};
}
// 删除交易记录
rpc DeleteTransaction(DeleteTransactionRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
delete: "/api/v1/transactions/{id}"
};
}
// 导入交易记录 (银行流水等)
rpc ImportTransactions(ImportTransactionsRequest) returns (ImportTransactionsResponse) {
option (google.api.http) = {
post: "/api/v1/ledgers/{ledger_id}/transactions:import"
body: "*"
};
}
// 导出交易记录
rpc ExportTransactions(ExportTransactionsRequest) returns (stream ExportTransactionsResponse) {
option (google.api.http) = {
get: "/api/v1/ledgers/{ledger_id}/transactions:export"
};
}
}3. 实体定义规范
protobuf
// 账本实体
message Ledger {
// 主键字段
string id = 1; // 账本唯一标识
// 基本信息
string name = 2; // 账本名称
string description = 3; // 账本描述
LedgerType type = 4; // 账本类型
LedgerStatus status = 5; // 账本状态
// 配置信息
common.v1.Currency default_currency = 6; // 默认货币
LedgerSettings settings = 7; // 账本设置
// 所有者信息
string owner_id = 8; // 所有者ID
// 时间戳
google.protobuf.Timestamp created_at = 10; // 创建时间
google.protobuf.Timestamp updated_at = 11; // 更新时间
google.protobuf.Timestamp archived_at = 12; // 归档时间
// 版本控制
int64 version = 13; // 乐观锁版本号
// 统计信息 (只读)
LedgerStats stats = 20; // 统计数据
// 扩展字段 (100-199 为扩展保留)
map<string, string> metadata = 100; // 元数据
repeated Extension extensions = 101; // 扩展信息
}
// 交易记录实体
message Transaction {
// 主键和关联
string id = 1; // 交易唯一标识
string ledger_id = 2; // 所属账本ID
// 交易信息
TransactionType type = 3; // 交易类型
common.v1.Money amount = 4; // 交易金额
string description = 5; // 交易描述
string note = 6; // 备注信息
// 分类信息
repeated string tag_ids = 7; // 标签ID列表
string category_id = 8; // 分类ID
// 时间信息
google.protobuf.Timestamp transaction_date = 9; // 交易发生时间
google.protobuf.Timestamp created_at = 10; // 记录创建时间
google.protobuf.Timestamp updated_at = 11; // 记录更新时间
// 附加信息
TransactionLocation location = 12; // 交易地点
repeated Attachment attachments = 13; // 附件列表
string source = 14; // 数据来源
// 状态信息
TransactionStatus status = 15; // 交易状态
string receipt_number = 16; // 票据号码
// 关联信息
string parent_transaction_id = 17; // 父交易ID (分期付款等)
repeated string child_transaction_ids = 18; // 子交易ID列表
// 版本控制
int64 version = 19; // 乐观锁版本号
// 扩展字段
map<string, string> metadata = 100;
}
// 预算实体
message Budget {
// 基本信息
string id = 1;
string ledger_id = 2;
string name = 3;
string description = 4;
// 预算配置
common.v1.Money amount = 5; // 预算金额
BudgetPeriod period = 6; // 预算周期
google.protobuf.Timestamp start_date = 7; // 开始日期
google.protobuf.Timestamp end_date = 8; // 结束日期
// 适用范围
repeated string tag_ids = 9; // 适用标签
repeated string category_ids = 10; // 适用分类
BudgetScope scope = 11; // 预算范围
// 预警配置
repeated BudgetAlert alerts = 12; // 预警设置
// 状态信息
BudgetStatus status = 13; // 预算状态
BudgetUsage usage = 14; // 使用情况
// 时间戳
google.protobuf.Timestamp created_at = 15;
google.protobuf.Timestamp updated_at = 16;
// 扩展字段
map<string, string> metadata = 100;
}4. 枚举定义规范
protobuf
// 账本类型
enum LedgerType {
LEDGER_TYPE_UNSPECIFIED = 0; // 未指定
LEDGER_TYPE_PERSONAL = 1; // 个人账本
LEDGER_TYPE_FAMILY = 2; // 家庭账本
LEDGER_TYPE_PROJECT = 3; // 项目账本
LEDGER_TYPE_TRAVEL = 4; // 旅行账本
LEDGER_TYPE_BUSINESS = 5; // 商务账本
LEDGER_TYPE_SHARED = 6; // 共享账本
}
// 账本状态
enum LedgerStatus {
LEDGER_STATUS_UNSPECIFIED = 0;
LEDGER_STATUS_ACTIVE = 1; // 活跃
LEDGER_STATUS_ARCHIVED = 2; // 已归档
LEDGER_STATUS_SUSPENDED = 3; // 已暂停
LEDGER_STATUS_DELETED = 4; // 已删除
}
// 交易类型
enum TransactionType {
TRANSACTION_TYPE_UNSPECIFIED = 0;
TRANSACTION_TYPE_INCOME = 1; // 收入
TRANSACTION_TYPE_EXPENSE = 2; // 支出
TRANSACTION_TYPE_TRANSFER = 3; // 转账
TRANSACTION_TYPE_ADJUSTMENT = 4; // 调整
}
// 交易状态
enum TransactionStatus {
TRANSACTION_STATUS_UNSPECIFIED = 0;
TRANSACTION_STATUS_PENDING = 1; // 待确认
TRANSACTION_STATUS_COMPLETED = 2; // 已完成
TRANSACTION_STATUS_CANCELLED = 3; // 已取消
TRANSACTION_STATUS_FAILED = 4; // 失败
TRANSACTION_STATUS_REFUNDED = 5; // 已退款
}
// 预算周期
enum BudgetPeriod {
BUDGET_PERIOD_UNSPECIFIED = 0;
BUDGET_PERIOD_DAILY = 1; // 日
BUDGET_PERIOD_WEEKLY = 2; // 周
BUDGET_PERIOD_MONTHLY = 3; // 月
BUDGET_PERIOD_QUARTERLY = 4; // 季度
BUDGET_PERIOD_YEARLY = 5; // 年
BUDGET_PERIOD_CUSTOM = 6; // 自定义
}5. 请求响应消息规范
protobuf
// 创建账本请求
message CreateLedgerRequest {
// 必填字段
string name = 1; // 账本名称
LedgerType type = 2; // 账本类型
// 可选字段
string description = 3; // 账本描述
common.v1.Currency default_currency = 4; // 默认货币
LedgerSettings settings = 5; // 账本设置
// 元数据
map<string, string> metadata = 100;
}
// 创建账本响应
message CreateLedgerResponse {
Ledger ledger = 1; // 创建的账本
OperationResult result = 2; // 操作结果
}
// 列出账本请求
message ListLedgersRequest {
// 分页参数
common.v1.PaginationRequest pagination = 1;
// 筛选条件
LedgerFilter filter = 2;
// 排序参数
repeated common.v1.SortOrder sort_orders = 3;
// 字段掩码 (部分字段返回)
google.protobuf.FieldMask field_mask = 4;
// 包含关联数据
LedgerInclude include = 5;
}
// 列出账本响应
message ListLedgersResponse {
repeated Ledger ledgers = 1; // 账本列表
common.v1.PaginationResponse pagination = 2; // 分页信息
ListStats stats = 3; // 列表统计
}
// 更新账本请求 (支持部分更新)
message UpdateLedgerRequest {
string id = 1; // 账本ID
// 可更新字段
optional string name = 2;
optional string description = 3;
optional LedgerSettings settings = 4;
// 字段掩码 (指定要更新的字段)
google.protobuf.FieldMask field_mask = 5;
// 乐观锁版本号
int64 version = 6;
}
// 筛选条件
message LedgerFilter {
// 基本筛选
repeated LedgerType types = 1; // 账本类型
repeated LedgerStatus statuses = 2; // 账本状态
string owner_id = 3; // 所有者ID
// 时间筛选
common.v1.TimeRange created_time_range = 4; // 创建时间范围
common.v1.TimeRange updated_time_range = 5; // 更新时间范围
// 搜索关键词
string search_query = 6; // 搜索查询
repeated string search_fields = 7; // 搜索字段
// 高级筛选
repeated string tag_ids = 8; // 包含标签
common.v1.Currency currency = 9; // 货币类型
// 自定义筛选
map<string, string> custom_filters = 100;
}HTTP RESTful API 设计
1. URL 设计规范
http
# 账本管理
GET /api/v1/ledgers # 列出账本
POST /api/v1/ledgers # 创建账本
GET /api/v1/ledgers/{id} # 获取账本详情
PATCH /api/v1/ledgers/{id} # 更新账本
DELETE /api/v1/ledgers/{id} # 删除账本
# 账本操作
POST /api/v1/ledgers/{id}:archive # 归档账本
POST /api/v1/ledgers/{id}:restore # 恢复账本
POST /api/v1/ledgers:batch # 批量操作
# 交易记录管理
GET /api/v1/ledgers/{ledger_id}/transactions # 列出交易记录
POST /api/v1/ledgers/{ledger_id}/transactions # 创建交易记录
GET /api/v1/transactions/{id} # 获取交易详情
PATCH /api/v1/transactions/{id} # 更新交易记录
DELETE /api/v1/transactions/{id} # 删除交易记录
# 交易记录操作
GET /api/v1/transactions:search # 搜索交易记录
POST /api/v1/transactions:quick # 快速记账
POST /api/v1/transactions:batch # 批量创建
POST /api/v1/transactions:import # 导入交易
GET /api/v1/transactions:export # 导出交易
# 标签管理
GET /api/v1/ledgers/{ledger_id}/tags # 列出标签
POST /api/v1/ledgers/{ledger_id}/tags # 创建标签
GET /api/v1/tags/{id} # 获取标签详情
PATCH /api/v1/tags/{id} # 更新标签
DELETE /api/v1/tags/{id} # 删除标签
# 预算管理
GET /api/v1/ledgers/{ledger_id}/budgets # 列出预算
POST /api/v1/ledgers/{ledger_id}/budgets # 创建预算
GET /api/v1/budgets/{id} # 获取预算详情
PATCH /api/v1/budgets/{id} # 更新预算
DELETE /api/v1/budgets/{id} # 删除预算
# 统计分析
GET /api/v1/ledgers/{ledger_id}/analytics # 获取统计分析
GET /api/v1/ledgers/{ledger_id}/reports # 获取报表数据
GET /api/v1/ledgers/{ledger_id}/trends # 获取趋势分析
# 移动端专用接口
GET /api/mobile/v1/dashboard # 移动端仪表板
POST /api/mobile/v1/sync # 数据同步
POST /api/mobile/v1/ocr # OCR 识别
# 管理后台接口
GET /api/admin/v1/users/{user_id}/ledgers # 用户账本管理
GET /api/admin/v1/stats # 系统统计
POST /api/admin/v1/maintenance # 系统维护2. HTTP 状态码规范
http
# 成功响应
200 OK # 查询成功
201 Created # 创建成功
204 No Content # 删除成功 / 更新成功无返回内容
# 客户端错误
400 Bad Request # 请求参数错误
401 Unauthorized # 未认证
403 Forbidden # 权限不足
404 Not Found # 资源不存在
409 Conflict # 资源冲突 (如重复创建)
422 Unprocessable Entity # 数据验证失败
429 Too Many Requests # 请求过于频繁
# 服务器错误
500 Internal Server Error # 服务器内部错误
502 Bad Gateway # 网关错误
503 Service Unavailable # 服务不可用
504 Gateway Timeout # 网关超时3. 请求响应格式
json
// 创建账本请求
POST /api/v1/ledgers
Content-Type: application/json
{
"name": "我的个人账本",
"description": "记录日常收支",
"type": "LEDGER_TYPE_PERSONAL",
"default_currency": "CURRENCY_CNY",
"settings": {
"enable_budget_alerts": true,
"budget_alert_threshold": 80,
"timezone": "Asia/Shanghai",
"locale": "zh-CN"
},
"metadata": {
"source": "mobile_app",
"version": "1.0.0"
}
}
// 创建账本响应
HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/v1/ledgers/ledger_123
{
"ledger": {
"id": "ledger_123",
"name": "我的个人账本",
"description": "记录日常收支",
"type": "LEDGER_TYPE_PERSONAL",
"status": "LEDGER_STATUS_ACTIVE",
"default_currency": "CURRENCY_CNY",
"owner_id": "user_456",
"settings": {
"enable_budget_alerts": true,
"budget_alert_threshold": 80,
"timezone": "Asia/Shanghai",
"locale": "zh-CN"
},
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"version": 1,
"stats": {
"transaction_count": 0,
"total_income": {
"amount": 0,
"currency": "CURRENCY_CNY"
},
"total_expense": {
"amount": 0,
"currency": "CURRENCY_CNY"
}
}
},
"result": {
"success": true,
"message": "账本创建成功",
"operation_id": "op_789"
}
}
// 列出账本请求
GET /api/v1/ledgers?page_size=20&page_token=abc123&type=LEDGER_TYPE_PERSONAL&sort=created_at:desc
// 列出账本响应
{
"ledgers": [
{
"id": "ledger_123",
"name": "我的个人账本",
// ... 其他字段
}
],
"pagination": {
"next_page_token": "def456",
"total_count": 1
},
"stats": {
"total_count": 1,
"active_count": 1,
"archived_count": 0
}
}
// 错误响应格式
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": {
"code": "INVALID_ARGUMENT",
"message": "请求参数错误",
"details": [
{
"field": "name",
"message": "账本名称不能为空"
},
{
"field": "type",
"message": "无效的账本类型"
}
],
"request_id": "req_123456",
"timestamp": "2024-01-15T10:30:00Z"
}
}4. 查询参数规范
http
# 分页参数
?page_size=20 # 页大小 (默认 20, 最大 100)
?page_token=abc123 # 分页令牌
# 筛选参数
?type=LEDGER_TYPE_PERSONAL # 按类型筛选
?status=LEDGER_STATUS_ACTIVE # 按状态筛选
?owner_id=user_123 # 按所有者筛选
?currency=CURRENCY_CNY # 按货币筛选
# 时间范围筛选
?created_after=2024-01-01T00:00:00Z
?created_before=2024-12-31T23:59:59Z
?updated_after=2024-01-01T00:00:00Z
# 搜索参数
?q=账本名称 # 搜索查询
?search_fields=name,description # 搜索字段
# 排序参数
?sort=created_at:desc # 按创建时间降序
?sort=name:asc,updated_at:desc # 多字段排序
# 字段选择
?fields=id,name,created_at # 只返回指定字段
?include=stats,members # 包含关联数据
# 交易记录特有参数
?transaction_type=TRANSACTION_TYPE_EXPENSE # 交易类型
?amount_min=1000 # 最小金额 (分)
?amount_max=100000 # 最大金额 (分)
?tag_ids=tag1,tag2 # 标签筛选
?date_from=2024-01-01 # 交易日期开始
?date_to=2024-01-31 # 交易日期结束错误处理和状态码
1. 错误码设计
protobuf
// 通用错误码
enum ErrorCode {
ERROR_CODE_UNSPECIFIED = 0;
// 客户端错误 (4xx)
ERROR_CODE_INVALID_ARGUMENT = 400;
ERROR_CODE_UNAUTHORIZED = 401;
ERROR_CODE_FORBIDDEN = 403;
ERROR_CODE_NOT_FOUND = 404;
ERROR_CODE_CONFLICT = 409;
ERROR_CODE_UNPROCESSABLE_ENTITY = 422;
ERROR_CODE_RATE_LIMITED = 429;
// 服务器错误 (5xx)
ERROR_CODE_INTERNAL_ERROR = 500;
ERROR_CODE_SERVICE_UNAVAILABLE = 503;
ERROR_CODE_TIMEOUT = 504;
}
// 业务错误码
enum LedgerErrorCode {
LEDGER_ERROR_CODE_UNSPECIFIED = 0;
// 账本相关错误
LEDGER_ERROR_CODE_NAME_REQUIRED = 1001;
LEDGER_ERROR_CODE_NAME_TOO_LONG = 1002;
LEDGER_ERROR_CODE_INVALID_TYPE = 1003;
LEDGER_ERROR_CODE_ALREADY_EXISTS = 1004;
LEDGER_ERROR_CODE_MEMBER_LIMIT_EXCEEDED = 1005;
// 交易相关错误
LEDGER_ERROR_CODE_INVALID_AMOUNT = 2001;
LEDGER_ERROR_CODE_INVALID_CURRENCY = 2002;
LEDGER_ERROR_CODE_INVALID_DATE = 2003;
LEDGER_ERROR_CODE_TAG_NOT_FOUND = 2004;
// 预算相关错误
LEDGER_ERROR_CODE_BUDGET_EXCEEDED = 3001;
LEDGER_ERROR_CODE_INVALID_PERIOD = 3002;
LEDGER_ERROR_CODE_OVERLAPPING_BUDGET = 3003;
// 权限相关错误
LEDGER_ERROR_CODE_INSUFFICIENT_PERMISSION = 4001;
LEDGER_ERROR_CODE_INVALID_MEMBER_ROLE = 4002;
}
// 错误详情
message ErrorDetail {
string field = 1; // 错误字段
string message = 2; // 错误消息
ErrorCode code = 3; // 错误码
map<string, string> metadata = 4; // 额外信息
}
// 错误响应
message ErrorResponse {
ErrorCode code = 1; // 主错误码
string message = 2; // 错误消息
repeated ErrorDetail details = 3; // 错误详情
string request_id = 4; // 请求ID
google.protobuf.Timestamp timestamp = 5; // 错误时间
}2. 错误处理策略
go
// 错误处理中间件
func ErrorHandlingMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
if len(c.Errors) > 0 {
err := c.Errors.Last()
// 转换为标准错误响应
errorResp := convertToErrorResponse(err.Err, c.GetString("request_id"))
// 设置对应的 HTTP 状态码
statusCode := getHTTPStatusCode(errorResp.Code)
c.JSON(statusCode, map[string]interface{}{
"error": errorResp,
})
}
}
}
// 业务层错误定义
type LedgerError struct {
Code ErrorCode
Message string
Details []ErrorDetail
}
func (e *LedgerError) Error() string {
return e.Message
}
// 创建业务错误
func NewValidationError(field, message string) *LedgerError {
return &LedgerError{
Code: ERROR_CODE_INVALID_ARGUMENT,
Message: "validation failed",
Details: []ErrorDetail{
{
Field: field,
Message: message,
Code: ERROR_CODE_INVALID_ARGUMENT,
},
},
}
}
func NewNotFoundError(resource, id string) *LedgerError {
return &LedgerError{
Code: ERROR_CODE_NOT_FOUND,
Message: fmt.Sprintf("%s not found", resource),
Details: []ErrorDetail{
{
Field: "id",
Message: fmt.Sprintf("%s with id '%s' not found", resource, id),
Code: ERROR_CODE_NOT_FOUND,
},
},
}
}API 版本管理
1. 版本策略
protobuf
// v1 版本
syntax = "proto3";
package ledger.v1;
option go_package = "github.com/FixIterate/lz-stash/gen/ledger/v1;ledgerv1";
// v2 版本 (向后兼容)
syntax = "proto3";
package ledger.v2;
option go_package = "github.com/FixIterate/lz-stash/gen/ledger/v2;ledgerv2";
// 版本兼容性规则:
// 1. 不能删除已有字段
// 2. 不能改变已有字段的类型
// 3. 不能改变已有字段的编号
// 4. 新增字段必须是可选的或有默认值
// 5. 新增字段使用新的编号2. 字段演化规范
protobuf
// v1.0 - 初始版本
message Ledger {
string id = 1;
string name = 2;
string description = 3;
}
// v1.1 - 添加新字段 (兼容)
message Ledger {
string id = 1;
string name = 2;
string description = 3;
// 新增字段,使用新编号
LedgerType type = 4; // v1.1 新增
common.v1.Currency currency = 5; // v1.1 新增
}
// v1.2 - 标记废弃字段 (兼容)
message Ledger {
string id = 1;
string name = 2;
string description = 3 [deprecated = true]; // 标记为废弃
LedgerType type = 4;
common.v1.Currency currency = 5;
// 替代字段
string summary = 6; // v1.2 新增,替代 description
}
// v2.0 - 破坏性变更 (不兼容)
message Ledger {
string id = 1;
string name = 2;
// 移除 description 字段
LedgerType type = 3; // 重新编号
common.v1.Currency currency = 4; // 重新编号
string summary = 5; // 重新编号
// 新的字段结构
LedgerSettings settings = 6; // v2.0 新增
}3. HTTP API 版本管理
http
# URL 路径版本控制 (推荐)
GET /api/v1/ledgers
GET /api/v2/ledgers
# Header 版本控制
GET /api/ledgers
Accept: application/vnd.ledger.v1+json
# 查询参数版本控制
GET /api/ledgers?version=v1
# 客户端版本协商
GET /api/ledgers
Accept: application/json
X-API-Version: v1性能优化
1. 分页和游标
protobuf
// 基于游标的分页 (推荐大数据集)
message ListTransactionsRequest {
string ledger_id = 1;
int32 page_size = 2; // 页大小
string cursor = 3; // 游标位置
ListOrder order = 4; // 排序方式
}
message ListTransactionsResponse {
repeated Transaction transactions = 1;
string next_cursor = 2; // 下一页游标
bool has_more = 3; // 是否有更多数据
int64 total_count = 4; // 总记录数 (可选)
}
// 基于偏移的分页 (适合小数据集)
message PaginationRequest {
int32 page_size = 1; // 页大小 (默认 20, 最大 100)
int32 page_number = 2; // 页码 (从 1 开始)
}
message PaginationResponse {
int32 page_size = 1; // 当前页大小
int32 page_number = 2; // 当前页码
int64 total_count = 3; // 总记录数
int32 total_pages = 4; // 总页数
bool has_next = 5; // 是否有下一页
bool has_prev = 6; // 是否有上一页
}2. 字段选择和关联查询
protobuf
// 字段掩码 (只返回需要的字段)
message GetLedgerRequest {
string id = 1;
google.protobuf.FieldMask field_mask = 2; // 字段掩码
}
// 关联数据包含选项
message LedgerInclude {
bool include_stats = 1; // 包含统计信息
bool include_members = 2; // 包含成员信息
bool include_recent_transactions = 3; // 包含最近交易
int32 recent_transaction_limit = 4; // 最近交易数量限制
}
message ListLedgersRequest {
common.v1.PaginationRequest pagination = 1;
LedgerFilter filter = 2;
google.protobuf.FieldMask field_mask = 3;
LedgerInclude include = 4; // 包含关联数据
}3. 批量操作
protobuf
// 批量创建
message BatchCreateTransactionsRequest {
string ledger_id = 1;
repeated CreateTransactionRequest requests = 2;
BatchOptions options = 3;
}
message BatchCreateTransactionsResponse {
repeated BatchResult results = 1;
BatchSummary summary = 2;
}
message BatchResult {
int32 index = 1; // 请求索引
oneof result {
Transaction transaction = 2; // 成功结果
ErrorResponse error = 3; // 错误结果
}
}
message BatchSummary {
int32 total_requests = 1; // 总请求数
int32 success_count = 2; // 成功数量
int32 error_count = 3; // 错误数量
repeated string error_indices = 4; // 错误索引列表
}
message BatchOptions {
bool continue_on_error = 1; // 遇到错误是否继续
bool atomic = 2; // 是否原子操作
int32 batch_size = 3; // 批次大小
}4. 缓存策略
http
# HTTP 缓存头
GET /api/v1/ledgers/123
Cache-Control: max-age=3600, must-revalidate
ETag: "v1-123-20240115103000"
Last-Modified: Mon, 15 Jan 2024 10:30:00 GMT
# 条件请求
GET /api/v1/ledgers/123
If-None-Match: "v1-123-20240115103000"
If-Modified-Since: Mon, 15 Jan 2024 10:30:00 GMT
# 响应
HTTP/1.1 304 Not Modified
# 缓存失效
PATCH /api/v1/ledgers/123
# 服务器返回新的 ETag 和 Last-Modified安全性考虑
1. 认证和授权
protobuf
// 请求认证信息
message AuthContext {
string user_id = 1; // 用户ID
repeated string roles = 2; // 用户角色
repeated string permissions = 3; // 用户权限
string session_id = 4; // 会话ID
google.protobuf.Timestamp expires_at = 5; // 过期时间
}
// 权限检查
message PermissionCheck {
string resource = 1; // 资源类型
string resource_id = 2; // 资源ID
string action = 3; // 操作类型
AuthContext auth_context = 4; // 认证上下文
}2. 数据验证
protobuf
// 输入验证注解 (使用 protoc-gen-validate)
import "validate/validate.proto";
message CreateLedgerRequest {
string name = 1 [(validate.rules).string = {
min_len: 1,
max_len: 100,
pattern: "^[a-zA-Z0-9\\s\\u4e00-\\u9fa5\\-_]+$"
}];
string description = 2 [(validate.rules).string = {
max_len: 500
}];
LedgerType type = 3 [(validate.rules).enum = {
defined_only: true,
not_in: [0] // 不能是 UNSPECIFIED
}];
}
message CreateTransactionRequest {
common.v1.Money amount = 1 [(validate.rules).message = {
required: true
}];
string description = 2 [(validate.rules).string = {
min_len: 1,
max_len: 200
}];
google.protobuf.Timestamp transaction_date = 3 [(validate.rules).timestamp = {
lt_now: true,
gte: {seconds: 946684800} // 不能早于 2000-01-01
}];
}3. 敏感数据处理
protobuf
// 敏感字段标记
message User {
string id = 1;
string username = 2;
string email = 3 [(sensitive) = true]; // 敏感信息
string phone = 4 [(sensitive) = true];
string password_hash = 5 [(internal) = true]; // 内部字段
}
// 脱敏响应
message PublicUserProfile {
string id = 1;
string username = 2;
string masked_email = 3; // 已脱敏的邮箱
// 不包含敏感信息
}文档生成和工具
1. OpenAPI 文档生成
bash
# 生成 OpenAPI 文档
buf generate --template buf.gen.openapi.yaml
# buf.gen.openapi.yaml
version: v1
plugins:
- plugin: buf.build/grpc-ecosystem/openapiv2
out: docs/openapi
opt:
- allow_merge=true
- merge_file_name=api
- json_names_for_fields=true
- include_package_in_tags=true2. 客户端 SDK 生成
bash
# 生成多语言客户端
buf generate --template buf.gen.nodejs.yaml
buf generate --template buf.gen.python.yaml
buf generate --template buf.gen.php.yaml
# 发布到包管理器
npm publish ./clients/nodejs
pip upload ./clients/python
composer install ./clients/php3. API 测试工具
bash
# 使用 grpcurl 测试 gRPC API
grpcurl -plaintext localhost:9090 list
grpcurl -plaintext localhost:9090 ledger.v1.LedgerService/ListLedgers
# 使用 Evans 交互式测试
evans --host localhost --port 9090 repl
# 使用 Postman 测试 HTTP API
curl -X POST http://localhost:8080/api/v1/ledgers \
-H "Content-Type: application/json" \
-d '{"name": "测试账本", "type": "LEDGER_TYPE_PERSONAL"}'最佳实践总结
1. API 设计原则
- 一致性: 统一的命名规范和响应格式
- 简洁性: 避免过度设计,保持接口简单明了
- 可扩展性: 预留扩展字段,支持向后兼容
- 安全性: 输入验证、权限控制、敏感数据保护
- 性能: 分页、缓存、批量操作优化
2. 错误处理
- 使用标准的错误码和消息格式
- 提供详细的错误信息帮助调试
- 区分客户端错误和服务器错误
- 记录错误日志便于故障排查
3. 版本管理
- 语义化版本控制
- 向后兼容的字段演化
- 废弃字段的标记和迁移计划
- 多版本并存的支持策略
4. 性能优化
- 合理的分页策略
- 字段选择减少传输量
- 批量操作提高效率
- 缓存机制减少服务器负载
这个 API 设计规范为 Ledger 模块提供了完整的接口定义标准,确保 API 的一致性、可维护性和可扩展性。