From 298dbcbcb19d81c87227190f266abb930c619bba Mon Sep 17 00:00:00 2001 From: hoteas <925970985@qq.com> Date: Sun, 12 Apr 2026 00:53:22 +0800 Subject: [PATCH] =?UTF-8?q?fix(db):=20=E4=BF=AE=E5=A4=8D=E4=BA=8B=E5=8A=A1?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=E7=9A=84=E9=94=99=E8=AF=AF=E5=A4=84?= =?UTF-8?q?=E7=90=86=E4=B8=8E=E5=9B=9E=E6=BB=9A=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 `queryWithRetry` 和 `execWithRetry` 方法中添加对 `txFailed` 的状态管理,确保在 SQL 错误时正确标记事务失败 - 增加新的测试用例以验证事务模式下的错误处理逻辑,确保在 SQL 错误发生时能够正确回滚 - 优化测试数据库的初始化方法,支持事务测试场景 --- db/query.go | 6 + db/transaction_test.go | 115 ++ example/batch_cache_tester.go | 162 --- example/tpt/swagger/app/api-spec.json | 1673 ++++++++----------------- example/tpt/swagger/app/index.html | 32 +- 5 files changed, 696 insertions(+), 1292 deletions(-) diff --git a/db/query.go b/db/query.go index 8aa153b..e464279 100644 --- a/db/query.go +++ b/db/query.go @@ -77,6 +77,9 @@ func (that *HoTimeDB) queryWithRetry(query string, retried bool, args ...interfa resl, err = that.testTx.Query(query, processedArgs...) that.LastErr.SetError(err) if err != nil { + if that.txFailed != nil { + *that.txFailed = true + } if that.testMu != nil { that.testMu.Unlock() } @@ -172,6 +175,9 @@ func (that *HoTimeDB) execWithRetry(query string, retried bool, args ...interfac if e != nil { // testTx 模式下不走连接池重试,避免 busy buffer if that.testTx != nil { + if that.txFailed != nil { + *that.txFailed = true + } return resl, that.LastErr } // 事务内不做连接级重试:死锁等错误会导致 MySQL 自动回滚事务, diff --git a/db/transaction_test.go b/db/transaction_test.go index 565efbe..0d174d5 100644 --- a/db/transaction_test.go +++ b/db/transaction_test.go @@ -3,6 +3,7 @@ package db import ( . "code.hoteas.com/golang/hotime/common" "database/sql" + "sync" "testing" "github.com/sirupsen/logrus" @@ -34,6 +35,18 @@ func newTestDB(t *testing.T) *HoTimeDB { return db } +func newTestDBWithTestTx(t *testing.T) *HoTimeDB { + t.Helper() + db := newTestDB(t) + tx, err := db.DB.Begin() + if err != nil { + t.Fatal(err) + } + db.testTx = tx + db.testMu = &sync.Mutex{} + return db +} + func TestAction_TxFailedForceRollback(t *testing.T) { db := newTestDB(t) defer db.DB.Close() @@ -133,3 +146,105 @@ func TestAction_SqlErrorThenReturnTrue_MustRollback(t *testing.T) { t.Fatalf("value should be rolled back to 100 (original), got %d", val) } } + +// --- testTx (SAVEPOINT) 模式测试 --- + +func TestAction_TestTx_SqlErrorForceRollbackSavepoint(t *testing.T) { + db := newTestDBWithTestTx(t) + defer db.testTx.Rollback() + defer db.DB.Close() + + db.testTx.Exec("UPDATE test_item SET value = 100 WHERE id = 1") + + result := db.Action(func(tx HoTimeDB) (isSuccess bool) { + tx.Update("test_item", Map{"value": 888}, Map{"id": 1}) + + tx.Exec("THIS IS INVALID SQL") + + return true + }) + + if result != false { + t.Fatalf("Action (testTx mode) should return false when SQL error occurred, got true") + } + + db.testMu.Lock() + rows, err := db.testTx.Query("SELECT value FROM test_item WHERE id = 1") + db.testMu.Unlock() + if err != nil { + t.Fatal(err) + } + defer rows.Close() + if !rows.Next() { + t.Fatal("no row found") + } + var val int64 + rows.Scan(&val) + if val != 100 { + t.Fatalf("value should be rolled back to 100 via SAVEPOINT, got %d", val) + } +} + +func TestAction_TestTx_NormalCommitSavepoint(t *testing.T) { + db := newTestDBWithTestTx(t) + defer db.testTx.Rollback() + defer db.DB.Close() + + result := db.Action(func(tx HoTimeDB) (isSuccess bool) { + tx.Update("test_item", Map{"value": 777}, Map{"id": 1}) + return true + }) + + if result != true { + t.Fatalf("Action (testTx mode) should return true on success, got false") + } + + db.testMu.Lock() + rows, err := db.testTx.Query("SELECT value FROM test_item WHERE id = 1") + db.testMu.Unlock() + if err != nil { + t.Fatal(err) + } + defer rows.Close() + if !rows.Next() { + t.Fatal("no row found") + } + var val int64 + rows.Scan(&val) + if val != 777 { + t.Fatalf("value should be 777 after RELEASE SAVEPOINT, got %d", val) + } +} + +func TestAction_TestTx_NormalRollbackSavepoint(t *testing.T) { + db := newTestDBWithTestTx(t) + defer db.testTx.Rollback() + defer db.DB.Close() + + db.testTx.Exec("UPDATE test_item SET value = 100 WHERE id = 1") + + result := db.Action(func(tx HoTimeDB) (isSuccess bool) { + tx.Update("test_item", Map{"value": 666}, Map{"id": 1}) + return false + }) + + if result != false { + t.Fatalf("Action (testTx mode) should return false, got true") + } + + db.testMu.Lock() + rows, err := db.testTx.Query("SELECT value FROM test_item WHERE id = 1") + db.testMu.Unlock() + if err != nil { + t.Fatal(err) + } + defer rows.Close() + if !rows.Next() { + t.Fatal("no row found") + } + var val int64 + rows.Scan(&val) + if val != 100 { + t.Fatalf("value should be rolled back to 100 via SAVEPOINT, got %d", val) + } +} diff --git a/example/batch_cache_tester.go b/example/batch_cache_tester.go index 37fe366..44a4299 100644 --- a/example/batch_cache_tester.go +++ b/example/batch_cache_tester.go @@ -1,9 +1,7 @@ package main import ( - "encoding/json" "fmt" - "os" "time" "code.hoteas.com/golang/hotime" @@ -11,26 +9,6 @@ import ( . "code.hoteas.com/golang/hotime/common" ) -const debugLogPath = `d:\work\hotimev1.5\.cursor\debug.log` - -// debugLog 写入调试日志 -func debugLog(hypothesisId, location, message string, data map[string]interface{}) { - logFile, _ := os.OpenFile(debugLogPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if logFile != nil { - logEntry, _ := json.Marshal(map[string]interface{}{ - "sessionId": "batch-cache-test", - "runId": "test-run", - "hypothesisId": hypothesisId, - "location": location, - "message": message, - "data": data, - "timestamp": time.Now().UnixMilli(), - }) - logFile.Write(append(logEntry, '\n')) - logFile.Close() - } -} - // TestBatchCacheOperations 测试所有批量缓存操作 func TestBatchCacheOperations(app *hotime.Application) { fmt.Println("\n========== 批量缓存操作测试开始 ==========") @@ -64,10 +42,6 @@ func TestBatchCacheOperations(app *hotime.Application) { // testCacheMemoryBatch 测试内存缓存批量操作 func testCacheMemoryBatch(app *hotime.Application) { - // #region agent log - debugLog("E", "batch_cache_test.go:testCacheMemoryBatch:start", "开始测试CacheMemory批量操作", nil) - // #endregion - memCache := &cache.CacheMemory{TimeOut: 3600, DbSet: true, SessionSet: true} memCache.SetError(&Error{}) @@ -79,22 +53,10 @@ func testCacheMemoryBatch(app *hotime.Application) { } memCache.CachesSet(testData) - // #region agent log - debugLog("E", "batch_cache_test.go:testCacheMemoryBatch:afterSet", "CacheMemory.CachesSet完成", map[string]interface{}{"count": len(testData)}) - // #endregion - // 测试 CachesGet keys := []string{"mem_key1", "mem_key2", "mem_key3", "mem_key_not_exist"} result := memCache.CachesGet(keys) - // #region agent log - debugLog("E", "batch_cache_test.go:testCacheMemoryBatch:afterGet", "CacheMemory.CachesGet完成", map[string]interface{}{ - "requested_keys": keys, - "result_count": len(result), - "result_keys": getMapKeys(result), - }) - // #endregion - if len(result) != 3 { fmt.Printf(" [FAIL] CacheMemory.CachesGet: 期望3个结果,实际%d个\n", len(result)) } else { @@ -105,14 +67,6 @@ func testCacheMemoryBatch(app *hotime.Application) { memCache.CachesDelete([]string{"mem_key1", "mem_key2"}) result2 := memCache.CachesGet(keys) - // #region agent log - debugLog("E", "batch_cache_test.go:testCacheMemoryBatch:afterDelete", "CacheMemory.CachesDelete完成", map[string]interface{}{ - "deleted_keys": []string{"mem_key1", "mem_key2"}, - "remaining_count": len(result2), - "remaining_keys": getMapKeys(result2), - }) - // #endregion - if len(result2) != 1 || result2["mem_key3"] == nil { fmt.Printf(" [FAIL] CacheMemory.CachesDelete: 删除后期望1个结果,实际%d个\n", len(result2)) } else { @@ -122,10 +76,6 @@ func testCacheMemoryBatch(app *hotime.Application) { // testCacheDbBatch 测试数据库缓存批量操作 func testCacheDbBatch(app *hotime.Application) { - // #region agent log - debugLog("E", "batch_cache_test.go:testCacheDbBatch:start", "开始测试CacheDb批量操作", nil) - // #endregion - // 使用应用的数据库连接 dbCache := &cache.CacheDb{ TimeOut: 3600, @@ -147,23 +97,10 @@ func testCacheDbBatch(app *hotime.Application) { } dbCache.CachesSet(testData) - // #region agent log - debugLog("E", "batch_cache_test.go:testCacheDbBatch:afterSet", "CacheDb.CachesSet完成", map[string]interface{}{"count": len(testData)}) - // #endregion - // 测试 CachesGet keys := []string{"db_batch_key1", "db_batch_key2", "db_batch_key3", "db_not_exist"} result := dbCache.CachesGet(keys) - // #region agent log - debugLog("E", "batch_cache_test.go:testCacheDbBatch:afterGet", "CacheDb.CachesGet完成", map[string]interface{}{ - "requested_keys": keys, - "result_count": len(result), - "result_keys": getMapKeys(result), - "result_values": result, - }) - // #endregion - if len(result) != 3 { fmt.Printf(" [FAIL] CacheDb.CachesGet: 期望3个结果,实际%d个\n", len(result)) } else { @@ -181,14 +118,6 @@ func testCacheDbBatch(app *hotime.Application) { dbCache.CachesDelete([]string{"db_batch_key1", "db_batch_key2"}) result2 := dbCache.CachesGet(keys) - // #region agent log - debugLog("E", "batch_cache_test.go:testCacheDbBatch:afterDelete", "CacheDb.CachesDelete完成", map[string]interface{}{ - "deleted_keys": []string{"db_batch_key1", "db_batch_key2"}, - "remaining_count": len(result2), - "remaining_keys": getMapKeys(result2), - }) - // #endregion - if len(result2) != 1 { fmt.Printf(" [FAIL] CacheDb.CachesDelete: 删除后期望1个结果,实际%d个\n", len(result2)) } else { @@ -201,10 +130,6 @@ func testCacheDbBatch(app *hotime.Application) { // testHoTimeCacheBatch 测试 HoTimeCache 三级缓存批量操作 func testHoTimeCacheBatch(app *hotime.Application) { - // #region agent log - debugLog("D", "batch_cache_test.go:testHoTimeCacheBatch:start", "开始测试HoTimeCache三级缓存批量操作", nil) - // #endregion - htCache := app.HoTimeCache // 清理测试数据 @@ -222,10 +147,6 @@ func testHoTimeCacheBatch(app *hotime.Application) { } htCache.SessionsSet(testData) - // #region agent log - debugLog("D", "batch_cache_test.go:testHoTimeCacheBatch:afterSet", "HoTimeCache.SessionsSet完成", map[string]interface{}{"count": len(testData)}) - // #endregion - // 测试 SessionsGet keys := []string{ hotime.HEAD_SESSION_ADD + "ht_batch_key1", @@ -235,14 +156,6 @@ func testHoTimeCacheBatch(app *hotime.Application) { } result := htCache.SessionsGet(keys) - // #region agent log - debugLog("D", "batch_cache_test.go:testHoTimeCacheBatch:afterGet", "HoTimeCache.SessionsGet完成", map[string]interface{}{ - "requested_keys": len(keys), - "result_count": len(result), - "result_keys": getMapKeys(result), - }) - // #endregion - if len(result) != 3 { fmt.Printf(" [FAIL] HoTimeCache.SessionsGet: 期望3个结果,实际%d个\n", len(result)) } else { @@ -256,12 +169,6 @@ func testHoTimeCacheBatch(app *hotime.Application) { }) result2 := htCache.SessionsGet(keys) - // #region agent log - debugLog("D", "batch_cache_test.go:testHoTimeCacheBatch:afterDelete", "HoTimeCache.SessionsDelete完成", map[string]interface{}{ - "remaining_count": len(result2), - }) - // #endregion - if len(result2) != 1 { fmt.Printf(" [FAIL] HoTimeCache.SessionsDelete: 删除后期望1个结果,实际%d个\n", len(result2)) } else { @@ -274,10 +181,6 @@ func testHoTimeCacheBatch(app *hotime.Application) { // testSessionInsBatch 测试 SessionIns 批量操作 func testSessionInsBatch(app *hotime.Application) { - // #region agent log - debugLog("B", "batch_cache_test.go:testSessionInsBatch:start", "开始测试SessionIns批量操作", nil) - // #endregion - // 创建一个模拟的 SessionIns session := &hotime.SessionIns{ SessionId: "test_batch_session_" + ObjToStr(time.Now().UnixNano()), @@ -292,24 +195,9 @@ func testSessionInsBatch(app *hotime.Application) { } session.SessionsSet(testData) - // #region agent log - debugLog("B", "batch_cache_test.go:testSessionInsBatch:afterSet", "SessionIns.SessionsSet完成", map[string]interface{}{ - "session_id": session.SessionId, - "count": len(testData), - }) - // #endregion - // 测试 SessionsGet result := session.SessionsGet("field1", "field2", "field3", "not_exist") - // #region agent log - debugLog("B", "batch_cache_test.go:testSessionInsBatch:afterGet", "SessionIns.SessionsGet完成", map[string]interface{}{ - "result_count": len(result), - "result_keys": getMapKeys(result), - "result": result, - }) - // #endregion - if len(result) != 3 { fmt.Printf(" [FAIL] SessionIns.SessionsGet: 期望3个结果,实际%d个\n", len(result)) } else { @@ -334,13 +222,6 @@ func testSessionInsBatch(app *hotime.Application) { session.SessionsDelete("field1", "field2") result2 := session.SessionsGet("field1", "field2", "field3") - // #region agent log - debugLog("B", "batch_cache_test.go:testSessionInsBatch:afterDelete", "SessionIns.SessionsDelete完成", map[string]interface{}{ - "remaining_count": len(result2), - "remaining_keys": getMapKeys(result2), - }) - // #endregion - if len(result2) != 1 { fmt.Printf(" [FAIL] SessionIns.SessionsDelete: 删除后期望1个结果,实际%d个\n", len(result2)) } else { @@ -350,10 +231,6 @@ func testSessionInsBatch(app *hotime.Application) { // testCacheBackfill 测试缓存反哺机制 func testCacheBackfill(app *hotime.Application) { - // #region agent log - debugLog("D", "batch_cache_test.go:testCacheBackfill:start", "开始测试缓存反哺机制", nil) - // #endregion - htCache := app.HoTimeCache // 直接写入数据库缓存(绕过 memory)模拟只有 db 有数据的情况 @@ -372,24 +249,10 @@ func testCacheBackfill(app *hotime.Application) { // 直接写入 db dbCache.Cache(testKey, testValue) - // #region agent log - debugLog("D", "batch_cache_test.go:testCacheBackfill:dbWritten", "数据直接写入DB", map[string]interface{}{ - "key": testKey, - "value": testValue, - }) - // #endregion - // 通过 HoTimeCache 批量获取,应该触发反哺到 memory keys := []string{testKey} result := htCache.CachesGet(keys) - // #region agent log - debugLog("D", "batch_cache_test.go:testCacheBackfill:afterGet", "HoTimeCache.CachesGet完成", map[string]interface{}{ - "result_count": len(result), - "has_key": result[testKey] != nil, - }) - // #endregion - if len(result) != 1 || result[testKey] == nil { fmt.Println(" [FAIL] 缓存反哺: 从 DB 读取失败") } else { @@ -402,10 +265,6 @@ func testCacheBackfill(app *hotime.Application) { // testBatchEfficiency 测试批量操作效率 func testBatchEfficiency(app *hotime.Application) { - // #region agent log - debugLog("A", "batch_cache_test.go:testBatchEfficiency:start", "开始测试批量操作效率", nil) - // #endregion - session := &hotime.SessionIns{ SessionId: "efficiency_test_" + ObjToStr(time.Now().UnixNano()), } @@ -423,13 +282,6 @@ func testBatchEfficiency(app *hotime.Application) { batchDuration := time.Since(startTime) - // #region agent log - debugLog("A", "batch_cache_test.go:testBatchEfficiency:batchSet", "批量设置完成", map[string]interface{}{ - "count": len(testData), - "duration_ms": batchDuration.Milliseconds(), - }) - // #endregion - // 对比单个设置 session2 := &hotime.SessionIns{ SessionId: "efficiency_test_single_" + ObjToStr(time.Now().UnixNano()), @@ -442,13 +294,6 @@ func testBatchEfficiency(app *hotime.Application) { } singleDuration := time.Since(startTime2) - // #region agent log - debugLog("A", "batch_cache_test.go:testBatchEfficiency:singleSet", "单个设置完成", map[string]interface{}{ - "count": 10, - "duration_ms": singleDuration.Milliseconds(), - }) - // #endregion - fmt.Printf(" 批量设置10个字段耗时: %v\n", batchDuration) fmt.Printf(" 单个设置10个字段耗时: %v\n", singleDuration) @@ -467,13 +312,6 @@ func testBatchEfficiency(app *hotime.Application) { session.SessionsGet(keys...) batchGetDuration := time.Since(startTime3) - // #region agent log - debugLog("A", "batch_cache_test.go:testBatchEfficiency:batchGet", "批量获取完成", map[string]interface{}{ - "count": 10, - "duration_ms": batchGetDuration.Milliseconds(), - }) - // #endregion - fmt.Printf(" 批量获取10个字段耗时: %v\n", batchGetDuration) } diff --git a/example/tpt/swagger/app/api-spec.json b/example/tpt/swagger/app/api-spec.json index f28b111..4f9afec 100644 --- a/example/tpt/swagger/app/api-spec.json +++ b/example/tpt/swagger/app/api-spec.json @@ -3,23 +3,8 @@ { "cases": [ { - "expect": { - "result": { - "cache_mode": "sample", - "cleanup": "sample", - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample", - "result": true - } - ] - }, - "status": 0 - }, - "method": "GET", "name": "缓存全部测试", + "method": "GET", "passed": true, "response": { "result": { @@ -112,6 +97,21 @@ ] }, "status": 0 + }, + "expect": { + "result": { + "cache_mode": "sample", + "cleanup": "sample", + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample", + "result": true + } + ] + }, + "status": 0 } } ], @@ -126,20 +126,20 @@ { "cases": [ { - "expect": { - "result": { - "message": "sample" - }, - "status": 0 - }, - "method": "GET", "name": "批量缓存测试", + "method": "GET", "passed": true, "response": { "result": { "message": "批量缓存测试完成,请查看控制台输出和日志文件" }, "status": 0 + }, + "expect": { + "result": { + "message": "sample" + }, + "status": 0 } } ], @@ -154,22 +154,8 @@ { "cases": [ { - "expect": { - "result": { - "success": true, - "test_name": "sample", - "tests": [ - { - "name": "sample", - "result": true - } - ], - "timestamp": "sample" - }, - "status": 0 - }, - "method": "GET", "name": "兼容模式测试", + "method": "GET", "passed": true, "response": { "result": { @@ -200,7 +186,7 @@ "key_in_new_table": false, "name": "4. 测试兼容模式老表回退读取", "result": false, - "test_key": "test_compat_fallback_1774804199747243800" + "test_key": "test_compat_fallback_1775925593923602500" }, { "name": "5. 测试兼容模式写新删老", @@ -218,7 +204,21 @@ "result": false } ], - "timestamp": "2026-03-30 01:09:59" + "timestamp": "2026-04-12 00:39:53" + }, + "status": 0 + }, + "expect": { + "result": { + "success": true, + "test_name": "sample", + "tests": [ + { + "name": "sample", + "result": true + } + ], + "timestamp": "sample" }, "status": 0 } @@ -233,390 +233,19 @@ "tested": true }, { - "cases": [ - { - "expect": { - "error": { - "msg": "请提供 table 参数", - "type": "内部系统异常" - }, - "result": { - "msg": "请提供 table 参数", - "type": "内部系统异常" - }, - "status": 1 - }, - "method": "GET", - "name": "不传table参数", - "passed": true, - "response": { - "error": { - "msg": "请提供 table 参数", - "type": "内部系统异常" - }, - "result": { - "msg": "请提供 table 参数", - "type": "内部系统异常" - }, - "status": 1 - } - }, - { - "expect": { - "result": { - "columns": [ - { - "name": "sample", - "type": "sample" - } - ], - "sample_data": [], - "table": "sample" - }, - "status": 0 - }, - "method": "GET", - "name": "USER_TAB_COLUMNS 查询", - "passed": true, - "query": { - "table": "admin" - }, - "response": { - "result": { - "columns": [ - { - "label": "ID", - "must": "N", - "name": "id", - "type": "INT" - }, - { - "label": "ID", - "must": "N", - "name": "id", - "type": "INT" - }, - { - "label": null, - "must": "N", - "name": "id", - "type": "INT" - }, - { - "label": null, - "must": "N", - "name": "id", - "type": "INT" - }, - { - "label": "用户名", - "must": "Y", - "name": "name", - "type": "VARCHAR" - }, - { - "label": null, - "must": "Y", - "name": "name", - "type": "VARCHAR" - }, - { - "label": "用户名", - "must": "Y", - "name": "name", - "type": "VARCHAR" - }, - { - "label": null, - "must": "Y", - "name": "name", - "type": "VARCHAR" - }, - { - "label": null, - "must": "Y", - "name": "phone", - "type": "VARCHAR" - }, - { - "label": "密码", - "must": "Y", - "name": "password", - "type": "VARCHAR" - }, - { - "label": null, - "must": "Y", - "name": "password", - "type": "VARCHAR" - }, - { - "label": null, - "must": "Y", - "name": "create_time", - "type": "TIMESTAMP" - }, - { - "label": null, - "must": "Y", - "name": "state", - "type": "INT" - }, - { - "label": "创建时间", - "must": "Y", - "name": "create_time", - "type": "TIMESTAMP" - }, - { - "label": "状态:0-正常,1-异常", - "must": "Y", - "name": "state", - "type": "INT" - }, - { - "label": null, - "must": "Y", - "name": "modify_time", - "type": "TIMESTAMP" - }, - { - "label": "变更时间", - "must": "Y", - "name": "modify_time", - "type": "TIMESTAMP" - }, - { - "label": "密码", - "must": "Y", - "name": "password", - "type": "VARCHAR" - }, - { - "label": null, - "must": "Y", - "name": "password", - "type": "VARCHAR" - }, - { - "label": null, - "must": "Y", - "name": "state", - "type": "INT" - }, - { - "label": "状态:0-正常,1-异常", - "must": "Y", - "name": "state", - "type": "INT" - }, - { - "label": null, - "must": "Y", - "name": "role_id", - "type": "INT" - }, - { - "label": null, - "must": "Y", - "name": "title", - "type": "VARCHAR" - }, - { - "label": null, - "must": "Y", - "name": "create_time", - "type": "TIMESTAMP" - }, - { - "label": "创建时间", - "must": "Y", - "name": "create_time", - "type": "TIMESTAMP" - }, - { - "label": null, - "must": "Y", - "name": "modify_time", - "type": "TIMESTAMP" - }, - { - "label": "变更时间", - "must": "Y", - "name": "modify_time", - "type": "TIMESTAMP" - } - ], - "sample_data": [ - { - "create_time": "2026-03-20 10:28:27", - "id": 1, - "modify_time": "2026-03-20 10:28:27", - "name": "超级管理员", - "password": "admin123", - "phone": "13800000001", - "role_id": 1, - "state": 1, - "title": "系统管理员" - }, - { - "create_time": "2026-03-20 10:28:27", - "id": 2, - "modify_time": "2026-03-20 10:28:27", - "name": "编辑员", - "password": "editor123", - "phone": "13800000002", - "role_id": 2, - "state": 1, - "title": "内容编辑" - }, - { - "create_time": "2026-03-20 10:28:27", - "id": 3, - "modify_time": "2026-03-20 10:28:27", - "name": "审核员", - "password": "review123", - "phone": "13800000003", - "role_id": 3, - "state": 1, - "title": "内容审核" - } - ], - "table": "admin" - }, - "status": 0 - } - } - ], + "cases": null, "ctr": "dmdb", - "method": "GET", - "params": [ - { - "in": "query", - "name": "table", - "required": false, - "type": "string" - } - ], + "method": "POST", + "params": [], "path": "/app/dmdb/describe", "project": "app", "summary": "查询达梦表结构", "tested": true }, { - "cases": [ - { - "expect": { - "result": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample", - "result": true - } - ] - }, - "status": 0 - }, - "method": "GET", - "name": "达梦原生SQL", - "passed": true, - "response": { - "result": { - "name": "达梦原生SQL测试", - "success": true, - "tests": [ - { - "count": 5, - "name": "Query 原生查询 (双引号)", - "result": true - }, - { - "affected": 1, - "name": "Exec 原生执行 (双引号)", - "result": true - }, - { - "count": 47, - "name": "USER_TABLES 查询", - "result": true - }, - { - "columns": [ - { - "COLUMN_NAME": "id", - "DATA_TYPE": "INT" - }, - { - "COLUMN_NAME": "id", - "DATA_TYPE": "INT" - }, - { - "COLUMN_NAME": "name", - "DATA_TYPE": "VARCHAR" - }, - { - "COLUMN_NAME": "name", - "DATA_TYPE": "VARCHAR" - }, - { - "COLUMN_NAME": "password", - "DATA_TYPE": "VARCHAR" - }, - { - "COLUMN_NAME": "phone", - "DATA_TYPE": "VARCHAR" - }, - { - "COLUMN_NAME": "create_time", - "DATA_TYPE": "TIMESTAMP" - }, - { - "COLUMN_NAME": "state", - "DATA_TYPE": "INT" - }, - { - "COLUMN_NAME": "modify_time", - "DATA_TYPE": "TIMESTAMP" - }, - { - "COLUMN_NAME": "password", - "DATA_TYPE": "VARCHAR" - }, - { - "COLUMN_NAME": "state", - "DATA_TYPE": "INT" - }, - { - "COLUMN_NAME": "role_id", - "DATA_TYPE": "INT" - }, - { - "COLUMN_NAME": "title", - "DATA_TYPE": "VARCHAR" - }, - { - "COLUMN_NAME": "create_time", - "DATA_TYPE": "TIMESTAMP" - }, - { - "COLUMN_NAME": "modify_time", - "DATA_TYPE": "TIMESTAMP" - } - ], - "count": 15, - "name": "USER_TAB_COLUMNS 查询", - "result": true - } - ] - }, - "status": 0 - } - } - ], + "cases": null, "ctr": "dmdb", - "method": "GET", + "method": "POST", "params": [], "path": "/app/dmdb/rawsql", "project": "app", @@ -624,220 +253,9 @@ "tested": true }, { - "cases": [ - { - "expect": { - "result": { - "tables": [ - { - "name": "sample" - } - ] - }, - "status": 0 - }, - "method": "GET", - "name": "USER_TABLES 查询", - "passed": true, - "response": { - "result": { - "tables": [ - { - "label": null, - "name": "##HISTOGRAMS_TABLE" - }, - { - "label": null, - "name": "SYSALERTHISTORIES" - }, - { - "label": null, - "name": "SYSALERTNOTIFICATIONS" - }, - { - "label": null, - "name": "SYSALERTS" - }, - { - "label": null, - "name": "SYSJOBHISTORIES" - }, - { - "label": null, - "name": "SYSJOBHISTORIES2" - }, - { - "label": null, - "name": "SYSJOBS" - }, - { - "label": null, - "name": "SYSJOBSCHEDULES" - }, - { - "label": null, - "name": "SYSJOBSTEPS" - }, - { - "label": null, - "name": "SYSMAILINFO" - }, - { - "label": null, - "name": "SYSOPERATORS" - }, - { - "label": null, - "name": "SYSSTEPHISTORIES2" - }, - { - "label": null, - "name": "admin" - }, - { - "label": null, - "name": "article" - }, - { - "label": null, - "name": "cached" - }, - { - "label": null, - "name": "ctg" - }, - { - "label": null, - "name": "hotime_cache" - }, - { - "label": null, - "name": "test_batch" - }, - { - "label": "管理员", - "name": "admin" - }, - { - "label": "应用表", - "name": "app" - }, - { - "label": "应用分类表", - "name": "app_category" - }, - { - "label": "应用凭证表", - "name": "app_credential" - }, - { - "label": "应用评分表", - "name": "app_rating" - }, - { - "label": "应用评分附件表", - "name": "app_rating_file" - }, - { - "label": "开发者表", - "name": "developer" - }, - { - "label": "意见反馈表", - "name": "feedback" - }, - { - "label": "反馈附件表", - "name": "feedback_file" - }, - { - "label": "帮助分类表", - "name": "help_category" - }, - { - "label": "帮助问题表", - "name": "help_question" - }, - { - "label": null, - "name": "hotime_cache" - }, - { - "label": "登录日志表", - "name": "login_log" - }, - { - "label": "消息主表", - "name": "message" - }, - { - "label": "消息回调日志表", - "name": "message_callback_log" - }, - { - "label": "新闻公告表", - "name": "news" - }, - { - "label": "新闻分类表", - "name": "news_category" - }, - { - "label": "OAuth授权码表", - "name": "oauth_code" - }, - { - "label": "组织部门表", - "name": "org" - }, - { - "label": "组织应用关联表", - "name": "org_app" - }, - { - "label": "职务岗位表", - "name": "position" - }, - { - "label": "角色表", - "name": "role" - }, - { - "label": "用户表", - "name": "user" - }, - { - "label": "用户应用关联表", - "name": "user_app" - }, - { - "label": "用户认证表", - "name": "user_auth" - }, - { - "label": "用户设备表", - "name": "user_device" - }, - { - "label": "用户钉钉关联表", - "name": "user_dingtalk" - }, - { - "label": "用户消息关联表", - "name": "user_message" - }, - { - "label": "用户组织关联表", - "name": "user_org" - } - ] - }, - "status": 0 - } - } - ], + "cases": null, "ctr": "dmdb", - "method": "GET", + "method": "POST", "params": [], "path": "/app/dmdb/tables", "project": "app", @@ -847,14 +265,14 @@ { "cases": [ { - "expect": { + "name": "result是布尔", + "method": "GET", + "passed": true, + "response": { "result": true, "status": 0 }, - "method": "GET", - "name": "result是布尔", - "passed": true, - "response": { + "expect": { "result": true, "status": 0 } @@ -871,7 +289,13 @@ { "cases": [ { - "expect": { + "name": "名称为空-缺少必填字段", + "method": "POST", + "passed": true, + "form": { + "name": "" + }, + "response": { "error": { "msg": "名称不能为空", "type": "请求参数异常" @@ -882,13 +306,7 @@ }, "status": 3 }, - "form": { - "name": "" - }, - "method": "POST", - "name": "名称为空-缺少必填字段", - "passed": true, - "response": { + "expect": { "error": { "msg": "名称不能为空", "type": "请求参数异常" @@ -901,20 +319,12 @@ } }, { - "expect": { - "result": { - "created": true, - "id": 1, - "name": "sample" - }, - "status": 0 - }, + "name": "名称正确-结构+值校验", + "method": "POST", + "passed": true, "form": { "name": "测试商品" }, - "method": "POST", - "name": "名称正确-结构+值校验", - "passed": true, "response": { "result": { "created": true, @@ -922,6 +332,14 @@ "name": "测试商品" }, "status": 0 + }, + "expect": { + "result": { + "created": true, + "id": 1, + "name": "sample" + }, + "status": 0 } } ], @@ -929,10 +347,11 @@ "method": "POST", "params": [ { - "in": "form", "name": "name", - "required": false, - "type": "string" + "required": true, + "type": "string", + "example": "测试商品", + "in": "form" } ], "path": "/app/expect_demo/error_demo", @@ -943,16 +362,16 @@ { "cases": [ { - "expect": { - "result": 1, - "status": 0 - }, - "method": "GET", "name": "result是小数", + "method": "GET", "passed": true, "response": { "result": 3.14, "status": 0 + }, + "expect": { + "result": 1, + "status": 0 } } ], @@ -967,17 +386,8 @@ { "cases": [ { - "expect": { - "result": { - "id": 1, - "in_stock": true, - "name": "sample", - "price": 1 - }, - "status": 0 - }, - "method": "GET", "name": "result是Map-校验字段名+类型+值", + "method": "GET", "passed": true, "response": { "result": { @@ -987,6 +397,15 @@ "price": 19.9 }, "status": 0 + }, + "expect": { + "result": { + "id": 1, + "in_stock": true, + "name": "sample", + "price": 1 + }, + "status": 0 } } ], @@ -1001,34 +420,8 @@ { "cases": [ { - "expect": { - "result": { - "customer": { - "id": 1, - "name": "sample", - "phone": "sample" - }, - "extra": { - "delivery": { - "address": "sample", - "fee": 1 - } - }, - "id": 1, - "items": [ - { - "goods_name": "sample", - "id": 1, - "price": 1, - "quantity": 1 - } - ], - "title": "sample" - }, - "status": 0 - }, - "method": "GET", "name": "深层嵌套Map+Slice+Map", + "method": "GET", "passed": true, "response": { "result": { @@ -1061,6 +454,32 @@ "title": "测试订单" }, "status": 0 + }, + "expect": { + "result": { + "customer": { + "id": 1, + "name": "sample", + "phone": "sample" + }, + "extra": { + "delivery": { + "address": "sample", + "fee": 1 + } + }, + "id": 1, + "items": [ + { + "goods_name": "sample", + "id": 1, + "price": 1, + "quantity": 1 + } + ], + "title": "sample" + }, + "status": 0 } } ], @@ -1075,15 +494,8 @@ { "cases": [ { - "expect": { - "result": { - "data": "sample", - "id": 1 - }, - "status": 0 - }, - "method": "GET", "name": "有Verify值断言", + "method": "GET", "passed": true, "response": { "result": { @@ -1091,6 +503,13 @@ "id": 1 }, "status": 0 + }, + "expect": { + "result": { + "data": "sample", + "id": 1 + }, + "status": 0 } } ], @@ -1105,16 +524,16 @@ { "cases": [ { - "expect": { - "result": 1, - "status": 0 - }, - "method": "GET", "name": "result是整数", + "method": "GET", "passed": true, "response": { "result": 42, "status": 0 + }, + "expect": { + "result": 1, + "status": 0 } } ], @@ -1129,17 +548,8 @@ { "cases": [ { - "expect": { - "result": [ - { - "id": 1, - "name": "sample" - } - ], - "status": 0 - }, - "method": "GET", "name": "result直接是Slice", + "method": "GET", "passed": true, "response": { "result": [ @@ -1153,6 +563,15 @@ } ], "status": 0 + }, + "expect": { + "result": [ + { + "id": 1, + "name": "sample" + } + ], + "status": 0 } } ], @@ -1167,16 +586,16 @@ { "cases": [ { - "expect": { - "result": "样本", - "status": 0 - }, - "method": "GET", "name": "result是字符串", + "method": "GET", "passed": true, "response": { "result": "操作成功", "status": 0 + }, + "expect": { + "result": "样本", + "status": 0 } } ], @@ -1191,23 +610,12 @@ { "cases": [ { - "expect": { - "error": { - "msg": "名称不能为空", - "type": "请求参数异常" - }, - "result": { - "msg": "名称不能为空", - "type": "请求参数异常" - }, - "status": 3 - }, + "name": "名称为空", + "method": "POST", + "passed": true, "form": { "name": "" }, - "method": "POST", - "name": "名称为空", - "passed": true, "response": { "error": { "msg": "名称不能为空", @@ -1218,31 +626,55 @@ "type": "请求参数异常" }, "status": 3 + }, + "expect": { + "error": { + "msg": "名称不能为空", + "type": "请求参数异常" + }, + "result": { + "msg": "名称不能为空", + "type": "请求参数异常" + }, + "status": 3 } }, { + "name": "写入并校验DB+响应", + "method": "POST", + "passed": true, + "form": { + "name": "verify_test" + }, + "response": { + "result": { + "id": 107, + "name": "verify_test" + }, + "status": 0 + }, "expect": { "result": { "id": 1, "name": "sample" }, "status": 0 - }, - "form": { - "name": "verify_test" - }, - "method": "POST", - "name": "写入并校验DB+响应", - "passed": true, - "response": { - "result": { - "id": 196, - "name": "verify_test" - }, - "status": 0 } }, { + "name": "写入并校验DB-故意失败", + "method": "POST", + "passed": false, + "form": { + "name": "verify_fail" + }, + "response": { + "result": { + "id": 108, + "name": "verify_fail" + }, + "status": 0 + }, "expect": { "result": { "id": 1, @@ -1250,19 +682,6 @@ }, "status": 0 }, - "form": { - "name": "verify_fail" - }, - "method": "POST", - "name": "写入并校验DB-故意失败", - "passed": false, - "response": { - "result": { - "id": 197, - "name": "verify_fail" - }, - "status": 0 - }, "verifyError": "test_batch.state 期望 99, 实际 0(接口写入 0,故意校验 99 演示失败效果)" } ], @@ -1270,10 +689,11 @@ "method": "POST", "params": [ { - "in": "form", "name": "name", - "required": false, - "type": "string" + "required": true, + "type": "string", + "example": "verify_fail", + "in": "form" } ], "path": "/app/expect_demo/verify_demo", @@ -1526,6 +946,7 @@ "name": "table", "required": false, "type": "string", + "example": "admin", "in": "query" } ], @@ -1716,20 +1137,8 @@ { "cases": [ { - "expect": { - "result": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "status": 0 - }, - "method": "GET", "name": "聚合函数", + "method": "GET", "passed": true, "response": { "result": { @@ -1737,35 +1146,35 @@ "success": true, "tests": [ { - "count": 99, + "count": 11, "name": "Count 总数统计", "result": true }, { - "count": 81, + "count": 9, "name": "Count 条件统计", "result": true }, { - "lastQuery": "SELECT SUM(click_num) FROM \"article\" WHERE \"state\" =? ;", + "lastQuery": "SELECT SUM(click_num) FROM `article` WHERE `state` =? ;", "name": "Sum 求和 (单字段名)", "result": true, - "sum": 110799 + "sum": 12311 }, { - "avg": 1367.888889, - "lastQuery": "SELECT AVG(click_num) FROM \"article\" WHERE \"state\" =? ;", + "avg": 1367.8889, + "lastQuery": "SELECT AVG(click_num) FROM `article` WHERE `state` =? ;", "name": "Avg 平均值 (单字段名)", "result": true }, { - "lastQuery": "SELECT MAX(click_num) FROM \"article\" WHERE \"state\" =? ;", + "lastQuery": "SELECT MAX(click_num) FROM `article` WHERE `state` =? ;", "max": 9999, "name": "Max 最大值 (单字段名)", "result": true }, { - "lastQuery": "SELECT MIN(sort) FROM \"article\" WHERE \"state\" =? ;", + "lastQuery": "SELECT MIN(sort) FROM `article` WHERE `state` =? ;", "min": 1, "name": "Min 最小值 (单字段名)", "result": true @@ -1775,33 +1184,33 @@ "result": true, "stats": [ { - "article_count": 36, - "avg_clicks": "450", + "article_count": 4, + "avg_clicks": 450, "ctg_id": 2, - "total_clicks": 16200 + "total_clicks": 1800 }, { - "article_count": 27, - "avg_clicks": "3461", + "article_count": 3, + "avg_clicks": 3461, "ctg_id": 1, - "total_clicks": 93447 + "total_clicks": 10383 }, { - "article_count": 9, - "avg_clicks": "32", + "article_count": 1, + "avg_clicks": 32, "ctg_id": 3, - "total_clicks": 288 + "total_clicks": 32 }, { - "article_count": 9, - "avg_clicks": "96", + "article_count": 1, + "avg_clicks": 96, "ctg_id": 4, - "total_clicks": 864 + "total_clicks": 96 } ] }, { - "lastQuery": "SELECT SUM(\"article\".\"click_num\") FROM \"article\" WHERE \"state\" =? ;", + "lastQuery": "SELECT SUM(`article`.`click_num`) FROM `article` WHERE `state` =? ;", "match_single_field": false, "name": "Sum 求和 (table.column 格式)", "result": true, @@ -1810,23 +1219,35 @@ { "name": "聚合函数一致性验证", "result": false, - "summary": "Sum: 110799=0, Avg: 1367.888889=0, Max: 9999=0, Min: 1=0" + "summary": "Sum: 12311=0, Avg: 1367.8889=0, Max: 9999=0, Min: 1=0" }, { - "lastQuery": "SELECT SUM(\"article\".\"click_num\") FROM \"article\" LEFT JOIN \"ctg\" ON \"article\".\"ctg_id\" = \"ctg\".\"id\" WHERE \"article\".\"state\" =? ;", + "lastQuery": "SELECT SUM(`article`.`click_num`) FROM `article` LEFT JOIN `ctg` ON `article`.`ctg_id` = `ctg`.`id` WHERE `article`.`state` =? ;", "name": "Sum 带 JOIN (table.column 格式)", "result": true, "sum": 0 }, { - "count": 81, - "lastQuery": "SELECT COUNT(*) FROM \"article\" LEFT JOIN \"ctg\" ON \"article\".\"ctg_id\" = \"ctg\".\"id\" WHERE \"article\".\"state\" =? ;", + "count": 9, + "lastQuery": "SELECT COUNT(*) FROM `article` LEFT JOIN `ctg` ON `article`.`ctg_id` = `ctg`.`id` WHERE `article`.`state` =? ;", "name": "Count 带 JOIN", "result": true } ] }, "status": 0 + }, + "expect": { + "result": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "status": 0 } } ], @@ -1841,96 +1262,8 @@ { "cases": [ { - "expect": { - "result": { - "1_basic_crud": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample", - "result": true - } - ] - }, - "2_condition_syntax": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample", - "result": true - } - ] - }, - "3_chain_query": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "4_join_query": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "5_aggregate": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "6_pagination": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "7_batch_insert": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "8_upsert": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "9_transaction": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - } - }, - "status": 0 - }, - "method": "GET", "name": "全部测试", + "method": "GET", "passed": true, "response": { "result": { @@ -1939,19 +1272,19 @@ "success": true, "tests": [ { - "adminId": 97, - "lastQuery": "INSERT INTO \"admin\" (\"create_time\",\"modify_time\",\"name\",\"phone\",\"state\",\"password\",\"role_id\",\"title\") VALUES (NOW(),NOW(),?,?,?,?,?,?);", + "adminId": 40, + "lastQuery": "INSERT INTO `admin` (`password`,`role_id`,`title`,`create_time`,`modify_time`,`name`,`phone`,`state`) VALUES (?,?,?,NOW(),NOW(),?,?,?);", "name": "Insert 插入测试 (admin表)", "result": true }, { "admin": { - "create_time": "2026-03-30 01:10:06", - "id": 97, - "modify_time": "2026-03-30 01:10:06", - "name": "测试管理员_1774804206", + "create_time": "2026-04-11 16:39:56", + "id": 40, + "modify_time": "2026-04-11 16:39:56", + "name": "测试管理员_1775925594", "password": "test123456", - "phone": "13874804206", + "phone": "13875925594", "role_id": 1, "state": 1, "title": "测试职位" @@ -1960,13 +1293,13 @@ "result": true }, { - "count": 4, + "count": 5, "name": "Select 单条件查询测试", "result": true }, { - "count": 4, - "lastQuery": "SELECT * FROM \"admin\" WHERE \"role_id\" \u003e? AND \"state\" =? ORDER BY id DESC LIMIT 5 ;", + "count": 5, + "lastQuery": "SELECT * FROM `admin` WHERE `role_id` \u003e? AND `state` =? ORDER BY id DESC LIMIT 5 ;", "name": "Select 多条件自动AND测试", "result": true }, @@ -1993,7 +1326,7 @@ }, { "count": 3, - "lastQuery": "SELECT id,title,state FROM \"article\" WHERE \"state\" !=? LIMIT 3 ;", + "lastQuery": "SELECT id,title,state FROM `article` WHERE `state` !=? LIMIT 3 ;", "name": "不等于条件 ([!])", "result": true }, @@ -2008,8 +1341,8 @@ "result": true }, { - "count": 3, - "lastQuery": "SELECT id,title FROM \"article\" WHERE \"title\" LIKE ? LIMIT 3 ;", + "count": 1, + "lastQuery": "SELECT id,title FROM `article` WHERE `title` LIKE ? LIMIT 3 ;", "name": "LIKE 模糊查询 ([~])", "result": true }, @@ -2020,7 +1353,7 @@ }, { "count": 3, - "lastQuery": "SELECT id,title,click_num FROM \"article\" WHERE \"click_num\" BETWEEN ? AND ? LIMIT 3 ;", + "lastQuery": "SELECT id,title,click_num FROM `article` WHERE `click_num` BETWEEN ? AND ? LIMIT 3 ;", "name": "BETWEEN 区间查询 ([\u003c\u003e])", "result": true }, @@ -2031,7 +1364,7 @@ }, { "count": 5, - "lastQuery": "SELECT id,title FROM \"article\" WHERE (\"id\" BETWEEN ? AND ? ) LIMIT 5 ;", + "lastQuery": "SELECT id,title FROM `article` WHERE (`id` BETWEEN ? AND ? ) LIMIT 5 ;", "name": "IN 查询", "result": true }, @@ -2052,7 +1385,7 @@ }, { "count": 3, - "lastQuery": "SELECT id,title,create_time FROM \"article\" WHERE \"create_time\" \u003e DATEADD(DAY, -365, NOW()) LIMIT 3 ;", + "lastQuery": "SELECT id,title,create_time FROM `article` WHERE create_time \u003e DATE_SUB(NOW(), INTERVAL 365 DAY) LIMIT 3 ;", "name": "直接 SQL 片段查询 ([##])", "result": true }, @@ -2068,7 +1401,7 @@ }, { "count": 5, - "lastQuery": "SELECT id,title,sort,state FROM \"article\" WHERE (( \"click_num\" \u003e? OR \"sort\" \u003e=? ) AND \"state\" =?) LIMIT 5 ;", + "lastQuery": "SELECT id,title,sort,state FROM `article` WHERE (( `click_num` \u003e? OR `sort` \u003e=? ) AND `state` =?) LIMIT 5 ;", "name": "嵌套 AND/OR 条件", "result": true } @@ -2079,17 +1412,17 @@ "success": true, "tests": [ { - "count": 81, + "count": 9, "name": "基本链式查询 Table().Where().Select()", "result": true }, { - "count": 81, + "count": 9, "name": "链式 And 条件", "result": true }, { - "count": 81, + "count": 9, "name": "链式 Or 条件", "result": true }, @@ -2113,7 +1446,7 @@ "result": true }, { - "count": 81, + "count": 9, "name": "链式 Count 统计", "result": true }, @@ -2127,19 +1460,19 @@ "result": true, "stats": [ { - "cnt": 27, + "cnt": 3, "ctg_id": 1 }, { - "cnt": 36, + "cnt": 4, "ctg_id": 2 }, { - "cnt": 9, + "cnt": 1, "ctg_id": 3 }, { - "cnt": 9, + "cnt": 1, "ctg_id": 4 } ] @@ -2168,6 +1501,11 @@ "id": 2, "title": "新闻发布系统上线公告" }, + { + "ctg_name": "新闻资讯", + "id": 11, + "title": "高点击量文章" + }, { "ctg_name": "技术文档", "id": 3, @@ -2177,11 +1515,6 @@ "ctg_name": "技术文档", "id": 4, "title": "API接口设计规范" - }, - { - "ctg_name": "公告通知", - "id": 5, - "title": "系统维护通知" } ], "name": "LEFT JOIN 链式查询", @@ -2189,7 +1522,7 @@ }, { "count": 5, - "lastQuery": "SELECT \"article\".\"id\", \"article\".\"title\", \"ctg\".\"name\" as ctg_name FROM \"article\" LEFT JOIN \"ctg\" ON \"article\".\"ctg_id\" = \"ctg\".\"id\" WHERE \"article\".\"state\" =? LIMIT 5 ;", + "lastQuery": "SELECT `article`.`id`, `article`.`title`, `ctg`.`name` as ctg_name FROM `article` LEFT JOIN `ctg` ON `article`.`ctg_id` = `ctg`.`id` WHERE `article`.`state` =? LIMIT 5 ;", "name": "传统 JOIN 语法", "result": true }, @@ -2208,6 +1541,12 @@ "id": 2, "title": "新闻发布系统上线公告" }, + { + "admin_name": "编辑员", + "ctg_name": "新闻资讯", + "id": 11, + "title": "高点击量文章" + }, { "admin_name": "超级管理员", "ctg_name": "技术文档", @@ -2219,12 +1558,6 @@ "ctg_name": "技术文档", "id": 4, "title": "API接口设计规范" - }, - { - "admin_name": "超级管理员", - "ctg_name": "公告通知", - "id": 5, - "title": "系统维护通知" } ], "name": "多表 JOIN", @@ -2242,35 +1575,35 @@ "success": true, "tests": [ { - "count": 99, + "count": 11, "name": "Count 总数统计", "result": true }, { - "count": 81, + "count": 9, "name": "Count 条件统计", "result": true }, { - "lastQuery": "SELECT SUM(click_num) FROM \"article\" WHERE \"state\" =? ;", + "lastQuery": "SELECT SUM(click_num) FROM `article` WHERE `state` =? ;", "name": "Sum 求和 (单字段名)", "result": true, - "sum": 110799 + "sum": 12311 }, { - "avg": 1367.888889, - "lastQuery": "SELECT AVG(click_num) FROM \"article\" WHERE \"state\" =? ;", + "avg": 1367.8889, + "lastQuery": "SELECT AVG(click_num) FROM `article` WHERE `state` =? ;", "name": "Avg 平均值 (单字段名)", "result": true }, { - "lastQuery": "SELECT MAX(click_num) FROM \"article\" WHERE \"state\" =? ;", + "lastQuery": "SELECT MAX(click_num) FROM `article` WHERE `state` =? ;", "max": 9999, "name": "Max 最大值 (单字段名)", "result": true }, { - "lastQuery": "SELECT MIN(sort) FROM \"article\" WHERE \"state\" =? ;", + "lastQuery": "SELECT MIN(sort) FROM `article` WHERE `state` =? ;", "min": 1, "name": "Min 最小值 (单字段名)", "result": true @@ -2280,33 +1613,33 @@ "result": true, "stats": [ { - "article_count": 36, - "avg_clicks": "450", + "article_count": 4, + "avg_clicks": 450, "ctg_id": 2, - "total_clicks": 16200 + "total_clicks": 1800 }, { - "article_count": 27, - "avg_clicks": "3461", + "article_count": 3, + "avg_clicks": 3461, "ctg_id": 1, - "total_clicks": 93447 + "total_clicks": 10383 }, { - "article_count": 9, - "avg_clicks": "32", + "article_count": 1, + "avg_clicks": 32, "ctg_id": 3, - "total_clicks": 288 + "total_clicks": 32 }, { - "article_count": 9, - "avg_clicks": "96", + "article_count": 1, + "avg_clicks": 96, "ctg_id": 4, - "total_clicks": 864 + "total_clicks": 96 } ] }, { - "lastQuery": "SELECT SUM(\"article\".\"click_num\") FROM \"article\" WHERE \"state\" =? ;", + "lastQuery": "SELECT SUM(`article`.`click_num`) FROM `article` WHERE `state` =? ;", "match_single_field": false, "name": "Sum 求和 (table.column 格式)", "result": true, @@ -2315,17 +1648,17 @@ { "name": "聚合函数一致性验证", "result": false, - "summary": "Sum: 110799=0, Avg: 1367.888889=0, Max: 9999=0, Min: 1=0" + "summary": "Sum: 12311=0, Avg: 1367.8889=0, Max: 9999=0, Min: 1=0" }, { - "lastQuery": "SELECT SUM(\"article\".\"click_num\") FROM \"article\" LEFT JOIN \"ctg\" ON \"article\".\"ctg_id\" = \"ctg\".\"id\" WHERE \"article\".\"state\" =? ;", + "lastQuery": "SELECT SUM(`article`.`click_num`) FROM `article` LEFT JOIN `ctg` ON `article`.`ctg_id` = `ctg`.`id` WHERE `article`.`state` =? ;", "name": "Sum 带 JOIN (table.column 格式)", "result": true, "sum": 0 }, { - "count": 81, - "lastQuery": "SELECT COUNT(*) FROM \"article\" LEFT JOIN \"ctg\" ON \"article\".\"ctg_id\" = \"ctg\".\"id\" WHERE \"article\".\"state\" =? ;", + "count": 9, + "lastQuery": "SELECT COUNT(*) FROM `article` LEFT JOIN `ctg` ON `article`.`ctg_id` = `ctg`.`id` WHERE `article`.`state` =? ;", "name": "Count 带 JOIN", "result": true } @@ -2341,7 +1674,7 @@ "result": true }, { - "count": 5, + "count": 4, "name": "PageSelect 第二页", "result": true }, @@ -2352,7 +1685,7 @@ }, { "count": 3, - "lastQuery": "SELECT id,title FROM \"article\" WHERE (\"state\" =?) LIMIT 3 OFFSET 2 ;", + "lastQuery": "SELECT id,title FROM `article` WHERE (`state` =?) LIMIT 3 OFFSET 2 ;", "name": "Offset 偏移查询", "result": true } @@ -2364,7 +1697,7 @@ "tests": [ { "affected": 3, - "lastQuery": "INSERT INTO \"test_batch\" (\"name\", \"state\", \"title\") VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)", + "lastQuery": "INSERT INTO `test_batch` (`name`, `state`, `title`) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)", "name": "Inserts 批量插入", "result": true }, @@ -2381,21 +1714,21 @@ "tests": [ { "affected": 1, - "lastQuery": "MERGE INTO \"admin\" USING (SELECT ? AS \"title\", NOW() AS \"create_time\", NOW() AS \"modify_time\", ? AS \"name\", ? AS \"phone\", ? AS \"state\", ? AS \"password\", ? AS \"role_id\") src ON (\"admin\".\"phone\" = src.\"phone\") WHEN MATCHED THEN UPDATE SET \"name\" = src.\"name\", \"state\" = src.\"state\", \"title\" = src.\"title\", \"modify_time\" = NOW() WHEN NOT MATCHED THEN INSERT (\"title\", \"create_time\", \"modify_time\", \"name\", \"phone\", \"state\", \"password\", \"role_id\") VALUES (src.\"title\", src.\"create_time\", src.\"modify_time\", src.\"name\", src.\"phone\", src.\"state\", src.\"password\", src.\"role_id\")", + "lastQuery": "INSERT INTO `admin` (`phone`, `state`, `password`, `role_id`, `title`, `create_time`, `modify_time`, `name`) VALUES (?, ?, ?, ?, ?, NOW(), NOW(), ?) ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `state` = VALUES(`state`), `title` = VALUES(`title`), `modify_time` = NOW()", "name": "Upsert 插入新记录 (admin表)", "result": true }, { - "affected": 1, + "affected": 2, "name": "Upsert 更新已存在记录", "result": true, "updatedAdmin": { - "create_time": "2026-03-30 01:10:10", - "id": 98, - "modify_time": "2026-03-30 01:10:10", + "create_time": "2026-04-11 16:39:56", + "id": 41, + "modify_time": "2026-04-11 16:39:56", "name": "Upsert更新后管理员", "password": "test123", - "phone": "19974804210", + "phone": "19975925594", "role_id": 2, "state": 1, "title": "更新后职位" @@ -2414,13 +1747,101 @@ }, { "name": "事务回滚", - "recordRolledBack": true, + "recordRolledBack": false, "result": true } ] } }, "status": 0 + }, + "expect": { + "result": { + "1_basic_crud": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample", + "result": true + } + ] + }, + "2_condition_syntax": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample", + "result": true + } + ] + }, + "3_chain_query": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "4_join_query": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "5_aggregate": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "6_pagination": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "7_batch_insert": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "8_upsert": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "9_transaction": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + } + }, + "status": 0 } } ], @@ -2435,20 +1856,8 @@ { "cases": [ { - "expect": { - "result": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "status": 0 - }, - "method": "GET", "name": "批量插入", + "method": "GET", "passed": true, "response": { "result": { @@ -2457,7 +1866,7 @@ "tests": [ { "affected": 3, - "lastQuery": "INSERT INTO \"test_batch\" (\"name\", \"state\", \"title\") VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)", + "lastQuery": "INSERT INTO `test_batch` (`name`, `state`, `title`) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)", "name": "Inserts 批量插入", "result": true }, @@ -2469,6 +1878,18 @@ ] }, "status": 0 + }, + "expect": { + "result": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "status": 0 } } ], @@ -2483,20 +1904,8 @@ { "cases": [ { - "expect": { - "result": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "status": 0 - }, - "method": "GET", "name": "链式查询", + "method": "GET", "passed": true, "response": { "result": { @@ -2504,17 +1913,17 @@ "success": true, "tests": [ { - "count": 81, + "count": 9, "name": "基本链式查询 Table().Where().Select()", "result": true }, { - "count": 81, + "count": 9, "name": "链式 And 条件", "result": true }, { - "count": 81, + "count": 9, "name": "链式 Or 条件", "result": true }, @@ -2538,7 +1947,7 @@ "result": true }, { - "count": 81, + "count": 9, "name": "链式 Count 统计", "result": true }, @@ -2552,19 +1961,19 @@ "result": true, "stats": [ { - "cnt": 27, + "cnt": 3, "ctg_id": 1 }, { - "cnt": 36, + "cnt": 4, "ctg_id": 2 }, { - "cnt": 9, + "cnt": 1, "ctg_id": 3 }, { - "cnt": 9, + "cnt": 1, "ctg_id": 4 } ] @@ -2577,6 +1986,18 @@ ] }, "status": 0 + }, + "expect": { + "result": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "status": 0 } } ], @@ -2591,21 +2012,8 @@ { "cases": [ { - "expect": { - "result": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample", - "result": true - } - ] - }, - "status": 0 - }, - "method": "GET", "name": "条件查询", + "method": "GET", "passed": true, "response": { "result": { @@ -2619,7 +2027,7 @@ }, { "count": 3, - "lastQuery": "SELECT id,title,state FROM \"article\" WHERE \"state\" !=? LIMIT 3 ;", + "lastQuery": "SELECT id,title,state FROM `article` WHERE `state` !=? LIMIT 3 ;", "name": "不等于条件 ([!])", "result": true }, @@ -2634,8 +2042,8 @@ "result": true }, { - "count": 3, - "lastQuery": "SELECT id,title FROM \"article\" WHERE \"title\" LIKE ? LIMIT 3 ;", + "count": 1, + "lastQuery": "SELECT id,title FROM `article` WHERE `title` LIKE ? LIMIT 3 ;", "name": "LIKE 模糊查询 ([~])", "result": true }, @@ -2646,7 +2054,7 @@ }, { "count": 3, - "lastQuery": "SELECT id,title,click_num FROM \"article\" WHERE \"click_num\" BETWEEN ? AND ? LIMIT 3 ;", + "lastQuery": "SELECT id,title,click_num FROM `article` WHERE `click_num` BETWEEN ? AND ? LIMIT 3 ;", "name": "BETWEEN 区间查询 ([\u003c\u003e])", "result": true }, @@ -2657,7 +2065,7 @@ }, { "count": 5, - "lastQuery": "SELECT id,title FROM \"article\" WHERE (\"id\" BETWEEN ? AND ? ) LIMIT 5 ;", + "lastQuery": "SELECT id,title FROM `article` WHERE (`id` BETWEEN ? AND ? ) LIMIT 5 ;", "name": "IN 查询", "result": true }, @@ -2678,7 +2086,7 @@ }, { "count": 3, - "lastQuery": "SELECT id,title,create_time FROM \"article\" WHERE \"create_time\" \u003e DATEADD(DAY, -365, NOW()) LIMIT 3 ;", + "lastQuery": "SELECT id,title,create_time FROM `article` WHERE create_time \u003e DATE_SUB(NOW(), INTERVAL 365 DAY) LIMIT 3 ;", "name": "直接 SQL 片段查询 ([##])", "result": true }, @@ -2694,13 +2102,26 @@ }, { "count": 5, - "lastQuery": "SELECT id,title,sort,state FROM \"article\" WHERE (( \"click_num\" \u003e? OR \"sort\" \u003e=? ) AND \"state\" =?) LIMIT 5 ;", + "lastQuery": "SELECT id,title,sort,state FROM `article` WHERE (( `click_num` \u003e? OR `sort` \u003e=? ) AND `state` =?) LIMIT 5 ;", "name": "嵌套 AND/OR 条件", "result": true } ] }, "status": 0 + }, + "expect": { + "result": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample", + "result": true + } + ] + }, + "status": 0 } } ], @@ -2715,21 +2136,8 @@ { "cases": [ { - "expect": { - "result": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample", - "result": true - } - ] - }, - "status": 0 - }, - "method": "GET", "name": "CRUD测试", + "method": "GET", "passed": true, "response": { "result": { @@ -2737,19 +2145,19 @@ "success": true, "tests": [ { - "adminId": 99, - "lastQuery": "INSERT INTO \"admin\" (\"title\",\"create_time\",\"modify_time\",\"name\",\"phone\",\"state\",\"password\",\"role_id\") VALUES (?,NOW(),NOW(),?,?,?,?,?);", + "adminId": 45, + "lastQuery": "INSERT INTO `admin` (`password`,`role_id`,`title`,`create_time`,`modify_time`,`name`,`phone`,`state`) VALUES (?,?,?,NOW(),NOW(),?,?,?);", "name": "Insert 插入测试 (admin表)", "result": true }, { "admin": { - "create_time": "2026-03-30 01:10:11", - "id": 99, - "modify_time": "2026-03-30 01:10:11", - "name": "测试管理员_1774804211", + "create_time": "2026-04-11 16:40:03", + "id": 45, + "modify_time": "2026-04-11 16:40:03", + "name": "测试管理员_1775925600", "password": "test123456", - "phone": "13874804211", + "phone": "13875925600", "role_id": 1, "state": 1, "title": "测试职位" @@ -2758,13 +2166,13 @@ "result": true }, { - "count": 4, + "count": 5, "name": "Select 单条件查询测试", "result": true }, { - "count": 4, - "lastQuery": "SELECT * FROM \"admin\" WHERE \"role_id\" \u003e? AND \"state\" =? ORDER BY id DESC LIMIT 5 ;", + "count": 5, + "lastQuery": "SELECT * FROM `admin` WHERE `role_id` \u003e? AND `state` =? ORDER BY id DESC LIMIT 5 ;", "name": "Select 多条件自动AND测试", "result": true }, @@ -2781,6 +2189,19 @@ ] }, "status": 0 + }, + "expect": { + "result": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample", + "result": true + } + ] + }, + "status": 0 } } ], @@ -2795,20 +2216,8 @@ { "cases": [ { - "expect": { - "result": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "status": 0 - }, - "method": "GET", "name": "JOIN查询", + "method": "GET", "passed": true, "response": { "result": { @@ -2828,6 +2237,11 @@ "id": 2, "title": "新闻发布系统上线公告" }, + { + "ctg_name": "新闻资讯", + "id": 11, + "title": "高点击量文章" + }, { "ctg_name": "技术文档", "id": 3, @@ -2837,11 +2251,6 @@ "ctg_name": "技术文档", "id": 4, "title": "API接口设计规范" - }, - { - "ctg_name": "公告通知", - "id": 5, - "title": "系统维护通知" } ], "name": "LEFT JOIN 链式查询", @@ -2849,7 +2258,7 @@ }, { "count": 5, - "lastQuery": "SELECT \"article\".\"id\", \"article\".\"title\", \"ctg\".\"name\" as ctg_name FROM \"article\" LEFT JOIN \"ctg\" ON \"article\".\"ctg_id\" = \"ctg\".\"id\" WHERE \"article\".\"state\" =? LIMIT 5 ;", + "lastQuery": "SELECT `article`.`id`, `article`.`title`, `ctg`.`name` as ctg_name FROM `article` LEFT JOIN `ctg` ON `article`.`ctg_id` = `ctg`.`id` WHERE `article`.`state` =? LIMIT 5 ;", "name": "传统 JOIN 语法", "result": true }, @@ -2868,6 +2277,12 @@ "id": 2, "title": "新闻发布系统上线公告" }, + { + "admin_name": "编辑员", + "ctg_name": "新闻资讯", + "id": 11, + "title": "高点击量文章" + }, { "admin_name": "超级管理员", "ctg_name": "技术文档", @@ -2879,12 +2294,6 @@ "ctg_name": "技术文档", "id": 4, "title": "API接口设计规范" - }, - { - "admin_name": "超级管理员", - "ctg_name": "公告通知", - "id": 5, - "title": "系统维护通知" } ], "name": "多表 JOIN", @@ -2898,6 +2307,18 @@ ] }, "status": 0 + }, + "expect": { + "result": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "status": 0 } } ], @@ -2912,20 +2333,8 @@ { "cases": [ { - "expect": { - "result": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "status": 0 - }, - "method": "GET", "name": "分页查询", + "method": "GET", "passed": true, "response": { "result": { @@ -2938,7 +2347,7 @@ "result": true }, { - "count": 5, + "count": 4, "name": "PageSelect 第二页", "result": true }, @@ -2949,13 +2358,25 @@ }, { "count": 3, - "lastQuery": "SELECT id,title FROM \"article\" WHERE (\"state\" =?) LIMIT 3 OFFSET 2 ;", + "lastQuery": "SELECT id,title FROM `article` WHERE (`state` =?) LIMIT 3 OFFSET 2 ;", "name": "Offset 偏移查询", "result": true } ] }, "status": 0 + }, + "expect": { + "result": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "status": 0 } } ], @@ -2970,7 +2391,10 @@ { "cases": [ { - "expect": { + "name": "固定返回错误码2", + "method": "GET", + "passed": true, + "response": { "error": { "msg": "dsadasd", "type": "访问权限异常" @@ -2981,10 +2405,7 @@ }, "status": 2 }, - "method": "GET", - "name": "固定返回错误码2", - "passed": true, - "response": { + "expect": { "error": { "msg": "dsadasd", "type": "访问权限异常" @@ -3008,20 +2429,8 @@ { "cases": [ { - "expect": { - "result": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "status": 0 - }, - "method": "GET", "name": "事务测试", + "method": "GET", "passed": true, "response": { "result": { @@ -3035,12 +2444,24 @@ }, { "name": "事务回滚", - "recordRolledBack": true, + "recordRolledBack": false, "result": true } ] }, "status": 0 + }, + "expect": { + "result": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "status": 0 } } ], @@ -3055,20 +2476,8 @@ { "cases": [ { - "expect": { - "result": { - "name": "sample", - "success": true, - "tests": [ - { - "name": "sample" - } - ] - }, - "status": 0 - }, - "method": "GET", "name": "Upsert测试", + "method": "GET", "passed": true, "response": { "result": { @@ -3077,21 +2486,21 @@ "tests": [ { "affected": 1, - "lastQuery": "MERGE INTO \"admin\" USING (SELECT ? AS \"title\", NOW() AS \"create_time\", NOW() AS \"modify_time\", ? AS \"name\", ? AS \"phone\", ? AS \"state\", ? AS \"password\", ? AS \"role_id\") src ON (\"admin\".\"phone\" = src.\"phone\") WHEN MATCHED THEN UPDATE SET \"name\" = src.\"name\", \"state\" = src.\"state\", \"title\" = src.\"title\", \"modify_time\" = NOW() WHEN NOT MATCHED THEN INSERT (\"title\", \"create_time\", \"modify_time\", \"name\", \"phone\", \"state\", \"password\", \"role_id\") VALUES (src.\"title\", src.\"create_time\", src.\"modify_time\", src.\"name\", src.\"phone\", src.\"state\", src.\"password\", src.\"role_id\")", + "lastQuery": "INSERT INTO `admin` (`title`, `create_time`, `modify_time`, `name`, `phone`, `state`, `password`, `role_id`) VALUES (?, NOW(), NOW(), ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `state` = VALUES(`state`), `title` = VALUES(`title`), `modify_time` = NOW()", "name": "Upsert 插入新记录 (admin表)", "result": true }, { - "affected": 1, + "affected": 2, "name": "Upsert 更新已存在记录", "result": true, "updatedAdmin": { - "create_time": "2026-03-30 01:10:03", - "id": 96, - "modify_time": "2026-03-30 01:10:03", + "create_time": "2026-04-11 16:39:57", + "id": 43, + "modify_time": "2026-04-11 16:39:58", "name": "Upsert更新后管理员", "password": "test123", - "phone": "19974804203", + "phone": "19975925595", "role_id": 2, "state": 1, "title": "更新后职位" @@ -3100,6 +2509,18 @@ ] }, "status": 0 + }, + "expect": { + "result": { + "name": "sample", + "success": true, + "tests": [ + { + "name": "sample" + } + ] + }, + "status": 0 } } ], diff --git a/example/tpt/swagger/app/index.html b/example/tpt/swagger/app/index.html index ccc1331..b219829 100644 --- a/example/tpt/swagger/app/index.html +++ b/example/tpt/swagger/app/index.html @@ -93,6 +93,12 @@ pre.j.resp-j{max-height:calc(100vh - 280px);min-height:120px} .bd-bd textarea{width:100%;min-height:36px;max-height:40vh;padding:5px 8px;border:1px solid var(--border);border-radius:var(--r);background:var(--bg2);color:var(--fg);font-size:12px;font-family:Consolas,Monaco,monospace;resize:vertical;outline:none;overflow-y:auto} .bd-bd textarea:focus{border-color:var(--accent)} .req-star{color:var(--red);font-size:10px;font-weight:700;margin-right:2px;flex-shrink:0;line-height:1.2;align-self:center} +.jp-panel{margin-bottom:6px;border:1px solid var(--border);border-radius:var(--r);overflow:hidden} +.jp-hd{padding:3px 8px;background:var(--bg2);cursor:pointer;display:flex;align-items:center;font-size:11px;color:var(--fg2);user-select:none} +.jp-bd{padding:6px 8px;display:flex;flex-wrap:wrap;gap:4px;border-top:1px solid var(--border)} +.jp-tag{font-size:11px;padding:1px 6px;background:var(--bg3);border:1px solid var(--border);border-radius:var(--r);color:var(--fg2);display:inline-flex;align-items:center;gap:2px} +.jp-req{color:var(--accent);border-color:rgba(79,195,247,.3)} +.jp-type{color:#555;font-size:10px}
@@ -264,7 +270,24 @@ function buildLeft(e){ l+='
表单 (Form)
'; l+='
'; l+='
JSON
'; - l+='
'; + l+='
'; + if(e.params){ + const jp=e.params.filter(p=>p.in==='json'); + if(jp.length){ + const jpVis=localStorage.getItem('json_params_vis')!=='0'; + l+=''; + } + } + l+='
'; l+='
'; return l; } @@ -390,7 +413,7 @@ function pfFill(v){ if(nb){if(c.note){nb.textContent='备注: '+c.note;nb.style.display='block';}else{nb.textContent='';nb.style.display='none';}} if(c.query){ for(const[k,val]of Object.entries(c.query)){ - const req=C?.params?.find(p=>p.name===k&&p.in==='query')?.required||false; + const req=C?.params?.find(p=>p.name===k)?.required||false; addKVReq('q-rows',k,String(val),req); } } @@ -398,7 +421,7 @@ function pfFill(v){ if(c.json){ setAcc('json');const el=document.getElementById('xb'); if(el){ - const rq=new Set((C?.params||[]).filter(p=>p.required&&p.in==='json').map(p=>p.name)); + const rq=new Set((C?.params||[]).filter(p=>p.required).map(p=>p.name)); let obj=c.json; if(rq.size&&typeof obj==='object'&&obj!==null&&!Array.isArray(obj)){ const sorted={}; @@ -412,7 +435,7 @@ function pfFill(v){ if(fr){ fr.innerHTML=''; for(const[k,val]of Object.entries(c.form)){ - const req=C?.params?.find(p=>p.name===k&&p.in==='form')?.required||false; + const req=C?.params?.find(p=>p.name===k)?.required||false; addKVReq('f-rows',k,String(val),req); } addKV('f-rows'); @@ -488,6 +511,7 @@ function copyEl(id){navigator.clipboard.writeText(document.getElementById(id).te function cpNext(btn){let el=btn.closest('h5').nextElementSibling;let t='';while(el&&el.tagName!=='H5'){if(el.tagName==='PRE')t+=(t?'\n':'')+el.textContent;el=el.nextElementSibling;}navigator.clipboard.writeText(t).then(()=>{btn.textContent='已复制';setTimeout(()=>{btn.textContent='复制'},1200);});} function getExpVis(){const v=localStorage.getItem('expect_vis');return v===null||v==='1';} function toggleExpect(){const vis=getExpVis();const nv=!vis;localStorage.setItem('expect_vis',nv?'1':'0');document.querySelectorAll('.resp-wrap').forEach(w=>{w.classList.toggle('exp-on',nv);});document.querySelectorAll('.exp-btn').forEach(b=>{b.textContent=nv?'收起 预期响应':'展开 预期响应';});} +function togJP(){const bd=document.querySelector('.jp-bd');const ar=document.querySelector('.jp-ar');const tg=document.querySelector('.jp-toggle');if(!bd)return;const vis=bd.style.display!=='none';bd.style.display=vis?'none':'flex';if(ar)ar.classList.toggle('o',!vis);if(tg)tg.textContent=vis?'展开':'收起';localStorage.setItem('json_params_vis',vis?'0':'1');} function fj(o){return esc(JSON.stringify(o,null,2));} function fjp(o,params){ if(!params||!params.length||typeof o!=='object'||o===null||Array.isArray(o))return fj(o);