Files
go-trustlog/api/model/converter.go
ryan fb182adef4 feat: OpType重构为OpCode (int32) - 完整实现
🎯 核心变更:
- OpType (string) → OpCode (int32)
- 20+ OpCode枚举常量 (基于DOIP/IRP标准)
- 类型安全 + 性能优化

📊 影响范围:
- 核心模型: Operation结构体、CBOR序列化
- 数据库: schema.go + SQL DDL (PostgreSQL/MySQL/SQLite)
- 持久化: repository.go查询、cursor_worker.go
- API接口: Protobuf定义 + gRPC客户端
- 测试代码: 60+ 测试文件更新

 测试结果:
- 通过率: 100% (所有87个测试用例)
- 总体覆盖率: 53.7%
- 核心包覆盖率: logger(100%), highclient(95.3%), model(79.1%)

📝 文档:
- 精简README (1056行→489行,减少54%)
- 完整的OpCode枚举说明
- 三种持久化策略示例
- 数据库表结构和架构图

🔧 技术细节:
- 类型转换: string(OpCode) → int32(OpCode)
- SQL参数: 字符串值 → 整数值
- Protobuf: op_type string → op_code int32
- 测试断言: 字符串比较 → 常量比较

🎉 质量保证:
- 零编译错误
- 100%测试通过
- PostgreSQL/Pulsar集成测试验证
- 分布式并发安全测试通过
2025-12-26 13:47:55 +08:00

201 lines
5.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package model
import (
"errors"
"fmt"
"google.golang.org/protobuf/types/known/timestamppb"
"go.yandata.net/iod/iod/go-trustlog/api/grpc/pb"
)
// FromProtobuf 将protobuf的OperationData转换为model.Operation.
func FromProtobuf(pbOp *pb.OperationData) (*Operation, error) {
if pbOp == nil {
return nil, errors.New("protobuf operation data is nil")
}
// 转换时间戳
if pbOp.GetTimestamp() == nil {
return nil, errors.New("timestamp is required")
}
timestamp := pbOp.GetTimestamp().AsTime()
// 构建Operation
operation := &Operation{
OpID: pbOp.GetOpId(),
Timestamp: timestamp,
OpSource: Source(pbOp.GetOpSource()),
OpCode: OpCode(pbOp.GetOpCode()),
DoPrefix: pbOp.GetDoPrefix(),
DoRepository: pbOp.GetDoRepository(),
Doid: pbOp.GetDoid(),
ProducerID: pbOp.GetProducerId(),
OpActor: pbOp.GetOpActor(),
// OpAlgorithm和OpMetaHash字段已移除固定使用Sha256Simd哈希值由Envelope的OriginalHash提供
}
// 处理可选的哈希字段
if reqHash := pbOp.GetRequestBodyHash(); reqHash != "" {
operation.RequestBodyHash = &reqHash
}
if respHash := pbOp.GetResponseBodyHash(); respHash != "" {
operation.ResponseBodyHash = &respHash
}
return operation, nil
}
// ToProtobuf 将model.Operation转换为protobuf的OperationData.
func ToProtobuf(op *Operation) (*pb.OperationData, error) {
if op == nil {
return nil, errors.New("operation is nil")
}
// 转换时间戳
timestamp := timestamppb.New(op.Timestamp)
pbOp := &pb.OperationData{
OpId: op.OpID,
Timestamp: timestamp,
OpSource: string(op.OpSource),
OpCode: int32(op.OpCode),
DoPrefix: op.DoPrefix,
DoRepository: op.DoRepository,
Doid: op.Doid,
ProducerId: op.ProducerID,
OpActor: op.OpActor,
// OpAlgorithm、OpMetaHash和OpHash字段已移除固定使用Sha256Simd哈希值由Envelope的OriginalHash提供
}
// 处理可选的哈希字段
if op.RequestBodyHash != nil {
pbOp.RequestBodyHash = *op.RequestBodyHash
}
if op.ResponseBodyHash != nil {
pbOp.ResponseBodyHash = *op.ResponseBodyHash
}
return pbOp, nil
}
// FromProtobufValidationResult 将protobuf的ValidationStreamRes转换为model.ValidationResult.
func FromProtobufValidationResult(pbRes *pb.ValidationStreamRes) (*ValidationResult, error) {
if pbRes == nil {
return nil, errors.New("protobuf validation result is nil")
}
result := &ValidationResult{
Code: pbRes.GetCode(),
Msg: pbRes.GetMsg(),
Progress: pbRes.GetProgress(),
Proof: ProofFromProtobuf(pbRes.GetProof()), // 取证证明
}
// 如果有操作数据,则转换
if pbRes.GetData() != nil {
op, err := FromProtobuf(pbRes.GetData())
if err != nil {
return nil, fmt.Errorf("failed to convert operation data: %w", err)
}
result.Data = op
}
return result, nil
}
// RecordFromProtobuf 将protobuf的RecordData转换为model.Record.
func RecordFromProtobuf(pbRec *pb.RecordData) (*Record, error) {
if pbRec == nil {
return nil, errors.New("protobuf record data is nil")
}
// 构建Record
record := &Record{
ID: pbRec.GetId(),
DoPrefix: pbRec.GetDoPrefix(),
ProducerID: pbRec.GetProducerId(),
Operator: pbRec.GetOperator(),
Extra: pbRec.GetExtra(),
RCType: pbRec.GetRcType(),
}
// 转换时间戳
if pbRec.GetTimestamp() != nil {
record.Timestamp = pbRec.GetTimestamp().AsTime()
}
return record, nil
}
// RecordToProtobuf 将model.Record转换为protobuf的RecordData.
func RecordToProtobuf(rec *Record) (*pb.RecordData, error) {
if rec == nil {
return nil, errors.New("record is nil")
}
// 转换时间戳
timestamp := timestamppb.New(rec.Timestamp)
pbRec := &pb.RecordData{
Id: rec.ID,
DoPrefix: rec.DoPrefix,
ProducerId: rec.ProducerID,
Timestamp: timestamp,
Operator: rec.Operator,
Extra: rec.Extra,
RcType: rec.RCType,
}
return pbRec, nil
}
// RecordValidationResult 包装记录验证的流式响应结果.
type RecordValidationResult struct {
Code int32 // 状态码100处理中200完成500失败
Msg string // 消息描述
Progress string // 当前进度(比如 "50%"
Data *Record // 最终完成时返回的记录数据,过程中可为空
Proof *Proof // 取证证明(仅在完成时返回)
}
// IsProcessing 判断是否正在处理中.
func (r *RecordValidationResult) IsProcessing() bool {
return r.Code == ValidationCodeProcessing
}
// IsCompleted 判断是否已完成.
func (r *RecordValidationResult) IsCompleted() bool {
return r.Code == ValidationCodeCompleted
}
// IsFailed 判断是否失败.
func (r *RecordValidationResult) IsFailed() bool {
return r.Code >= ValidationCodeFailed
}
// RecordFromProtobufValidationResult 将protobuf的RecordValidationStreamRes转换为model.RecordValidationResult.
func RecordFromProtobufValidationResult(pbRes *pb.RecordValidationStreamRes) (*RecordValidationResult, error) {
if pbRes == nil {
return nil, errors.New("protobuf record validation result is nil")
}
result := &RecordValidationResult{
Code: pbRes.GetCode(),
Msg: pbRes.GetMsg(),
Progress: pbRes.GetProgress(),
Proof: ProofFromProtobuf(pbRes.GetProof()), // 取证证明
}
// 如果有记录数据,则转换
if pbRes.GetResult() != nil {
rec, err := RecordFromProtobuf(pbRes.GetResult())
if err != nil {
return nil, fmt.Errorf("failed to convert record data: %w", err)
}
result.Data = rec
}
return result, nil
}