From a61b1a8840ade57ba389facdaf9989d0b27d8879 Mon Sep 17 00:00:00 2001 From: ryan <2650306917@qq.com> Date: Thu, 25 Dec 2025 10:55:16 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=BF=AE=E5=A4=8D=20README=20=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E4=B8=8E=E5=BD=93=E5=89=8D=20API=20=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E6=80=A7=20-=20OpType=E6=94=B9=E4=B8=BAstring?= =?UTF-8?q?=EF=BC=8CNewFullOperation=E5=8F=82=E6=95=B0=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=EF=BC=8C=E6=9F=A5=E8=AF=A2API=E7=BB=9F=E4=B8=80=EF=BC=8CPersis?= =?UTF-8?q?tence=E5=AE=8C=E6=95=B4=E7=A4=BA=E4=BE=8B=EF=BC=8C=E6=98=8E?= =?UTF-8?q?=E7=A1=AE=E6=89=80=E6=9C=89=E5=AD=98=E8=AF=81=E7=AD=96=E7=95=A5?= =?UTF-8?q?=E9=83=BD=E6=94=AF=E6=8C=81SM2=E7=AD=BE=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 193 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 116 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 71e547a..4e92f46 100644 --- a/README.md +++ b/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`:数据标识 @@ -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 签名验证 ``` ---