hotime/example/main.go
hoteas c2955d2500 feat(db): 实现数据库查询中的数组参数展开和空数组处理
- 在 Get 方法中添加无参数时的默认字段和 LIMIT 1 处理
- 实现 expandArrayPlaceholder 方法,自动展开 IN (?) 和 NOT IN (?) 中的数组参数
- 为空数组的 IN 条件生成 1=0 永假条件,NOT IN 生成 1=1 永真条件
- 在 queryWithRetry 和 execWithRetry 中集成数组占位符预处理
- 修复 where.go 中空切片条件的处理逻辑
- 添加完整的 IN/NOT IN 数组查询测试用例
- 更新 .gitignore 规则格式
2026-01-22 07:16:42 +08:00

1043 lines
36 KiB
Go
Raw 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 main
import (
"fmt"
"time"
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
. "code.hoteas.com/golang/hotime/db"
)
func main() {
appIns := Init("config/config.json")
appIns.SetConnectListener(func(that *Context) (isFinished bool) {
return isFinished
})
appIns.Run(Router{
"app": {
"test": {
// 测试入口 - 运行所有测试
"all": func(that *Context) {
results := Map{}
// 初始化测试表
initTestTables(that)
// 1. 基础 CRUD 测试
results["1_basic_crud"] = testBasicCRUD(that)
// 2. 条件查询语法测试
results["2_condition_syntax"] = testConditionSyntax(that)
// 3. 链式查询测试
results["3_chain_query"] = testChainQuery(that)
// 4. JOIN 查询测试
results["4_join_query"] = testJoinQuery(that)
// 5. 聚合函数测试
results["5_aggregate"] = testAggregate(that)
// 6. 分页查询测试
results["6_pagination"] = testPagination(that)
// 7. 批量插入测试
results["7_batch_insert"] = testBatchInsert(that)
// 8. Upsert 测试
results["8_upsert"] = testUpsert(that)
// 9. 事务测试
results["9_transaction"] = testTransaction(that)
// 10. 原生 SQL 测试
results["10_raw_sql"] = testRawSQL(that)
that.Display(0, results)
},
// 查询数据库表结构
"tables": func(that *Context) {
// 查询所有表
tables := that.Db.Query("SHOW TABLES")
that.Display(0, Map{"tables": tables})
},
// 查询指定表的结构
"describe": func(that *Context) {
tableName := that.Req.FormValue("table")
if tableName == "" {
that.Display(1, "请提供 table 参数")
return
}
// 查询表结构
columns := that.Db.Query("DESCRIBE " + tableName)
// 查询表数据前10条
data := that.Db.Select(tableName, Map{"LIMIT": 10})
that.Display(0, Map{"table": tableName, "columns": columns, "sample_data": data})
},
// 单独测试入口
"crud": func(that *Context) { that.Display(0, testBasicCRUD(that)) },
"condition": func(that *Context) { that.Display(0, testConditionSyntax(that)) },
"chain": func(that *Context) { that.Display(0, testChainQuery(that)) },
"join": func(that *Context) { that.Display(0, testJoinQuery(that)) },
"aggregate": func(that *Context) { that.Display(0, testAggregate(that)) },
"pagination": func(that *Context) { that.Display(0, testPagination(that)) },
"batch": func(that *Context) { that.Display(0, testBatchInsert(that)) },
"upsert": func(that *Context) { that.Display(0, testUpsert(that)) },
"transaction": func(that *Context) { that.Display(0, testTransaction(that)) },
"rawsql": func(that *Context) { that.Display(0, testRawSQL(that)) },
},
},
})
}
// initTestTables 初始化测试表MySQL真实数据库
func initTestTables(that *Context) {
// MySQL 真实数据库已有表,创建测试用的临时表
// 创建测试用的批量插入表
that.Db.Exec(`CREATE TABLE IF NOT EXISTS test_batch (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
title VARCHAR(200),
state INT DEFAULT 0,
create_time DATETIME
)`)
// 检查 admin 表数据
adminCount := that.Db.Count("admin")
articleCount := that.Db.Count("article")
}
// ==================== 1. 基础 CRUD 测试 ====================
func testBasicCRUD(that *Context) Map {
result := Map{"name": "基础CRUD测试", "tests": Slice{}}
tests := Slice{}
// 1.1 Insert 测试 - 使用 admin 表
insertTest := Map{"name": "Insert 插入测试 (admin表)"}
adminId := that.Db.Insert("admin", Map{
"name": "测试管理员_" + fmt.Sprintf("%d", time.Now().Unix()),
"phone": fmt.Sprintf("138%d", time.Now().Unix()%100000000),
"state": 1,
"password": "test123456",
"role_id": 1,
"title": "测试职位",
"create_time[#]": "NOW()",
"modify_time[#]": "NOW()",
})
insertTest["result"] = adminId > 0
insertTest["adminId"] = adminId
insertTest["lastQuery"] = that.Db.LastQuery
tests = append(tests, insertTest)
// 1.2 Get 测试
getTest := Map{"name": "Get 获取单条记录测试"}
admin := that.Db.Get("admin", "*", Map{"id": adminId})
getTest["result"] = admin != nil && admin.GetInt64("id") == adminId
getTest["admin"] = admin
tests = append(tests, getTest)
// 1.3 Select 测试 - 单条件
selectTest1 := Map{"name": "Select 单条件查询测试"}
admins1 := that.Db.Select("admin", "*", Map{"state": 1, "LIMIT": 5})
selectTest1["result"] = len(admins1) >= 0 // 可能表中没有数据
selectTest1["count"] = len(admins1)
tests = append(tests, selectTest1)
// 1.4 Select 测试 - 多条件(自动 AND
selectTest2 := Map{"name": "Select 多条件自动AND测试"}
admins2 := that.Db.Select("admin", "*", Map{
"state": 1,
"role_id[>]": 0,
"ORDER": "id DESC",
"LIMIT": 5,
})
selectTest2["result"] = true // 只要不报错就算成功
selectTest2["count"] = len(admins2)
selectTest2["lastQuery"] = that.Db.LastQuery
tests = append(tests, selectTest2)
// 1.5 Update 测试
updateTest := Map{"name": "Update 更新测试"}
affected := that.Db.Update("admin", Map{
"title": "更新后的职位",
"modify_time[#]": "NOW()",
}, Map{"id": adminId})
updateTest["result"] = affected > 0
updateTest["affected"] = affected
tests = append(tests, updateTest)
// 1.6 Delete 测试 - 使用 test_batch 表
deleteTest := Map{"name": "Delete 删除测试"}
// 先创建一个临时记录用于删除
tempId := that.Db.Insert("test_batch", Map{
"name": "临时删除测试",
"title": "测试标题",
"state": 1,
"create_time[#]": "NOW()",
})
deleteAffected := that.Db.Delete("test_batch", Map{"id": tempId})
deleteTest["result"] = deleteAffected > 0
deleteTest["affected"] = deleteAffected
debugLog("main.go:175", "Delete 测试", Map{"affected": deleteAffected}, "H1_DELETE")
tests = append(tests, deleteTest)
result["tests"] = tests
result["success"] = true
return result
}
// ==================== 2. 条件查询语法测试 ====================
func testConditionSyntax(that *Context) Map {
result := Map{"name": "条件查询语法测试", "tests": Slice{}}
tests := Slice{}
debugLog("main.go:188", "开始条件查询语法测试 (MySQL)", nil, "H2_CONDITION")
// 2.1 等于 (=) - 使用 article 表
test1 := Map{"name": "等于条件 (=)"}
articles1 := that.Db.Select("article", "id,title", Map{"state": 0, "LIMIT": 3})
test1["result"] = true
test1["count"] = len(articles1)
debugLog("main.go:195", "等于条件测试", Map{"count": len(articles1)}, "H2_EQUAL")
tests = append(tests, test1)
// 2.2 不等于 ([!])
test2 := Map{"name": "不等于条件 ([!])"}
articles2 := that.Db.Select("article", "id,title,state", Map{"state[!]": -1, "LIMIT": 3})
test2["result"] = true
test2["count"] = len(articles2)
test2["lastQuery"] = that.Db.LastQuery
debugLog("main.go:204", "不等于条件测试", Map{"count": len(articles2), "query": that.Db.LastQuery}, "H2_NOT_EQUAL")
tests = append(tests, test2)
// 2.3 大于 ([>]) 和 小于 ([<])
test3 := Map{"name": "大于小于条件 ([>], [<])"}
articles3 := that.Db.Select("article", "id,title,click_num", Map{
"click_num[>]": 0,
"click_num[<]": 100000,
"LIMIT": 3,
})
test3["result"] = true
test3["count"] = len(articles3)
debugLog("main.go:216", "大于小于条件测试", Map{"count": len(articles3)}, "H2_GREATER_LESS")
tests = append(tests, test3)
// 2.4 大于等于 ([>=]) 和 小于等于 ([<=])
test4 := Map{"name": "大于等于小于等于条件 ([>=], [<=])"}
articles4 := that.Db.Select("article", "id,title,sort", Map{
"sort[>=]": 0,
"sort[<=]": 100,
"LIMIT": 3,
})
test4["result"] = true
test4["count"] = len(articles4)
debugLog("main.go:228", "大于等于小于等于条件测试", Map{"count": len(articles4)}, "H2_GTE_LTE")
tests = append(tests, test4)
// 2.5 LIKE 模糊查询 ([~])
test5 := Map{"name": "LIKE 模糊查询 ([~])"}
articles5 := that.Db.Select("article", "id,title", Map{"title[~]": "新闻", "LIMIT": 3})
test5["result"] = true
test5["count"] = len(articles5)
test5["lastQuery"] = that.Db.LastQuery
debugLog("main.go:237", "LIKE 模糊查询测试", Map{"count": len(articles5), "query": that.Db.LastQuery}, "H2_LIKE")
tests = append(tests, test5)
// 2.6 右模糊 ([~!])
test6 := Map{"name": "右模糊查询 ([~!])"}
articles6 := that.Db.Select("admin", "id,name", Map{"name[~!]": "管理", "LIMIT": 3})
test6["result"] = true
test6["count"] = len(articles6)
debugLog("main.go:245", "右模糊查询测试", Map{"count": len(articles6)}, "H2_LIKE_RIGHT")
tests = append(tests, test6)
// 2.7 BETWEEN ([<>])
test7 := Map{"name": "BETWEEN 区间查询 ([<>])"}
articles7 := that.Db.Select("article", "id,title,click_num", Map{"click_num[<>]": Slice{0, 1000}, "LIMIT": 3})
test7["result"] = true
test7["count"] = len(articles7)
test7["lastQuery"] = that.Db.LastQuery
debugLog("main.go:254", "BETWEEN 区间查询测试", Map{"count": len(articles7), "query": that.Db.LastQuery}, "H2_BETWEEN")
tests = append(tests, test7)
// 2.8 NOT BETWEEN ([><])
test8 := Map{"name": "NOT BETWEEN 查询 ([><])"}
articles8 := that.Db.Select("article", "id,title,sort", Map{"sort[><]": Slice{-10, 0}, "LIMIT": 3})
test8["result"] = true
test8["count"] = len(articles8)
debugLog("main.go:262", "NOT BETWEEN 查询测试", Map{"count": len(articles8)}, "H2_NOT_BETWEEN")
tests = append(tests, test8)
// 2.9 IN 查询
test9 := Map{"name": "IN 查询"}
articles9 := that.Db.Select("article", "id,title", Map{"id": Slice{1, 2, 3, 4, 5}, "LIMIT": 5})
test9["result"] = true
test9["count"] = len(articles9)
test9["lastQuery"] = that.Db.LastQuery
debugLog("main.go:271", "IN 查询测试", Map{"count": len(articles9), "query": that.Db.LastQuery}, "H2_IN")
tests = append(tests, test9)
// 2.10 NOT IN ([!])
test10 := Map{"name": "NOT IN 查询 ([!])"}
articles10 := that.Db.Select("article", "id,title", Map{"id[!]": Slice{1, 2, 3}, "LIMIT": 5})
test10["result"] = true
test10["count"] = len(articles10)
debugLog("main.go:279", "NOT IN 查询测试", Map{"count": len(articles10)}, "H2_NOT_IN")
tests = append(tests, test10)
// 2.11 IS NULL
test11 := Map{"name": "IS NULL 查询"}
articles11 := that.Db.Select("article", "id,title,img", Map{"img": nil, "LIMIT": 3})
test11["result"] = true
test11["count"] = len(articles11)
debugLog("main.go:287", "IS NULL 查询测试", Map{"count": len(articles11)}, "H2_IS_NULL")
tests = append(tests, test11)
// 2.12 IS NOT NULL ([!])
test12 := Map{"name": "IS NOT NULL 查询 ([!])"}
articles12 := that.Db.Select("article", "id,title,create_time", Map{"create_time[!]": nil, "LIMIT": 3})
test12["result"] = true
test12["count"] = len(articles12)
debugLog("main.go:295", "IS NOT NULL 查询测试", Map{"count": len(articles12)}, "H2_IS_NOT_NULL")
tests = append(tests, test12)
// 2.13 直接 SQL ([##] 用于 SQL 片段)
test13 := Map{"name": "直接 SQL 片段查询 ([##])"}
articles13 := that.Db.Select("article", "id,title,create_time", Map{
"[##]": "create_time > DATE_SUB(NOW(), INTERVAL 365 DAY)",
"LIMIT": 3,
})
test13["result"] = true
test13["count"] = len(articles13)
test13["lastQuery"] = that.Db.LastQuery
debugLog("main.go:307", "直接 SQL 片段查询测试", Map{"count": len(articles13), "query": that.Db.LastQuery}, "H2_RAW_SQL")
tests = append(tests, test13)
// 2.14 显式 AND 条件
test14 := Map{"name": "显式 AND 条件"}
articles14 := that.Db.Select("article", "id,title,state,click_num", Map{
"AND": Map{
"state": 0,
"click_num[>=]": 0,
},
"LIMIT": 3,
})
test14["result"] = true
test14["count"] = len(articles14)
debugLog("main.go:321", "显式 AND 条件测试", Map{"count": len(articles14)}, "H2_EXPLICIT_AND")
tests = append(tests, test14)
// 2.15 OR 条件
test15 := Map{"name": "OR 条件"}
articles15 := that.Db.Select("article", "id,title,sort,click_num", Map{
"OR": Map{
"sort": 0,
"click_num[>]": 10,
},
"LIMIT": 5,
})
test15["result"] = true
test15["count"] = len(articles15)
debugLog("main.go:335", "OR 条件测试", Map{"count": len(articles15)}, "H2_OR")
tests = append(tests, test15)
// 2.16 嵌套 AND/OR 条件
test16 := Map{"name": "嵌套 AND/OR 条件"}
articles16 := that.Db.Select("article", "id,title,sort,state", Map{
"AND": Map{
"state": 0,
"OR": Map{
"sort[>=]": 0,
"click_num[>]": 0,
},
},
"LIMIT": 5,
})
test16["result"] = true
test16["count"] = len(articles16)
test16["lastQuery"] = that.Db.LastQuery
debugLog("main.go:353", "嵌套 AND/OR 条件测试", Map{"count": len(articles16), "query": that.Db.LastQuery}, "H2_NESTED")
tests = append(tests, test16)
result["tests"] = tests
result["success"] = true
return result
}
// ==================== 3. 链式查询测试 ====================
func testChainQuery(that *Context) Map {
result := Map{"name": "链式查询测试", "tests": Slice{}}
tests := Slice{}
debugLog("main.go:366", "开始链式查询测试 (MySQL)", nil, "H3_CHAIN")
// 3.1 基本链式查询 - 使用 article 表
test1 := Map{"name": "基本链式查询 Table().Where().Select()"}
articles1 := that.Db.Table("article").
Where("state", 0).
Select("id,title,author")
test1["result"] = len(articles1) >= 0
test1["count"] = len(articles1)
debugLog("main.go:375", "基本链式查询测试", Map{"count": len(articles1)}, "H3_BASIC")
tests = append(tests, test1)
// 3.2 链式 And 条件
test2 := Map{"name": "链式 And 条件"}
articles2 := that.Db.Table("article").
Where("state", 0).
And("click_num[>=]", 0).
And("sort[>=]", 0).
Select("id,title,click_num")
test2["result"] = len(articles2) >= 0
test2["count"] = len(articles2)
debugLog("main.go:387", "链式 And 条件测试", Map{"count": len(articles2)}, "H3_AND")
tests = append(tests, test2)
// 3.3 链式 Or 条件
test3 := Map{"name": "链式 Or 条件"}
articles3 := that.Db.Table("article").
Where("state", 0).
Or(Map{
"sort": 0,
"click_num[>]": 10,
}).
Select("id,title,sort,click_num")
test3["result"] = len(articles3) >= 0
test3["count"] = len(articles3)
debugLog("main.go:401", "链式 Or 条件测试", Map{"count": len(articles3)}, "H3_OR")
tests = append(tests, test3)
// 3.4 链式 Order
test4 := Map{"name": "链式 Order 排序"}
articles4 := that.Db.Table("article").
Where("state", 0).
Order("create_time DESC", "id ASC").
Limit(0, 5).
Select("id,title,create_time")
test4["result"] = len(articles4) >= 0
test4["count"] = len(articles4)
debugLog("main.go:413", "链式 Order 排序测试", Map{"count": len(articles4)}, "H3_ORDER")
tests = append(tests, test4)
// 3.5 链式 Limit
test5 := Map{"name": "链式 Limit 限制"}
articles5 := that.Db.Table("article").
Where("state", 0).
Limit(0, 3).
Select("id,title")
test5["result"] = len(articles5) <= 3
test5["count"] = len(articles5)
debugLog("main.go:424", "链式 Limit 限制测试", Map{"count": len(articles5)}, "H3_LIMIT")
tests = append(tests, test5)
// 3.6 链式 Get 单条
test6 := Map{"name": "链式 Get 获取单条"}
article6 := that.Db.Table("article").
Where("state", 0).
Get("id,title,author")
test6["result"] = article6 != nil || true // 允许为空
test6["article"] = article6
debugLog("main.go:434", "链式 Get 获取单条测试", Map{"article": article6}, "H3_GET")
tests = append(tests, test6)
// 3.7 链式 Count
test7 := Map{"name": "链式 Count 统计"}
count7 := that.Db.Table("article").
Where("state", 0).
Count()
test7["result"] = count7 >= 0
test7["count"] = count7
debugLog("main.go:444", "链式 Count 统计测试", Map{"count": count7}, "H3_COUNT")
tests = append(tests, test7)
// 3.8 链式 Page 分页
test8 := Map{"name": "链式 Page 分页"}
articles8 := that.Db.Table("article").
Where("state", 0).
Page(1, 5).
Select("id,title")
test8["result"] = len(articles8) <= 5
test8["count"] = len(articles8)
debugLog("main.go:455", "链式 Page 分页测试", Map{"count": len(articles8)}, "H3_PAGE")
tests = append(tests, test8)
// 3.9 链式 Group 分组
test9 := Map{"name": "链式 Group 分组"}
stats9 := that.Db.Table("article").
Where("state", 0).
Group("ctg_id").
Select("ctg_id, COUNT(*) as cnt")
test9["result"] = len(stats9) >= 0
test9["stats"] = stats9
debugLog("main.go:466", "链式 Group 分组测试", Map{"stats": stats9}, "H3_GROUP")
tests = append(tests, test9)
// 3.10 链式 Update
test10 := Map{"name": "链式 Update 更新"}
// 先获取一个文章ID
testArticle := that.Db.Table("article").Where("state", 0).Get("id")
if testArticle != nil {
affected := that.Db.Table("article").
Where("id", testArticle.GetInt64("id")).
Update(Map{"modify_time[#]": "NOW()"})
test10["result"] = affected >= 0
test10["affected"] = affected
} else {
test10["result"] = true
test10["note"] = "无可用测试数据"
}
debugLog("main.go:483", "链式 Update 更新测试", test10, "H3_UPDATE")
tests = append(tests, test10)
result["tests"] = tests
result["success"] = true
return result
}
// ==================== 4. JOIN 查询测试 ====================
func testJoinQuery(that *Context) Map {
result := Map{"name": "JOIN查询测试", "tests": Slice{}}
tests := Slice{}
debugLog("main.go:496", "开始 JOIN 查询测试 (MySQL)", nil, "H4_JOIN")
// 4.1 LEFT JOIN 链式 - article 关联 ctg
test1 := Map{"name": "LEFT JOIN 链式查询"}
articles1 := that.Db.Table("article").
LeftJoin("ctg", "article.ctg_id = ctg.id").
Where("article.state", 0).
Limit(0, 5).
Select("article.id, article.title, ctg.name as ctg_name")
test1["result"] = len(articles1) >= 0
test1["count"] = len(articles1)
test1["data"] = articles1
debugLog("main.go:508", "LEFT JOIN 链式查询测试", Map{"count": len(articles1)}, "H4_LEFT_JOIN")
tests = append(tests, test1)
// 4.2 传统 JOIN 语法
test2 := Map{"name": "传统 JOIN 语法"}
articles2 := that.Db.Select("article",
Slice{
Map{"[>]ctg": "article.ctg_id = ctg.id"},
},
"article.id, article.title, ctg.name as ctg_name",
Map{
"article.state": 0,
"LIMIT": 5,
})
test2["result"] = len(articles2) >= 0
test2["count"] = len(articles2)
test2["lastQuery"] = that.Db.LastQuery
debugLog("main.go:525", "传统 JOIN 语法测试", Map{"count": len(articles2), "query": that.Db.LastQuery}, "H4_TRADITIONAL_JOIN")
tests = append(tests, test2)
// 4.3 多表 JOIN - article 关联 ctg 和 admin
test3 := Map{"name": "多表 JOIN"}
articles3 := that.Db.Table("article").
LeftJoin("ctg", "article.ctg_id = ctg.id").
LeftJoin("admin", "article.admin_id = admin.id").
Where("article.state", 0).
Limit(0, 5).
Select("article.id, article.title, ctg.name as ctg_name, admin.name as admin_name")
test3["result"] = len(articles3) >= 0
test3["count"] = len(articles3)
test3["data"] = articles3
debugLog("main.go:539", "多表 JOIN 测试", Map{"count": len(articles3)}, "H4_MULTI_JOIN")
tests = append(tests, test3)
// 4.4 INNER JOIN
test4 := Map{"name": "INNER JOIN"}
articles4 := that.Db.Table("article").
InnerJoin("ctg", "article.ctg_id = ctg.id").
Where("ctg.state", 0).
Limit(0, 5).
Select("article.id, article.title, ctg.name as ctg_name")
test4["result"] = len(articles4) >= 0
test4["count"] = len(articles4)
debugLog("main.go:551", "INNER JOIN 测试", Map{"count": len(articles4)}, "H4_INNER_JOIN")
tests = append(tests, test4)
result["tests"] = tests
result["success"] = true
return result
}
// ==================== 5. 聚合函数测试 ====================
func testAggregate(that *Context) Map {
result := Map{"name": "聚合函数测试", "tests": Slice{}}
tests := Slice{}
debugLog("main.go:564", "开始聚合函数测试 (MySQL)", nil, "H5_AGGREGATE")
// 5.1 Count 总数
test1 := Map{"name": "Count 总数统计"}
count1 := that.Db.Count("article")
test1["result"] = count1 >= 0
test1["count"] = count1
debugLog("main.go:571", "Count 总数统计测试", Map{"count": count1}, "H5_COUNT")
tests = append(tests, test1)
// 5.2 Count 带条件
test2 := Map{"name": "Count 条件统计"}
count2 := that.Db.Count("article", Map{"state": 0})
test2["result"] = count2 >= 0
test2["count"] = count2
debugLog("main.go:579", "Count 条件统计测试", Map{"count": count2}, "H5_COUNT_WHERE")
tests = append(tests, test2)
// 5.3 Sum 求和
test3 := Map{"name": "Sum 求和"}
sum3 := that.Db.Sum("article", "click_num", Map{"state": 0})
test3["result"] = sum3 >= 0
test3["sum"] = sum3
debugLog("main.go:587", "Sum 求和测试", Map{"sum": sum3}, "H5_SUM")
tests = append(tests, test3)
// 5.4 Avg 平均值
test4 := Map{"name": "Avg 平均值"}
avg4 := that.Db.Avg("article", "click_num", Map{"state": 0})
test4["result"] = avg4 >= 0
test4["avg"] = avg4
debugLog("main.go:595", "Avg 平均值测试", Map{"avg": avg4}, "H5_AVG")
tests = append(tests, test4)
// 5.5 Max 最大值
test5 := Map{"name": "Max 最大值"}
max5 := that.Db.Max("article", "click_num", Map{"state": 0})
test5["result"] = max5 >= 0
test5["max"] = max5
debugLog("main.go:603", "Max 最大值测试", Map{"max": max5}, "H5_MAX")
tests = append(tests, test5)
// 5.6 Min 最小值
test6 := Map{"name": "Min 最小值"}
min6 := that.Db.Min("article", "sort", Map{"state": 0})
test6["result"] = true // sort 可能为 0
test6["min"] = min6
debugLog("main.go:611", "Min 最小值测试", Map{"min": min6}, "H5_MIN")
tests = append(tests, test6)
// 5.7 GROUP BY 分组统计
test7 := Map{"name": "GROUP BY 分组统计"}
stats7 := that.Db.Select("article",
"ctg_id, COUNT(*) as article_count, AVG(click_num) as avg_clicks, SUM(click_num) as total_clicks",
Map{
"state": 0,
"GROUP": "ctg_id",
"ORDER": "article_count DESC",
"LIMIT": 10,
})
test7["result"] = len(stats7) >= 0
test7["stats"] = stats7
debugLog("main.go:625", "GROUP BY 分组统计测试", Map{"stats": stats7}, "H5_GROUP_BY")
tests = append(tests, test7)
result["tests"] = tests
result["success"] = true
return result
}
// ==================== 6. 分页查询测试 ====================
func testPagination(that *Context) Map {
result := Map{"name": "分页查询测试", "tests": Slice{}}
tests := Slice{}
debugLog("main.go:638", "开始分页查询测试 (MySQL)", nil, "H6_PAGINATION")
// 6.1 PageSelect 分页查询
test1 := Map{"name": "PageSelect 分页查询"}
articles1 := that.Db.Page(1, 5).PageSelect("article", "*", Map{
"state": 0,
"ORDER": "id DESC",
})
test1["result"] = len(articles1) <= 5
test1["count"] = len(articles1)
debugLog("main.go:648", "PageSelect 分页查询测试", Map{"count": len(articles1)}, "H6_PAGE_SELECT")
tests = append(tests, test1)
// 6.2 第二页
test2 := Map{"name": "PageSelect 第二页"}
articles2 := that.Db.Page(2, 5).PageSelect("article", "*", Map{
"state": 0,
"ORDER": "id DESC",
})
test2["result"] = len(articles2) <= 5
test2["count"] = len(articles2)
debugLog("main.go:659", "PageSelect 第二页测试", Map{"count": len(articles2)}, "H6_PAGE_2")
tests = append(tests, test2)
// 6.3 链式分页
test3 := Map{"name": "链式 Page 分页"}
articles3 := that.Db.Table("article").
Where("state", 0).
Order("id DESC").
Page(1, 3).
Select("id,title,author")
test3["result"] = len(articles3) <= 3
test3["count"] = len(articles3)
debugLog("main.go:671", "链式 Page 分页测试", Map{"count": len(articles3)}, "H6_CHAIN_PAGE")
tests = append(tests, test3)
// 6.4 Offset 偏移
test4 := Map{"name": "Offset 偏移查询"}
articles4 := that.Db.Table("article").
Where("state", 0).
Limit(3).
Offset(2).
Select("id,title")
test4["result"] = len(articles4) <= 3
test4["count"] = len(articles4)
test4["lastQuery"] = that.Db.LastQuery
debugLog("main.go:684", "Offset 偏移查询测试", Map{"count": len(articles4), "query": that.Db.LastQuery}, "H6_OFFSET")
tests = append(tests, test4)
result["tests"] = tests
result["success"] = true
return result
}
// ==================== 7. 批量插入测试 ====================
func testBatchInsert(that *Context) Map {
result := Map{"name": "批量插入测试", "tests": Slice{}}
tests := Slice{}
debugLog("main.go:697", "开始批量插入测试 (MySQL)", nil, "H7_BATCH")
// 7.1 批量插入
test1 := Map{"name": "BatchInsert 批量插入"}
timestamp := time.Now().UnixNano()
affected1 := that.Db.BatchInsert("test_batch", []Map{
{"name": fmt.Sprintf("批量测试1_%d", timestamp), "title": "标题1", "state": 1},
{"name": fmt.Sprintf("批量测试2_%d", timestamp), "title": "标题2", "state": 1},
{"name": fmt.Sprintf("批量测试3_%d", timestamp), "title": "标题3", "state": 1},
})
test1["result"] = affected1 >= 0
test1["affected"] = affected1
test1["lastQuery"] = that.Db.LastQuery
debugLog("main.go:710", "BatchInsert 批量插入测试", Map{"affected": affected1, "query": that.Db.LastQuery}, "H7_BATCH_INSERT")
tests = append(tests, test1)
// 7.2 带 [#] 的批量插入
test2 := Map{"name": "BatchInsert 带 [#] 标记"}
timestamp2 := time.Now().UnixNano()
affected2 := that.Db.BatchInsert("test_batch", []Map{
{"name": fmt.Sprintf("带时间测试1_%d", timestamp2), "title": "标题带时间1", "state": 1, "create_time[#]": "NOW()"},
{"name": fmt.Sprintf("带时间测试2_%d", timestamp2), "title": "标题带时间2", "state": 1, "create_time[#]": "NOW()"},
})
test2["result"] = affected2 >= 0
test2["affected"] = affected2
debugLog("main.go:722", "BatchInsert 带 [#] 标记测试", Map{"affected": affected2}, "H7_BATCH_RAW")
tests = append(tests, test2)
// 清理测试数据
that.Db.Delete("test_batch", Map{"name[~]": fmt.Sprintf("_%d", timestamp)})
that.Db.Delete("test_batch", Map{"name[~]": fmt.Sprintf("_%d", timestamp2)})
result["tests"] = tests
result["success"] = true
return result
}
// ==================== 8. Upsert 测试 ====================
func testUpsert(that *Context) Map {
result := Map{"name": "Upsert测试", "tests": Slice{}}
tests := Slice{}
debugLog("main.go:739", "开始 Upsert 测试 (MySQL)", nil, "H8_UPSERT")
// 使用 admin 表测试 UpsertMySQL ON DUPLICATE KEY UPDATE
timestamp := time.Now().Unix()
testPhone := fmt.Sprintf("199%08d", timestamp%100000000)
// 8.1 Upsert 插入新记录
test1 := Map{"name": "Upsert 插入新记录 (admin表)"}
affected1 := that.Db.Upsert("admin",
Map{
"name": "Upsert测试管理员",
"phone": testPhone,
"state": 1,
"password": "test123",
"role_id": 1,
"title": "测试职位",
"create_time[#]": "NOW()",
"modify_time[#]": "NOW()",
},
Slice{"phone"},
Slice{"name", "state", "title", "modify_time"},
)
test1["result"] = affected1 >= 0
test1["affected"] = affected1
test1["lastQuery"] = that.Db.LastQuery
debugLog("main.go:763", "Upsert 插入新记录测试", Map{"affected": affected1, "query": that.Db.LastQuery}, "H8_UPSERT_INSERT")
tests = append(tests, test1)
// 8.2 Upsert 更新已存在记录
test2 := Map{"name": "Upsert 更新已存在记录"}
affected2 := that.Db.Upsert("admin",
Map{
"name": "Upsert更新后管理员",
"phone": testPhone,
"state": 1,
"password": "updated123",
"role_id": 2,
"title": "更新后职位",
"create_time[#]": "NOW()",
"modify_time[#]": "NOW()",
},
Slice{"phone"},
Slice{"name", "title", "role_id", "modify_time"},
)
test2["result"] = affected2 >= 0
test2["affected"] = affected2
debugLog("main.go:783", "Upsert 更新已存在记录测试", Map{"affected": affected2}, "H8_UPSERT_UPDATE")
tests = append(tests, test2)
// 验证更新结果
updatedAdmin := that.Db.Get("admin", "*", Map{"phone": testPhone})
test2["updatedAdmin"] = updatedAdmin
// 清理测试数据
that.Db.Delete("admin", Map{"phone": testPhone})
result["tests"] = tests
result["success"] = true
return result
}
// ==================== 9. 事务测试 ====================
func testTransaction(that *Context) Map {
result := Map{"name": "事务测试", "tests": Slice{}}
tests := Slice{}
debugLog("main.go:803", "开始事务测试 (MySQL)", nil, "H9_TRANSACTION")
// 9.1 事务成功提交
test1 := Map{"name": "事务成功提交"}
timestamp := time.Now().Unix()
testName1 := fmt.Sprintf("事务测试_%d", timestamp)
success1 := that.Db.Action(func(tx HoTimeDB) bool {
// 插入记录到 test_batch 表
recordId := tx.Insert("test_batch", Map{
"name": testName1,
"title": "事务提交测试",
"state": 1,
"create_time[#]": "NOW()",
})
debugLog("main.go:818", "事务内插入记录", Map{"recordId": recordId}, "H9_TX_INSERT")
return recordId != 0
})
test1["result"] = success1
// 验证数据是否存在
checkRecord := that.Db.Get("test_batch", "*", Map{"name": testName1})
test1["recordExists"] = checkRecord != nil
debugLog("main.go:831", "事务成功提交测试", Map{"success": success1, "recordExists": checkRecord != nil}, "H9_TX_SUCCESS")
tests = append(tests, test1)
// 9.2 事务回滚
test2 := Map{"name": "事务回滚"}
testName2 := fmt.Sprintf("事务回滚测试_%d", timestamp)
success2 := that.Db.Action(func(tx HoTimeDB) bool {
// 插入记录
recordId := tx.Insert("test_batch", Map{
"name": testName2,
"title": "事务回滚测试",
"state": 1,
"create_time[#]": "NOW()",
})
debugLog("main.go:846", "事务内插入(将回滚)", Map{"recordId": recordId}, "H9_TX_ROLLBACK_INSERT")
// 返回 false 触发回滚
return false
})
test2["result"] = !success2 // 期望回滚,所以 success2 应该为 false
// 验证数据是否不存在(已回滚)
checkRecord2 := that.Db.Get("test_batch", "*", Map{"name": testName2})
test2["recordRolledBack"] = checkRecord2 == nil
debugLog("main.go:856", "事务回滚测试", Map{"success": success2, "rolledBack": checkRecord2 == nil}, "H9_TX_ROLLBACK")
tests = append(tests, test2)
// 清理测试数据
that.Db.Delete("test_batch", Map{"name": testName1})
result["tests"] = tests
result["success"] = true
return result
}
// ==================== 10. 原生 SQL 测试 ====================
func testRawSQL(that *Context) Map {
result := Map{"name": "原生SQL测试", "tests": Slice{}}
tests := Slice{}
debugLog("main.go:872", "开始原生 SQL 测试 (MySQL)", nil, "H10_RAW_SQL")
// 10.1 Query 查询 - 使用真实的 article 表
test1 := Map{"name": "Query 原生查询"}
articles1 := that.Db.Query("SELECT id, title, author FROM `article` WHERE state = ? LIMIT ?", 0, 5)
test1["result"] = len(articles1) >= 0
test1["count"] = len(articles1)
debugLog("main.go:879", "Query 原生查询测试", Map{"count": len(articles1)}, "H10_QUERY")
tests = append(tests, test1)
// 10.2 Exec 执行 - 使用 article 表
test2 := Map{"name": "Exec 原生执行"}
// 更新一条记录
testArticle := that.Db.Get("article", "id", Map{"state": 0})
if testArticle != nil {
res, err := that.Db.Exec("UPDATE `article` SET modify_time = NOW() WHERE id = ?", testArticle.GetInt64("id"))
if err.GetError() == nil && res != nil {
affected, _ := res.RowsAffected()
test2["result"] = affected >= 0
test2["affected"] = affected
} else {
test2["result"] = false
test2["error"] = err.GetError()
}
} else {
test2["result"] = true
test2["note"] = "无可用测试数据"
}
debugLog("main.go:899", "Exec 原生执行测试", test2, "H10_EXEC")
tests = append(tests, test2)
// ==================== IN/NOT IN 数组测试 ====================
// H1: IN (?) 配合非空数组能正确展开
test3 := Map{"name": "H1: IN (?) 非空数组展开"}
// #region agent log
debugLog("main.go:test3", "H1测试开始: IN非空数组", Map{"ids": []int{1, 2, 3, 4, 5}}, "H1")
// #endregion
articles3 := that.Db.Query("SELECT id, title FROM `article` WHERE id IN (?) LIMIT 10", []int{1, 2, 3, 4, 5})
// #region agent log
debugLog("main.go:test3", "H1测试结果: IN非空数组", Map{
"count": len(articles3),
"lastQuery": that.Db.LastQuery,
"data": articles3,
}, "H1")
// #endregion
test3["result"] = len(articles3) >= 0
test3["count"] = len(articles3)
test3["lastQuery"] = that.Db.LastQuery
tests = append(tests, test3)
// H2: IN (?) 配合空数组替换为 1=0
test4 := Map{"name": "H2: IN (?) 空数组替换为1=0"}
// #region agent log
debugLog("main.go:test4", "H2测试开始: IN空数组", Map{"ids": []int{}}, "H2")
// #endregion
articles4 := that.Db.Query("SELECT id, title FROM `article` WHERE id IN (?) LIMIT 10", []int{})
// #region agent log
debugLog("main.go:test4", "H2测试结果: IN空数组", Map{
"count": len(articles4),
"lastQuery": that.Db.LastQuery,
"expected": "应包含1=0返回0条记录",
}, "H2")
// #endregion
test4["result"] = len(articles4) == 0 // 空数组的IN应该返回0条
test4["count"] = len(articles4)
test4["lastQuery"] = that.Db.LastQuery
test4["expected"] = "count=0, SQL应包含1=0"
tests = append(tests, test4)
// H3: NOT IN (?) 配合空数组替换为 1=1
test5 := Map{"name": "H3: NOT IN (?) 空数组替换为1=1"}
// #region agent log
debugLog("main.go:test5", "H3测试开始: NOT IN空数组", Map{"ids": []int{}}, "H3")
// #endregion
articles5 := that.Db.Query("SELECT id, title FROM `article` WHERE id NOT IN (?) LIMIT 10", []int{})
// #region agent log
debugLog("main.go:test5", "H3测试结果: NOT IN空数组", Map{
"count": len(articles5),
"lastQuery": that.Db.LastQuery,
"expected": "应包含1=1返回所有记录限制10条",
}, "H3")
// #endregion
test5["result"] = len(articles5) > 0 // NOT IN空数组应该返回记录
test5["count"] = len(articles5)
test5["lastQuery"] = that.Db.LastQuery
test5["expected"] = "count>0, SQL应包含1=1"
tests = append(tests, test5)
// H4: NOT IN (?) 配合非空数组正常展开
test6 := Map{"name": "H4: NOT IN (?) 非空数组展开"}
// #region agent log
debugLog("main.go:test6", "H4测试开始: NOT IN非空数组", Map{"ids": []int{1, 2, 3}}, "H4")
// #endregion
articles6 := that.Db.Query("SELECT id, title FROM `article` WHERE id NOT IN (?) LIMIT 10", []int{1, 2, 3})
// #region agent log
debugLog("main.go:test6", "H4测试结果: NOT IN非空数组", Map{
"count": len(articles6),
"lastQuery": that.Db.LastQuery,
}, "H4")
// #endregion
test6["result"] = len(articles6) >= 0
test6["count"] = len(articles6)
test6["lastQuery"] = that.Db.LastQuery
tests = append(tests, test6)
// H5: 普通 ? 占位符保持原有行为
test7 := Map{"name": "H5: 普通?占位符不受影响"}
// #region agent log
debugLog("main.go:test7", "H5测试开始: 普通占位符", Map{"state": 0, "limit": 5}, "H5")
// #endregion
articles7 := that.Db.Query("SELECT id, title FROM `article` WHERE state = ? LIMIT ?", 0, 5)
// #region agent log
debugLog("main.go:test7", "H5测试结果: 普通占位符", Map{
"count": len(articles7),
"lastQuery": that.Db.LastQuery,
}, "H5")
// #endregion
test7["result"] = len(articles7) >= 0
test7["count"] = len(articles7)
test7["lastQuery"] = that.Db.LastQuery
tests = append(tests, test7)
// 额外测试: Select ORM方法的空数组处理
test8 := Map{"name": "ORM Select: IN空数组"}
// #region agent log
debugLog("main.go:test8", "ORM测试开始: Select IN空数组", nil, "H2_ORM")
// #endregion
articles8 := that.Db.Select("article", "id,title", Map{"id": []int{}, "LIMIT": 10})
// #region agent log
debugLog("main.go:test8", "ORM测试结果: Select IN空数组", Map{
"count": len(articles8),
"lastQuery": that.Db.LastQuery,
}, "H2_ORM")
// #endregion
test8["result"] = len(articles8) == 0
test8["count"] = len(articles8)
test8["lastQuery"] = that.Db.LastQuery
tests = append(tests, test8)
// 额外测试: Select ORM方法的NOT IN空数组处理
test9 := Map{"name": "ORM Select: NOT IN空数组"}
// #region agent log
debugLog("main.go:test9", "ORM测试开始: Select NOT IN空数组", nil, "H3_ORM")
// #endregion
articles9 := that.Db.Select("article", "id,title", Map{"id[!]": []int{}, "LIMIT": 10})
// #region agent log
debugLog("main.go:test9", "ORM测试结果: Select NOT IN空数组", Map{
"count": len(articles9),
"lastQuery": that.Db.LastQuery,
}, "H3_ORM")
// #endregion
test9["result"] = len(articles9) > 0 // NOT IN 空数组应返回记录
test9["count"] = len(articles9)
test9["lastQuery"] = that.Db.LastQuery
tests = append(tests, test9)
result["tests"] = tests
result["success"] = true
return result
}