docs: 修复 README 示例与当前 API 的一致性 - OpType改为string,NewFullOperation参数修正,查询API统一,Persistence完整示例,明确所有存证策略都支持SM2签名

This commit is contained in:
ryan
2025-12-25 10:55:16 +08:00
parent e54d3c96b7
commit a61b1a8840

193
README.md
View File

@@ -89,7 +89,7 @@ SDK 提供两种数据模型,分别适用于不同的业务场景:
- `OpID`:操作唯一标识符(自动生成 UUID v7
- `Timestamp`:操作时间戳(必填)
- `OpSource`:操作来源(`DOIP``IRP`
- `OpType`:操作类型(如 `Create``Update``Delete` 等)
- `OpType`:操作类型(如 `"Create"``"Update"``"Delete"`,字符串类型
- `OpAlgorithm`:哈希算法类型(默认 `Sha256Simd`
- `OpMetaHash`:元数据哈希值(自动计算)
- `DataID`:数据标识
@@ -104,14 +104,16 @@ SDK 提供两种数据模型,分别适用于不同的业务场景:
**创建方式**
```go
op, err := model.NewFullOperation(
model.OpSourceDOIP, // 操作来源
model.OpTypeCreate, // 操作类型
dataID, // 数据标识
"user123", // 操作者
[]byte(`{"foo":"bar"}`), // 请求体(支持 string 或 []byte
[]byte(`{"status":"ok"}`), // 响应体(支持 string 或 []byte
model.SHA256, // 哈希算法
time.Now(), // 操作时间戳
model.OpSourceDOIP, // 操作来源
string(model.OpTypeCreate), // 操作类型(字符串)
"10.1000", // doPrefix
"my-repo", // doRepository
"10.1000/my-repo/object123", // doid完整标识
"producer-123", // producerID
"user123", // opActor
[]byte(`{"foo":"bar"}`), // 请求体(支持 string 或 []byte
[]byte(`{"status":"ok"}`), // 响应体(支持 string 或 []byte
time.Now(), // 操作时间戳
)
```
@@ -333,23 +335,18 @@ envelopeConfig := model.DefaultEnvelopeConfig(privateKeyHex, publicKeyHex)
client := highclient.NewClient(pub, myLogger, envelopeConfig)
defer client.Close()
// 构造 DataID
dataID := model.DataID{
DoPrefix: "10.1000",
DoRepository: "my-repo",
Doid: "10.1000/my-repo/object123",
}
// 构造完整的 Operation
op, err := model.NewFullOperation(
model.OpSourceDOIP, // 操作来源DOIP 或 IRP
model.OpTypeCreate, // 操作类型:Create, Update, Delete 等
dataID, // 数据标识
"user123", // 操作者
[]byte(`{"foo":"bar"}`), // 请求体
[]byte(`{"status":"ok"}`), // 响应体
model.Sha256Simd, // 哈希算法
time.Now(), // 操作时间
model.OpSourceDOIP, // 操作来源DOIP 或 IRP
string(model.OpTypeCreate), // 操作类型:字符串
"10.1000", // doPrefix
"my-repo", // doRepository
"10.1000/my-repo/object123", // doid完整标识
"producer-123", // producerID
"user123", // opActor
[]byte(`{"foo":"bar"}`), // 请求体
[]byte(`{"status":"ok"}`), // 响应体
time.Now(), // 操作时间戳
)
if err != nil {
panic(err)
@@ -454,13 +451,13 @@ import (
ctx := context.Background()
// 构造查询请求
req := queryclient.ListOperationsRequest{
req := queryclient.ListRequest{
PageSize: 100, // 每页数量
PreTime: time.Now().Add(-24 * time.Hour), // 游标分页(可选)
// 可选过滤条件
OpSource: model.OpSourceDOIP, // 按操作来源过滤
OpType: model.OpTypeCreate, // 按操作类型过滤
OpSource: "DOIP", // 按操作来源过滤(字符串)
OpType: "Create", // 按操作类型过滤(字符串)
DoPrefix: "10.1000", // 按数据前缀过滤
DoRepository: "my-repo", // 按仓库过滤
}
@@ -475,15 +472,15 @@ if err != nil {
fmt.Printf("Total count: %d\n", resp.Count)
for _, op := range resp.Data {
fmt.Printf("Operation ID: %s, Type: %s, Time: %s\n",
op.Meta.OpID, op.Meta.OpType, op.Meta.Timestamp)
op.OpID, op.OpType, op.Timestamp)
}
```
#### 2.3 取证验证(流式)
```go
// 构造验证请求
validationReq := queryclient.ValidationRequest{
Time: time.Now().Add(-1 * time.Hour),
validationReq := queryclient.ValidateRequest{
Timestamp: time.Now().Add(-1 * time.Hour),
OpID: "operation-id-123",
OpType: "Create",
DoRepository: "my-repo",
@@ -535,7 +532,7 @@ if finalResult.IsCompleted() {
#### 2.5 查询记录列表Record
```go
// 构造记录查询请求
recordReq := queryclient.ListRecordsRequest{
recordReq := queryclient.ListRequest{
PageSize: 50, // 每页数量
PreTime: time.Now().Add(-24 * time.Hour), // 游标分页(可选)
@@ -561,7 +558,7 @@ for _, rec := range recordResp.Data {
#### 2.6 记录验证(流式)
```go
// 构造记录验证请求
recordValidationReq := queryclient.RecordValidationRequest{
recordValidationReq := queryclient.ValidateRequest{
Timestamp: time.Now().Add(-1 * time.Hour),
RecordID: "record-id-123",
DoPrefix: "10.1000",
@@ -638,20 +635,46 @@ import (
"time"
"go.yandata.net/iod/iod/go-trustlog/api/persistence"
"go.yandata.net/iod/iod/go-trustlog/api/adapter"
"go.yandata.net/iod/iod/go-trustlog/api/model"
"go.yandata.net/iod/iod/go-trustlog/api/logger"
"github.com/go-logr/logr"
)
func main() {
ctx := context.Background()
// 创建 Persistence Client
// 1. 创建 Logger
myLogger := logger.NewLogger(logr.Discard())
// 2. 创建 Pulsar Publisher
publisher, err := adapter.NewPublisher(
adapter.PublisherConfig{
URL: "pulsar://localhost:6650",
},
myLogger,
)
if err != nil {
panic(err)
}
defer publisher.Close()
// 3. 准备 SM2 密钥和 Envelope 配置(用于签名)
privateKeyHex := []byte("私钥D的十六进制字符串")
publicKeyHex := []byte("04 + x坐标 + y坐标的十六进制字符串")
envelopeConfig := model.DefaultEnvelopeConfig(privateKeyHex, publicKeyHex)
// 4. 创建 Persistence Client
client, err := persistence.NewPersistenceClient(ctx, persistence.PersistenceClientConfig{
Publisher: publisher, // Pulsar Publisher
Logger: logger,
EnvelopeConfig: envelopeConfig, // SM2 签名配置
Publisher: publisher, // Pulsar Publisher
Logger: myLogger,
EnvelopeConfig: envelopeConfig, // SM2 签名配置
DBConfig: persistence.DBConfig{
DriverName: "postgres",
DSN: "postgres://user:pass@localhost:5432/trustlog?sslmode=disable",
DriverName: "postgres",
DSN: "postgres://user:pass@localhost:5432/trustlog?sslmode=disable",
MaxOpenConns: 20,
MaxIdleConns: 10,
ConnMaxLifetime: time.Hour,
},
PersistenceConfig: persistence.PersistenceConfig{
Strategy: persistence.StrategyDBAndTrustlog, // 既落库又存证
@@ -675,38 +698,45 @@ func main() {
}
defer client.Close()
// 发布操作(立即返回,异步存证)
// 5. 发布操作(立即返回,异步存证)
clientIP := "192.168.1.100"
serverIP := "10.0.0.1"
op := &model.Operation{
OpID: "op-001",
OpType: model.OpTypeCreate,
Doid: "10.1000/repo/obj",
ProducerID: "producer-001",
OpSource: model.OpSourceDOIP,
DoPrefix: "10.1000",
OpID: "op-001",
OpType: string(model.OpTypeCreate), // 字符串类型
Doid: "10.1000/repo/obj",
ProducerID: "producer-001",
OpSource: model.OpSourceDOIP,
DoPrefix: "10.1000",
DoRepository: "repo",
ClientIP: &clientIP, // 可空
ServerIP: &serverIP, // 可空
OpActor: "user-123",
Timestamp: time.Now(),
ClientIP: &clientIP, // 可空
ServerIP: &serverIP, // 可空
}
if err := client.OperationPublish(ctx, op); err != nil {
panic(err)
}
// ✅ 落库成功CursorWorker 会自动异步存证
// ✅ 落库成功CursorWorker 会自动异步存证(带签名)
println("操作已保存,正在异步存证...")
}
```
#### 3.2 三种持久化策略
| 策略 | 说明 | 适用场景 |
|------|------|----------|
| **StrategyDBOnly** | 仅落库,不存证 | 历史数据存档、审计日志 |
| **StrategyDBAndTrustlog** | 既落库又存证(异步) | ⭐ 生产环境推荐 |
| **StrategyTrustlogOnly** | 仅存证,不落库 | 轻量级场景 |
| 策略 | 说明 | 是否签名 | 适用场景 |
|------|------|---------|----------|
| **StrategyDBOnly** | 仅落库,不存证 | ❌ 不签名 | 历史数据存档、审计日志 |
| **StrategyDBAndTrustlog** | 既落库又存证(异步) |**签名存证** | ⭐ 生产环境推荐 |
| **StrategyTrustlogOnly** | 仅存证,不落库 |**签名存证** | 轻量级场景 |
**重要说明**
- 所有存证操作(`StrategyDBAndTrustlog``StrategyTrustlogOnly`)都会使用 `EnvelopeConfig` 进行 **SM2 签名**
- `StrategyDBOnly` 仅保存到数据库,不会进行签名和存证
- 创建 `PersistenceClient` 时**必须**提供 `EnvelopeConfig`(即使是 `StrategyDBOnly` 也建议提供,以便后续切换策略)
#### 3.3 Cursor + Retry 双层架构
@@ -717,13 +747,13 @@ func main() {
CursorWorker第一道防线
├── 增量扫描 operation 表
├── 快速尝试存证
├── 快速尝试存证(使用 Envelope 签名)✅
├── 成功 → 更新状态
└── 失败 → 加入 retry 表
RetryWorker第二道防线
├── 扫描 retry 表
├── 指数退避重试
├── 指数退避重试(使用 Envelope 签名)✅
├── 成功 → 删除 retry 记录
└── 失败 → 标记死信
```
@@ -733,6 +763,7 @@ RetryWorker第二道防线
- ✅ 双层保障确保最终一致性
- ✅ 性能优秀,扩展性强
- ✅ 监控清晰,易于维护
-**所有存证操作都经过 SM2 签名验证**
#### 3.4 数据库表设计
@@ -970,13 +1001,15 @@ func main() {
op, _ := model.NewFullOperation(
model.OpSourceDOIP,
model.OpTypeCreate,
dataID,
"admin",
[]byte(`{"action":"create"}`),
[]byte(`{"status":"success"}`),
model.SHA256,
time.Now(),
string(model.OpTypeCreate), // 字符串类型
"10.1000", // doPrefix
"test-repo", // doRepository
"10.1000/test-repo/doc001", // doid
"producer-001", // producerID
"admin", // opActor
[]byte(`{"action":"create"}`), // requestBody
[]byte(`{"status":"success"}`), // responseBody
time.Now(), // timestamp
)
_ = client.OperationPublish(op)
@@ -992,7 +1025,7 @@ func main() {
)
defer queryClient.Close()
listResp, _ := queryClient.ListOperations(ctx, queryclient.ListOperationsRequest{
listResp, _ := queryClient.ListOperations(ctx, queryclient.ListRequest{
PageSize: 10,
DoRepository: "test-repo",
})
@@ -1003,11 +1036,11 @@ func main() {
if len(listResp.Data) > 0 {
firstOp := listResp.Data[0]
validationReq := queryclient.ValidationRequest{
Time: firstOp.Meta.Timestamp,
OpID: firstOp.Meta.OpID,
OpType: string(firstOp.Meta.OpType),
DoRepository: firstOp.DataID.DoRepository,
validationReq := queryclient.ValidateRequest{
Timestamp: firstOp.Timestamp,
OpID: firstOp.OpID,
OpType: firstOp.OpType, // 已经是字符串
DoRepository: firstOp.DoRepository,
}
result, _ := queryClient.ValidateOperationSync(ctx, validationReq, nil)
@@ -1027,17 +1060,22 @@ func main() {
### DOIP 操作类型7种
```go
model.OpTypeHello // Hello 握手
model.OpTypeRetrieve // 检索资源
model.OpTypeCreate // 新建资源
model.OpTypeDelete // 删除资源
model.OpTypeUpdate // 更新资源
model.OpTypeSearch // 搜索资源
model.OpTypeListOperations // 列出可用操作
// 使用时需要转换为字符串
string(model.OpTypeHello) // "Hello"
string(model.OpTypeRetrieve) // "Retrieve"
string(model.OpTypeCreate) // "Create"
string(model.OpTypeDelete) // "Delete"
string(model.OpTypeUpdate) // "Update"
string(model.OpTypeSearch) // "Search"
string(model.OpTypeListOperations) // "ListOperations"
```
### IRP 操作类型33种
```go
// 使用时需要转换为字符串,例如:
string(model.OpTypeOCCreateHandle) // "OC_CREATE_HANDLE"
string(model.OpTypeOCDeleteHandle) // "OC_DELETE_HANDLE"
// Handle 基础操作
model.OpTypeOCReserved, model.OpTypeOCResolution, model.OpTypeOCGetSiteInfo
model.OpTypeOCCreateHandle, model.OpTypeOCDeleteHandle, model.OpTypeOCAddValue
@@ -1179,13 +1217,13 @@ model.OpTypeOCQueryRouter
[CursorWorker每10秒]
├── 增量扫描 operation 表
├── 尝试发送到存证系统
├── 尝试发送到存证系统Envelope 签名)✅
├── 成功 → 更新状态为 TRUSTLOGGED
└── 失败 → 加入 trustlog_retry 表
[RetryWorker每30秒]
├── 扫描 trustlog_retry 表
├── 指数退避重试(1m → 2m → 4m → 8m → 16m
├── 指数退避重试(Envelope 签名)✅
├── 成功 → 删除 retry 记录
└── 失败 → 标记为 DEAD_LETTER
@@ -1194,6 +1232,7 @@ model.OpTypeOCQueryRouter
- ✅ 双层保障确保最终一致性
- ✅ 性能优秀(增量扫描 + 索引查询)
- ✅ 易于监控和运维
- ✅ 所有存证操作都经过 SM2 签名验证
```
---