docs: 修复 README 示例与当前 API 的一致性 - OpType改为string,NewFullOperation参数修正,查询API统一,Persistence完整示例,明确所有存证策略都支持SM2签名
This commit is contained in:
159
README.md
159
README.md
@@ -89,7 +89,7 @@ SDK 提供两种数据模型,分别适用于不同的业务场景:
|
||||
- `OpID`:操作唯一标识符(自动生成 UUID v7)
|
||||
- `Timestamp`:操作时间戳(必填)
|
||||
- `OpSource`:操作来源(`DOIP` 或 `IRP`)
|
||||
- `OpType`:操作类型(如 `Create`、`Update`、`Delete` 等)
|
||||
- `OpType`:操作类型(如 `"Create"`、`"Update"`、`"Delete"` 等,字符串类型)
|
||||
- `OpAlgorithm`:哈希算法类型(默认 `Sha256Simd`)
|
||||
- `OpMetaHash`:元数据哈希值(自动计算)
|
||||
- `DataID`:数据标识
|
||||
@@ -105,12 +105,14 @@ SDK 提供两种数据模型,分别适用于不同的业务场景:
|
||||
```go
|
||||
op, err := model.NewFullOperation(
|
||||
model.OpSourceDOIP, // 操作来源
|
||||
model.OpTypeCreate, // 操作类型
|
||||
dataID, // 数据标识
|
||||
"user123", // 操作者
|
||||
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)
|
||||
model.SHA256, // 哈希算法
|
||||
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", // 操作者
|
||||
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"}`), // 响应体
|
||||
model.Sha256Simd, // 哈希算法
|
||||
time.Now(), // 操作时间
|
||||
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 签名配置
|
||||
Logger: myLogger,
|
||||
EnvelopeConfig: envelopeConfig, // ⭐ SM2 签名配置
|
||||
DBConfig: persistence.DBConfig{
|
||||
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,18 +698,20 @@ 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,
|
||||
OpType: string(model.OpTypeCreate), // 字符串类型
|
||||
Doid: "10.1000/repo/obj",
|
||||
ProducerID: "producer-001",
|
||||
OpSource: model.OpSourceDOIP,
|
||||
DoPrefix: "10.1000",
|
||||
DoRepository: "repo",
|
||||
OpActor: "user-123",
|
||||
Timestamp: time.Now(),
|
||||
ClientIP: &clientIP, // 可空
|
||||
ServerIP: &serverIP, // 可空
|
||||
}
|
||||
@@ -695,18 +720,23 @@ func main() {
|
||||
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 签名验证
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user