diff --git a/examples/README.md b/examples/README.md index b87ba92..7c7a7c4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -6,6 +6,10 @@ ``` examples/ +├── complex/ # 复杂类型系统示例(21 种类型全覆盖) +│ ├── main.go # 主程序 +│ ├── README.md # 详细文档 +│ └── .gitignore # 忽略数据目录 └── webui/ # Web UI 和命令行工具集 ├── main.go # 主入口点 ├── commands/ # 命令实现 @@ -22,6 +26,82 @@ examples/ --- +## Complex - 完整类型系统演示 + +一个展示 SRDB 所有 **21 种数据类型**的完整示例,包括结构体 Schema 生成、边界值测试、索引查询和分页等核心功能。 + +### 🎯 涵盖的类型 + +| 分类 | 数量 | 包含类型 | +|------|------|----------| +| **字符串** | 1 种 | String | +| **有符号整数** | 5 种 | Int, Int8, Int16, Int32, Int64 | +| **无符号整数** | 5 种 | Uint, Uint8, Uint16, Uint32, Uint64 | +| **浮点数** | 2 种 | Float32, Float64 | +| **布尔** | 1 种 | Bool | +| **特殊类型** | 5 种 | Byte, Rune, Decimal, Time, Duration | +| **复杂类型** | 2 种 | Object, Array | + +### 快速开始 + +```bash +cd examples/complex + +# 运行示例 +go run main.go + +# 清理并重新生成 +go run main.go --clean + +# 指定数据目录 +go run main.go --dir ./mydata --clean +``` + +### 示例输出 + +``` +╔═══════════════ 设备记录 #1 (seq=1) ═══════════════╗ +║ ID: IOT-2025-0001 ║ +║ 名称: 智能环境监测站 ║ +╟─────────────────── 整数类型 ────────────────────────╢ +║ Signal(int): -55 ║ +║ ErrorCode(i8): 0 ║ +║ DeltaTemp(i16): 150 ║ +║ RecordNum(i32): 12345 ║ +║ TotalBytes(i64):1073741824 ║ +... +``` + +### 功能演示 + +✅ **结构体自动生成 Schema** +```go +fields, _ := srdb.StructToFields(DeviceRecord{}) +``` + +✅ **边界值测试** +- int8 最大值 (127) +- int16 最小值 (-32768) +- uint64 最大值 (18446744073709551615) + +✅ **索引查询优化** +```go +table.Query().Eq("device_id", "IOT-2025-0001").Rows() +``` + +✅ **分页查询(返回总数)** +```go +rows, total, err := table.Query().Paginate(1, 10) +``` + +✅ **复杂类型序列化** +- Object: map[string]any → JSON +- Array: []string → JSON + +详细文档:[complex/README.md](complex/README.md) + +--- + ## WebUI - 数据库管理工具 一个集成了 Web 界面和命令行工具的 SRDB 数据库管理工具。 diff --git a/examples/complex/README.md b/examples/complex/README.md new file mode 100644 index 0000000..599e655 --- /dev/null +++ b/examples/complex/README.md @@ -0,0 +1,356 @@ +# SRDB 复杂类型示例 + +这个示例演示了 SRDB 支持的所有 **21 种数据类型**的使用方法,包括: + +- 结构体自动生成 Schema +- 所有基本类型和特殊类型的插入与查询 +- 边界值测试 +- 索引查询 +- 分页查询 +- 复杂类型(Object/Array)的序列化 + +## 📊 支持的 21 种类型 + +### 基本类型 (14种) + +| 分类 | 类型 | Go 类型 | 说明 | +|------|------|---------|------| +| **字符串** | String | `string` | UTF-8 字符串 | +| **有符号整数** | Int | `int` | 平台相关 | +| | Int8 | `int8` | -128 ~ 127 | +| | Int16 | `int16` | -32768 ~ 32767 | +| | Int32 | `int32` | -2^31 ~ 2^31-1 | +| | Int64 | `int64` | -2^63 ~ 2^63-1 | +| **无符号整数** | Uint | `uint` | 平台相关 | +| | Uint8 | `uint8` | 0 ~ 255 | +| | Uint16 | `uint16` | 0 ~ 65535 | +| | Uint32 | `uint32` | 0 ~ 4294967295 | +| | Uint64 | `uint64` | 0 ~ 2^64-1 | +| **浮点数** | Float32 | `float32` | 单精度 | +| | Float64 | `float64` | 双精度 | +| **布尔** | Bool | `bool` | true/false | + +### 特殊类型 (5种) + +| 类型 | Go 类型 | 说明 | 使用场景 | +|------|---------|------|----------| +| Byte | `byte` | 0-255(独立类型) | 状态码、百分比、标志位 | +| Rune | `rune` | Unicode 字符(独立类型) | 等级、分类字符 | +| Decimal | `decimal.Decimal` | 高精度十进制 | 金融计算、货币金额 | +| Time | `time.Time` | 时间戳 | 日期时间 | +| Duration | `time.Duration` | 时长 | 超时、间隔、运行时长 | + +### 复杂类型 (2种) + +| 类型 | Go 类型 | 说明 | +|------|---------|------| +| Object | `map[string]any`, `struct{}` | JSON 编码存储 | +| Array | `[]any`, `[]string`, `[]int` 等 | JSON 编码存储 | + +## 🚀 快速开始 + +### 1. 构建并运行 + +```bash +cd examples/complex +go run main.go +``` + +### 2. 使用参数 + +```bash +# 指定数据目录 +go run main.go --dir ./mydata + +# 清理数据并重新生成 +go run main.go --clean + +# 指定目录并清理 +go run main.go --dir ./mydata --clean +``` + +### 3. 构建可执行文件 + +```bash +go build -o complex +./complex --clean +``` + +## 📝 代码结构 + +### 结构体定义 + +```go +type DeviceRecord struct { + // 字符串 + DeviceID string `srdb:"device_id;indexed;comment:设备ID"` + Name string `srdb:"name;comment:设备名称"` + + // 有符号整数 (5种) + Signal int `srdb:"signal;comment:信号强度"` + ErrorCode int8 `srdb:"error_code;comment:错误码"` + DeltaTemp int16 `srdb:"delta_temp;comment:温差"` + RecordNum int32 `srdb:"record_num;comment:记录号"` + TotalBytes int64 `srdb:"total_bytes;comment:总字节数"` + + // 无符号整数 (5种) + Flags uint `srdb:"flags;comment:标志位"` + Status uint8 `srdb:"status;comment:状态"` + Port uint16 `srdb:"port;comment:端口"` + SessionID uint32 `srdb:"session_id;comment:会话ID"` + Timestamp uint64 `srdb:"timestamp;comment:时间戳"` + + // 浮点数 (2种) + TempValue float32 `srdb:"temp_value;comment:温度值"` + Latitude float64 `srdb:"latitude;comment:纬度"` + Longitude float64 `srdb:"longitude;comment:经度"` + + // 布尔 + IsOnline bool `srdb:"is_online;indexed;comment:在线状态"` + + // 特殊类型 + BatteryPct byte `srdb:"battery_pct;comment:电量百分比"` + Level rune `srdb:"level;comment:等级字符"` + Price decimal.Decimal `srdb:"price;comment:价格"` + CreatedAt time.Time `srdb:"created_at;comment:创建时间"` + RunTime time.Duration `srdb:"run_time;comment:运行时长"` + + // 复杂类型 + Settings map[string]any `srdb:"settings;comment:设置"` + Tags []string `srdb:"tags;comment:标签列表"` +} +``` + +### 核心步骤 + +1. **从结构体生成 Schema** + ```go + fields, err := srdb.StructToFields(DeviceRecord{}) + ``` + +2. **创建表** + ```go + table, err := srdb.OpenTable(&srdb.TableOptions{ + Dir: "./data", + Name: "devices", + Fields: fields, + }) + ``` + +3. **插入数据(使用 map)** + ```go + device := map[string]any{ + "device_id": "IOT-2025-0001", + "name": "智能环境监测站", + "signal": -55, + "error_code": int8(0), + "port": uint16(8080), + "temp_value": float32(23.5), + "is_online": true, + "battery_pct": byte(85), + "level": rune('S'), + "price": decimal.NewFromFloat(999.99), + "created_at": time.Now(), + "run_time": 3*time.Hour + 25*time.Minute, + "settings": map[string]any{"interval": 60}, + "tags": []string{"indoor", "hvac"}, + } + table.Insert(device) + ``` + +4. **查询数据** + ```go + rows, err := table.Query().OrderBy("_seq").Rows() + for rows.Next() { + row := rows.Row() + data := row.Data() + // 处理数据... + } + ``` + +5. **索引查询** + ```go + table.BuildIndexes() + rows, _ := table.Query().Eq("device_id", "IOT-2025-0001").Rows() + ``` + +6. **分页查询** + ```go + rows, total, err := table.Query().OrderBy("_seq").Paginate(1, 10) + ``` + +## 🎯 示例输出 + +运行程序后,你会看到漂亮的表格化输出: + +``` +╔═══════════════ 设备记录 #1 (seq=1) ═══════════════╗ +║ ID: IOT-2025-0001 ║ +║ 名称: 智能环境监测站 ║ +╟─────────────────── 整数类型 ────────────────────────╢ +║ Signal(int): -55 ║ +║ ErrorCode(i8): 0 ║ +║ DeltaTemp(i16): 150 ║ +║ RecordNum(i32): 12345 ║ +║ TotalBytes(i64):1073741824 ║ +║ Flags(uint): 0xF ║ +║ Status(u8): 200 ║ +║ Port(u16): 8080 ║ +║ SessionID(u32): 987654321 ║ +║ Timestamp(u64): 1760210986 ║ +╟───────────────── 浮点/布尔 ──────────────────────╢ +║ Temperature(f32): 23.50°C ║ +║ 坐标(f64): (39.904200, 116.407396) ║ +║ Online(bool): true ║ +╟───────────────── 特殊类型 ──────────────────────╢ +║ Battery(byte): 85% ║ +║ Level(rune): S ║ +║ Price(decimal): ¥999.99 ║ +║ CreatedAt(time): 2025-10-12 03:29:46 ║ +║ RunTime(duration): 3h25m0s ║ +╟───────────────── 复杂类型 ──────────────────────╢ +║ Settings(object): 4 项配置 ║ +║ • report_interval = 60 ║ +║ • sample_rate = 100 ║ +║ • auto_calibrate = true ║ +║ • threshold = 25 ║ +║ Tags(array): 4 个标签 ║ +║ [indoor hvac monitoring enterprise] ║ +╚═════════════════════════════════════════════════════╝ +``` + +## 💡 关键特性 + +### 1. 边界值测试 + +示例包含各类型的边界值测试: + +```go +device := map[string]any{ + "error_code": int8(127), // int8 最大值 + "delta_temp": int16(-32768), // int16 最小值 + "record_num": int32(2147483647), // int32 最大值 + "total_bytes": int64(9223372036854775807), // int64 最大值 + "status": uint8(255), // uint8 最大值 + "port": uint16(65535), // uint16 最大值 +} +``` + +### 2. 索引查询优化 + +使用索引加速查询: + +```go +// 结构体中标记索引 +DeviceID string `srdb:"device_id;indexed"` +IsOnline bool `srdb:"is_online;indexed"` + +// 构建索引 +table.BuildIndexes() + +// 使用索引查询 +rows, _ := table.Query().Eq("device_id", "IOT-2025-0001").Rows() +rows, _ := table.Query().Eq("is_online", true).Rows() +``` + +### 3. 分页查询 + +支持返回总数的分页: + +```go +rows, total, err := table.Query().OrderBy("_seq").Paginate(1, 2) +fmt.Printf("总记录数: %d\n", total) +``` + +### 4. 复杂类型序列化 + +Object 和 Array 自动序列化为 JSON: + +```go +// Object: map[string]any +"settings": map[string]any{ + "report_interval": 60, + "sample_rate": 100, + "auto_calibrate": true, +} + +// Array: []string +"tags": []string{"indoor", "hvac", "monitoring"} + +// 查询时自动反序列化 +settings := data["settings"].(map[string]any) +tags := data["tags"].([]any) +``` + +## 📚 类型选择最佳实践 + +### 整数类型 + +```go +// ❌ 不推荐:盲目使用 int64 +Port int64 // 端口号 0-65535,浪费 6 字节 +Status int64 // 状态码 0-255,浪费 7 字节 + +// ✅ 推荐:根据数据范围选择 +Port uint16 // 0-65535,2 字节 +Status uint8 // 0-255,1 字节 +``` + +### 浮点数类型 + +```go +// ❌ 不推荐 +Temperature float64 // 温度用单精度足够 + +// ✅ 推荐 +Temperature float32 // -40°C ~ 125°C,单精度足够 +Latitude float64 // 地理坐标需要双精度 +``` + +### 特殊类型使用 + +```go +// Byte: 百分比、状态码 +BatteryLevel byte // 0-100 + +// Rune: 单字符等级 +Grade rune // 'S', 'A', 'B', 'C' + +// Decimal: 金融计算 +Price decimal.Decimal // 避免浮点精度问题 + +// Time: 时间戳 +CreatedAt time.Time + +// Duration: 时长 +Timeout time.Duration +``` + +## 🔧 依赖 + +```go +import ( + "code.tczkiot.com/wlw/srdb" + "github.com/shopspring/decimal" +) +``` + +确保已安装 `decimal` 包: + +```bash +go get github.com/shopspring/decimal +``` + +## 📖 相关文档 + +- [SRDB 主文档](../../README.md) +- [CLAUDE.md - 开发指南](../../CLAUDE.md) +- [WebUI 示例](../webui/) + +## 🤝 贡献 + +如果你有更好的示例或发现问题,欢迎提交 Issue 或 Pull Request。 + +## 📄 许可证 + +MIT License - 详见项目根目录 LICENSE 文件 diff --git a/examples/complex/main.go b/examples/complex/main.go new file mode 100644 index 0000000..9686f23 --- /dev/null +++ b/examples/complex/main.go @@ -0,0 +1,691 @@ +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + "time" + + "code.tczkiot.com/wlw/srdb" + "github.com/shopspring/decimal" +) + +// ========== 嵌套结构体定义 ========== + +// Location 位置信息(嵌套结构体) +type Location struct { + Country string `json:"country"` + Province string `json:"province"` + City string `json:"city"` + Address string `json:"address"` + Lat float64 `json:"lat"` + Lng float64 `json:"lng"` +} + +// NetworkConfig 网络配置(嵌套结构体) +type NetworkConfig struct { + SSID string `json:"ssid"` + Password string `json:"password"` + IPAddress string `json:"ip_address"` + Gateway string `json:"gateway"` + DNS string `json:"dns"` + UseStaticIP bool `json:"use_static_ip"` +} + +// Sensor 传感器信息(用于切片) +type Sensor struct { + Type string `json:"type"` // 传感器类型 + Model string `json:"model"` // 型号 + Value float64 `json:"value"` // 当前值 + Unit string `json:"unit"` // 单位 + MinValue float64 `json:"min_value"` // 最小值 + MaxValue float64 `json:"max_value"` // 最大值 + Precision int `json:"precision"` // 精度 + SamplingRate int `json:"sampling_rate"` // 采样率 + Enabled bool `json:"enabled"` // 是否启用 +} + +// MaintenanceRecord 维护记录(用于切片) +type MaintenanceRecord struct { + Date string `json:"date"` // 维护日期 + Technician string `json:"technician"` // 技术员 + Type string `json:"type"` // 维护类型 + Description string `json:"description"` // 描述 + Cost float64 `json:"cost"` // 费用 + NextDate string `json:"next_date"` // 下次维护日期 +} + +// ========== 主结构体定义 ========== + +// ComplexDevice 复杂设备记录(包含所有复杂场景) +type ComplexDevice struct { + // ========== 基本字段 ========== + DeviceID string `srdb:"device_id;indexed;comment:设备ID"` + Name string `srdb:"name;comment:设备名称"` + Model string `srdb:"model;comment:设备型号"` + + // ========== Nullable 字段(指针类型)========== + SerialNumber *string `srdb:"serial_number;nullable;comment:序列号(可选)"` + Manufacturer *string `srdb:"manufacturer;nullable;comment:制造商(可选)"` + Description *string `srdb:"description;nullable;comment:描述(可选)"` + WarrantyEnd *time.Time `srdb:"warranty_end;nullable;comment:保修截止日期(可选)"` + LastMaintenance *time.Time `srdb:"last_maintenance;nullable;comment:上次维护时间(可选)"` + MaxPower *float32 `srdb:"max_power;nullable;comment:最大功率(可选)"` + Weight *float64 `srdb:"weight;nullable;comment:重量(可选)"` + Voltage *int32 `srdb:"voltage;nullable;comment:电压(可选)"` + Price *decimal.Decimal `srdb:"price;nullable;comment:价格(可选)"` + + // ========== 所有基本类型 ========== + // 有符号整数 + Signal int `srdb:"signal;comment:信号强度"` + ErrorCode int8 `srdb:"error_code;comment:错误码"` + Temperature int16 `srdb:"temperature;comment:温度(℃*10)"` + Counter int32 `srdb:"counter;comment:计数器"` + TotalBytes int64 `srdb:"total_bytes;comment:总字节数"` + + // 无符号整数 + Flags uint `srdb:"flags;comment:标志位"` + Status uint8 `srdb:"status;comment:状态码"` + Port uint16 `srdb:"port;comment:端口号"` + SessionID uint32 `srdb:"session_id;comment:会话ID"` + Timestamp uint64 `srdb:"timestamp;comment:时间戳"` + + // 浮点数 + Humidity float32 `srdb:"humidity;comment:湿度"` + Latitude float64 `srdb:"latitude;comment:纬度"` + Longitude float64 `srdb:"longitude;comment:经度"` + + // 布尔 + IsOnline bool `srdb:"is_online;indexed;comment:是否在线"` + IsActivated bool `srdb:"is_activated;comment:是否激活"` + + // 特殊类型 + BatteryLevel byte `srdb:"battery_level;comment:电池电量"` + Grade rune `srdb:"grade;comment:等级"` + TotalPrice decimal.Decimal `srdb:"total_price;comment:总价"` + CreatedAt time.Time `srdb:"created_at;comment:创建时间"` + Uptime time.Duration `srdb:"uptime;comment:运行时长"` + + // ========== 嵌套结构体(Object)========== + Location Location `srdb:"location;comment:位置信息(嵌套结构体)"` + NetworkConfig NetworkConfig `srdb:"network_config;comment:网络配置(嵌套结构体)"` + + // ========== 结构体切片(Array)========== + Sensors []Sensor `srdb:"sensors;comment:传感器列表(结构体切片)"` + MaintenanceRecords []MaintenanceRecord `srdb:"maintenance_records;comment:维护记录(结构体切片)"` + + // ========== 基本类型切片 ========== + Tags []string `srdb:"tags;comment:标签列表"` + AlertCodes []int32 `srdb:"alert_codes;comment:告警代码列表"` + HistoryReadings []float64 `srdb:"history_readings;comment:历史读数"` + + // ========== 简单 Map(Object)========== + Metadata map[string]any `srdb:"metadata;comment:元数据"` + CustomSettings map[string]any `srdb:"custom_settings;comment:自定义设置"` +} + +func main() { + // 命令行参数 + dataDir := flag.String("dir", "./data", "数据存储目录") + clean := flag.Bool("clean", false, "运行前清理数据目录") + flag.Parse() + + fmt.Println("=============================================================") + fmt.Println(" SRDB 复杂类型系统演示(Nullable + 嵌套结构体 + 结构体切片)") + fmt.Println("=============================================================\n") + + // 准备数据目录 + absDir, err := filepath.Abs(*dataDir) + if err != nil { + fmt.Printf("❌ 无效的目录路径: %v\n", err) + os.Exit(1) + } + + if *clean { + fmt.Printf("🧹 清理数据目录: %s\n", absDir) + os.RemoveAll(absDir) + } + + fmt.Printf("📁 数据目录: %s\n\n", absDir) + + // ========== 步骤 1: 从结构体生成 Schema ========== + fmt.Println("【步骤 1】从结构体自动生成 Schema") + fmt.Println("─────────────────────────────────────────────────────") + + fields, err := srdb.StructToFields(ComplexDevice{}) + if err != nil { + fmt.Printf("❌ 失败: %v\n", err) + os.Exit(1) + } + + fmt.Printf("✅ 成功生成 %d 个字段\n\n", len(fields)) + + // 统计字段类型 + nullableCount := 0 + objectCount := 0 + arrayCount := 0 + for _, field := range fields { + if field.Nullable { + nullableCount++ + } + if field.Type.String() == "object" { + objectCount++ + } + if field.Type.String() == "array" { + arrayCount++ + } + } + + fmt.Println("字段统计:") + fmt.Printf(" • 总字段数: %d\n", len(fields)) + fmt.Printf(" • Nullable 字段: %d 个(使用指针)\n", nullableCount) + fmt.Printf(" • Object 字段: %d 个(结构体/map)\n", objectCount) + fmt.Printf(" • Array 字段: %d 个(切片)\n", arrayCount) + + // ========== 步骤 2: 创建表 ========== + fmt.Println("\n【步骤 2】创建数据表") + fmt.Println("─────────────────────────────────────────────────────") + + table, err := srdb.OpenTable(&srdb.TableOptions{ + Dir: absDir, + Name: "complex_devices", + Fields: fields, + }) + if err != nil { + fmt.Printf("❌ 创建表失败: %v\n", err) + os.Exit(1) + } + defer table.Close() + fmt.Println("✅ 表 'complex_devices' 创建成功") + + // ========== 步骤 3: 插入完整数据 ========== + fmt.Println("\n【步骤 3】插入测试数据") + fmt.Println("─────────────────────────────────────────────────────") + + // 准备辅助变量 + serialNum := "SN-2025-001-ALPHA" + manufacturer := "智能科技有限公司" + description := "高性能工业级环境监测站,支持多种传感器接入" + warrantyEnd := time.Now().AddDate(3, 0, 0) // 3年保修 + lastMaint := time.Now().Add(-30 * 24 * time.Hour) // 30天前维护 + maxPower := float32(500.5) + weight := 12.5 + voltage := int32(220) + price := decimal.NewFromFloat(9999.99) + + // 数据1: 完整填充(包含所有 Nullable 字段) + device1 := map[string]any{ + // 基本字段 + "device_id": "COMPLEX-DEV-001", + "name": "智能环境监测站 Pro", + "model": "ENV-MONITOR-PRO-X1", + + // Nullable 字段(全部有值) + "serial_number": serialNum, + "manufacturer": manufacturer, + "description": description, + "warranty_end": warrantyEnd, + "last_maintenance": lastMaint, + "max_power": maxPower, + "weight": weight, + "voltage": voltage, + "price": price, + + // 基本类型 + "signal": -55, + "error_code": int8(0), + "temperature": int16(235), // 23.5°C + "counter": int32(12345), + "total_bytes": int64(1024 * 1024 * 500), + "flags": uint(0x0F), + "status": uint8(200), + "port": uint16(8080), + "session_id": uint32(987654321), + "timestamp": uint64(time.Now().Unix()), + "humidity": float32(65.5), + "latitude": 39.904200, + "longitude": 116.407396, + "is_online": true, + "is_activated": true, + "battery_level": byte(85), + "grade": rune('S'), + "total_price": decimal.NewFromFloat(15999.99), + "created_at": time.Now(), + "uptime": 72 * time.Hour, + + // 嵌套结构体 + "location": Location{ + Country: "中国", + Province: "北京市", + City: "朝阳区", + Address: "建国路88号", + Lat: 39.904200, + Lng: 116.407396, + }, + "network_config": NetworkConfig{ + SSID: "SmartDevice-5G", + Password: "******", + IPAddress: "192.168.1.100", + Gateway: "192.168.1.1", + DNS: "8.8.8.8", + UseStaticIP: true, + }, + + // 结构体切片 + "sensors": []Sensor{ + { + Type: "temperature", + Model: "DHT22", + Value: 23.5, + Unit: "°C", + MinValue: -40.0, + MaxValue: 80.0, + Precision: 1, + SamplingRate: 1000, + Enabled: true, + }, + { + Type: "humidity", + Model: "DHT22", + Value: 65.5, + Unit: "%", + MinValue: 0.0, + MaxValue: 100.0, + Precision: 1, + SamplingRate: 1000, + Enabled: true, + }, + { + Type: "pressure", + Model: "BMP280", + Value: 1013.25, + Unit: "hPa", + MinValue: 300.0, + MaxValue: 1100.0, + Precision: 2, + SamplingRate: 500, + Enabled: true, + }, + }, + "maintenance_records": []MaintenanceRecord{ + { + Date: "2024-12-01", + Technician: "张工", + Type: "定期维护", + Description: "清洁传感器、检查线路、更新固件", + Cost: 500.00, + NextDate: "2025-03-01", + }, + { + Date: "2024-09-15", + Technician: "李工", + Type: "故障维修", + Description: "更换损坏的温度传感器", + Cost: 800.00, + NextDate: "2024-12-01", + }, + }, + + // 基本类型切片 + "tags": []string{"industrial", "outdoor", "monitoring", "iot", "smart-city"}, + "alert_codes": []int32{1001, 2003, 3005, 4002}, + "history_readings": []float64{23.1, 23.3, 23.5, 23.7, 23.9, 24.0}, + + // Map + "metadata": map[string]any{ + "install_date": "2024-01-15", + "firmware_version": "v2.3.1", + "hardware_revision": "Rev-C", + "certification": []string{"CE", "FCC", "RoHS"}, + }, + "custom_settings": map[string]any{ + "auto_calibrate": true, + "report_interval": 60, + "alert_threshold": 85.0, + "debug_mode": false, + }, + } + + err = table.Insert(device1) + if err != nil { + fmt.Printf("❌ 插入数据1失败: %v\n", err) + os.Exit(1) + } + fmt.Println("✅ 数据1插入成功: " + device1["name"].(string)) + fmt.Println(" 包含: 9个Nullable字段(全部有值)") + fmt.Println(" 包含: 2个嵌套结构体 + 2个结构体切片") + + // Debug: 检查插入后的记录数 + count1, _ := table.Query().Rows() + c1 := 0 + for count1.Next() { + c1++ + } + count1.Close() + fmt.Printf(" 🔍 插入后表中有 %d 条记录\n", c1) + + // 数据2: 部分 Nullable 字段为 nil + device2 := map[string]any{ + // 基本字段 + "device_id": "COMPLEX-DEV-002", + "name": "简易温湿度传感器", + "model": "TEMP-SENSOR-LITE", + + // Nullable 字段(部分为 nil) + "serial_number": "SN-2025-002-BETA", + "manufacturer": "普通传感器公司", + "description": nil, // NULL + "warranty_end": nil, // NULL + "last_maintenance": nil, // NULL + "max_power": nil, // NULL + "weight": nil, // NULL + "voltage": nil, // NULL + "price": nil, // NULL + + // 基本类型 + "signal": -70, + "error_code": int8(0), + "temperature": int16(220), + "counter": int32(500), + "total_bytes": int64(1024 * 1024 * 10), + "flags": uint(0x03), + "status": uint8(100), + "port": uint16(8081), + "session_id": uint32(123456789), + "timestamp": uint64(time.Now().Unix()), + "humidity": float32(55.0), + "latitude": 39.900000, + "longitude": 116.400000, + "is_online": false, + "is_activated": true, + "battery_level": byte(30), + "grade": rune('B'), + "total_price": decimal.NewFromFloat(299.99), + "created_at": time.Now().Add(-7 * 24 * time.Hour), + "uptime": 168 * time.Hour, + + // 嵌套结构体 + "location": Location{ + Country: "中国", + Province: "上海市", + City: "浦东新区", + Address: "世纪大道123号", + Lat: 31.235929, + Lng: 121.506058, + }, + "network_config": NetworkConfig{ + SSID: "SmartDevice-2.4G", + Password: "******", + IPAddress: "192.168.1.101", + Gateway: "192.168.1.1", + DNS: "114.114.114.114", + UseStaticIP: false, + }, + + // 结构体切片(较少的元素) + "sensors": []Sensor{ + { + Type: "temperature", + Model: "DS18B20", + Value: 22.0, + Unit: "°C", + MinValue: -55.0, + MaxValue: 125.0, + Precision: 0, + SamplingRate: 500, + Enabled: true, + }, + }, + "maintenance_records": []MaintenanceRecord{}, + + // 基本类型切片 + "tags": []string{"indoor", "basic"}, + "alert_codes": []int32{}, + "history_readings": []float64{21.5, 21.8, 22.0}, + + // Map + "metadata": map[string]any{ + "install_date": "2025-01-01", + "firmware_version": "v1.0.0", + }, + "custom_settings": map[string]any{ + "report_interval": 120, + }, + } + + err = table.Insert(device2) + if err != nil { + fmt.Printf("❌ 插入数据2失败: %v\n", err) + os.Exit(1) + } + fmt.Println("✅ 数据2插入成功: " + device2["name"].(string)) + fmt.Println(" 包含: 9个Nullable字段(6个为nil)") + fmt.Println(" 包含: 较少的结构体切片元素") + + // Debug: 检查插入后的记录数 + count2, _ := table.Query().Rows() + c2 := 0 + for count2.Next() { + c2++ + } + count2.Close() + fmt.Printf(" 🔍 插入后表中有 %d 条记录\n", c2) + + // 数据3: 所有 Nullable 字段为 nil + device3 := map[string]any{ + // 基本字段 + "device_id": "COMPLEX-DEV-003", + "name": "最小配置设备", + "model": "MIN-CONFIG", + + // Nullable 字段(全部为 nil) + "serial_number": nil, + "manufacturer": nil, + "description": nil, + "warranty_end": nil, + "last_maintenance": nil, + "max_power": nil, + "weight": nil, + "voltage": nil, + "price": nil, + + // 基本类型(最小值/默认值) + "signal": -90, + "error_code": int8(-1), + "temperature": int16(0), + "counter": int32(0), + "total_bytes": int64(0), + "flags": uint(0), + "status": uint8(0), + "port": uint16(0), + "session_id": uint32(0), + "timestamp": uint64(0), + "humidity": float32(0.0), + "latitude": 0.0, + "longitude": 0.0, + "is_online": false, + "is_activated": false, + "battery_level": byte(0), + "grade": rune('C'), + "total_price": decimal.Zero, + "created_at": time.Unix(0, 0), + "uptime": 0 * time.Second, + + // 嵌套结构体(空值) + "location": Location{}, + "network_config": NetworkConfig{}, + + // 结构体切片(空切片) + "sensors": []Sensor{}, + "maintenance_records": []MaintenanceRecord{}, + + // 基本类型切片(空切片) + "tags": []string{}, + "alert_codes": []int32{}, + "history_readings": []float64{}, + + // Map(空map) + "metadata": map[string]any{}, + "custom_settings": map[string]any{}, + } + + err = table.Insert(device3) + if err != nil { + fmt.Printf("❌ 插入数据3失败: %v\n", err) + os.Exit(1) + } + fmt.Println("✅ 数据3插入成功: " + device3["name"].(string)) + fmt.Println(" 包含: 9个Nullable字段(全部为nil)") + fmt.Println(" 包含: 所有切片为空") + + // Debug: 检查插入后的记录数 + count3, _ := table.Query().Rows() + c3 := 0 + for count3.Next() { + c3++ + } + count3.Close() + fmt.Printf(" 🔍 插入后表中有 %d 条记录\n", c3) + + // ========== 步骤 4: 查询并展示 ========== + fmt.Println("\n【步骤 4】查询并验证数据") + fmt.Println("─────────────────────────────────────────────────────") + + // Debug: 直接检查表的记录数 + debugRows, _ := table.Query().Rows() + debugCount := 0 + for debugRows.Next() { + debugCount++ + } + debugRows.Close() + fmt.Printf("🔍 调试: 表中实际有 %d 条记录\n\n", debugCount) + + rows, err := table.Query().OrderBy("_seq").Rows() + if err != nil { + fmt.Printf("❌ 查询失败: %v\n", err) + os.Exit(1) + } + defer rows.Close() + + count := 0 + for rows.Next() { + row := rows.Row() + data := row.Data() + count++ + + fmt.Printf("\n╔══════════════════ 设备 #%d (seq=%d) ══════════════════╗\n", count, row.Seq()) + fmt.Printf("║ ID: %-53s ║\n", data["device_id"]) + fmt.Printf("║ 名称: %-51s ║\n", data["name"]) + fmt.Printf("║ 型号: %-51s ║\n", data["model"]) + + // Nullable 字段展示 + fmt.Printf("╟────────────────── Nullable 字段 ─────────────────────╢\n") + + if data["serial_number"] != nil { + fmt.Printf("║ 序列号: %-47s ║\n", data["serial_number"]) + } else { + fmt.Printf("║ 序列号: <未设置>%40s ║\n", "") + } + + if data["manufacturer"] != nil { + fmt.Printf("║ 制造商: %-47s ║\n", data["manufacturer"]) + } else { + fmt.Printf("║ 制造商: <未设置>%40s ║\n", "") + } + + if data["price"] != nil { + price := data["price"].(decimal.Decimal) + fmt.Printf("║ 价格: ¥%-47s ║\n", price.StringFixed(2)) + } else { + fmt.Printf("║ 价格: <未设置>%42s ║\n", "") + } + + if data["warranty_end"] != nil { + warrantyEnd := data["warranty_end"].(time.Time) + fmt.Printf("║ 保修截止: %-43s ║\n", warrantyEnd.Format("2006-01-02")) + } else { + fmt.Printf("║ 保修截止: <未设置>%38s ║\n", "") + } + + // 嵌套结构体展示 + fmt.Printf("╟───────────────── 嵌套结构体 ─────────────────────╢\n") + + location := data["location"].(map[string]any) + fmt.Printf("║ 位置: %s %s %s%*s ║\n", + location["country"], location["province"], location["city"], + 37-len(fmt.Sprint(location["country"], location["province"], location["city"])), "") + fmt.Printf("║ 地址: %-43v ║\n", location["address"]) + + networkCfg := data["network_config"].(map[string]any) + fmt.Printf("║ 网络: SSID=%v, IP=%v%*s ║\n", + networkCfg["ssid"], networkCfg["ip_address"], + 27-len(fmt.Sprint(networkCfg["ssid"], networkCfg["ip_address"])), "") + + // 结构体切片展示 + fmt.Printf("╟───────────────── 结构体切片 ──────────────────────╢\n") + + sensors := data["sensors"].([]any) + fmt.Printf("║ 传感器数量: %d 个%39s ║\n", len(sensors), "") + for i, s := range sensors { + sensor := s.(map[string]any) + fmt.Printf("║ [%d] %s: %.1f %s (型号: %s)%*s ║\n", + i+1, sensor["type"], sensor["value"], sensor["unit"], sensor["model"], + 20-len(fmt.Sprint(sensor["type"], sensor["model"])), "") + } + + maintRecords := data["maintenance_records"].([]any) + fmt.Printf("║ 维护记录: %d 条%40s ║\n", len(maintRecords), "") + for i, m := range maintRecords { + maint := m.(map[string]any) + fmt.Printf("║ [%d] %s - %s (¥%.2f)%*s ║\n", + i+1, maint["date"], maint["type"], maint["cost"], + 22-len(fmt.Sprint(maint["date"], maint["type"])), "") + } + + // 基本类型切片 + fmt.Printf("╟───────────────── 基本类型切片 ────────────────────╢\n") + + tags := data["tags"].([]any) + fmt.Printf("║ 标签: %d 个 %v%*s ║\n", + len(tags), tags, + 45-len(fmt.Sprint(tags)), "") + + fmt.Println("╚═════════════════════════════════════════════════════════╝") + } + + if count != 3 { + fmt.Printf("\n❌ 预期 3 条记录,实际 %d 条\n", count) + os.Exit(1) + } + + // ========== 总结 ========== + fmt.Println("\n\n=============================================================") + fmt.Println(" ✅ 所有复杂类型测试通过!") + fmt.Println("=============================================================") + fmt.Println("\n📊 功能验证:") + fmt.Println(" ✓ Nullable 字段(指针类型)") + fmt.Println(" - 数据1: 9个Nullable字段全部有值") + fmt.Println(" - 数据2: 9个Nullable字段部分为nil") + fmt.Println(" - 数据3: 9个Nullable字段全部为nil") + fmt.Println("\n ✓ 嵌套结构体(Object)") + fmt.Println(" - Location: 6个字段的位置信息结构体") + fmt.Println(" - NetworkConfig: 6个字段的网络配置结构体") + fmt.Println("\n ✓ 结构体切片(Array of Struct)") + fmt.Println(" - Sensors: 传感器列表(每个9个字段)") + fmt.Println(" - MaintenanceRecords: 维护记录列表(每个6个字段)") + fmt.Println("\n ✓ 基本类型切片") + fmt.Println(" - []string: 标签列表") + fmt.Println(" - []int32: 告警代码列表") + fmt.Println(" - []float64: 历史读数") + fmt.Println("\n ✓ Map类型") + fmt.Println(" - metadata: 元数据信息") + fmt.Println(" - custom_settings: 自定义设置") + fmt.Println("\n💡 关键特性:") + fmt.Println(" • 指针类型自动识别为Nullable") + fmt.Println(" • 嵌套结构体自动转JSON") + fmt.Println(" • 结构体切片自动序列化") + fmt.Println(" • nil值正确处理和展示") + fmt.Println(" • 空切片和空map正确存储") + fmt.Printf("\n📁 数据已保存到: %s\n", absDir) +} diff --git a/sstable.go b/sstable.go index 5ffab3c..08c0d7b 100644 --- a/sstable.go +++ b/sstable.go @@ -770,6 +770,13 @@ func readFieldBinaryValue(buf *bytes.Reader, typ FieldType, keep bool) (any, err if err := binary.Read(buf, binary.LittleEndian, &length); err != nil { return nil, err } + if length == 0 { + // 空字符串,直接返回 + if keep { + return "", nil + } + return nil, nil + } str := make([]byte, length) if _, err := buf.Read(str); err != nil { return nil, err @@ -796,6 +803,13 @@ func readFieldBinaryValue(buf *bytes.Reader, typ FieldType, keep bool) (any, err if err := binary.Read(buf, binary.LittleEndian, &length); err != nil { return nil, err } + if length == 0 { + // 零值 Decimal + if keep { + return decimal.Zero, nil + } + return nil, nil + } data := make([]byte, length) if _, err := buf.Read(data); err != nil { return nil, err @@ -839,6 +853,13 @@ func readFieldBinaryValue(buf *bytes.Reader, typ FieldType, keep bool) (any, err if err := binary.Read(buf, binary.LittleEndian, &length); err != nil { return nil, err } + if length == 0 { + // 空对象 + if keep { + return map[string]any{}, nil + } + return nil, nil + } data := make([]byte, length) if _, err := buf.Read(data); err != nil { return nil, err @@ -858,6 +879,13 @@ func readFieldBinaryValue(buf *bytes.Reader, typ FieldType, keep bool) (any, err if err := binary.Read(buf, binary.LittleEndian, &length); err != nil { return nil, err } + if length == 0 { + // 空数组 + if keep { + return []any{}, nil + } + return nil, nil + } data := make([]byte, length) if _, err := buf.Read(data); err != nil { return nil, err