feat: 迁移 Admin Repository 到 MySQL 8 (#79)

This commit is contained in:
chengma
2026-08-10 02:21:31 +08:00
parent 05793e48b3
commit 13cfced80d
29 changed files with 390 additions and 240 deletions
+2 -10
View File
@@ -9,6 +9,7 @@ import (
"testing"
"time"
"cmautobuy/admin/internal/testutil"
"cmautobuy/admin/model"
"cmautobuy/admin/repository"
)
@@ -53,16 +54,7 @@ func TestListClientPageForUser_分页统计不泄露其他采购员客户端(t *
// 用真实的 migrations,这样表结构变了测试会跟着失败。
func newTestDB(t *testing.T) *sql.DB {
t.Helper()
db, err := repository.Open(t.TempDir())
if err != nil {
t.Fatalf("打开测试库失败: %v", err)
}
if err := repository.Migrate(db); err != nil {
t.Fatalf("迁移失败: %v", err)
}
t.Cleanup(func() { db.Close() })
return db
return testutil.OpenMySQL(t)
}
// insertTask 插一条待领取的任务,供领取相关的测试使用。
+2 -2
View File
@@ -305,7 +305,7 @@ func TestListPddProducts_规格数(t *testing.T) {
}
// skus_json 存进了坏数据时,列表页必须还能打开。
// json_array_length 碰到非法 JSON 会让整条查询报错,那样页面直接白屏。
// JSON_LENGTH 碰到非法 JSON 会让整条查询报错,那样页面直接白屏。
func TestListPddProducts_坏掉的采集结果不影响列表打开(t *testing.T) {
db := newTestDB(t)
createProduct(t, db, "100000000001")
@@ -843,7 +843,7 @@ func insertStaleCollectingProduct(t *testing.T, db *sql.DB, goodsID string) {
INSERT INTO tasks (task_id, task_type, status, assigned_client,
pdd_goods_url, pdd_goods_id, created_at, updated_at)
VALUES (?, 'collect', 'claimed', 'client-已离线',
'https://mobile.yangkeduo.com/goods.html?goods_id=' || ?, ?, ?, ?)`,
CONCAT('https://mobile.yangkeduo.com/goods.html?goods_id=', ?), ?, ?, ?)`,
"COL-STALE-"+goodsID, goodsID, goodsID, staleAt, staleAt); err != nil {
t.Fatalf("构造卡死任务失败: %v", err)
}
+1 -1
View File
@@ -206,7 +206,7 @@ func insertShopeeSKU(t *testing.T, db *sql.DB, goodsID, skuID string) {
if _, err := db.Exec(`
INSERT INTO shopee_products (goods_id, title, created_at, updated_at)
VALUES (?, '测试商品', ?, ?)
ON CONFLICT(goods_id) DO NOTHING`, goodsID, now, now); err != nil {
ON DUPLICATE KEY UPDATE goods_id = VALUES(goods_id)`, goodsID, now, now); err != nil {
t.Fatalf("插入蝦皮商品失败: %v", err)
}
if _, err := db.Exec(`
+8 -2
View File
@@ -57,11 +57,17 @@ func TestSybMapping_动态维度保存并复用(t *testing.T) {
if err := SaveSybMapping(db, "SYB-1", key, "USR-1"); err != nil {
t.Fatal(err)
}
detail, _ = GetSybProcessingDetail(db, "SYB-1")
detail, err = GetSybProcessingDetail(db, "SYB-1")
if err != nil {
t.Fatal(err)
}
if !detail.MappingValid {
t.Fatal("保存后相同蝦皮 SKU + 当前 PDD 商品应自动复用")
}
list, _ := ListSybOrdersView(db, "", SybStagePurchaseReady, 1)
list, err := ListSybOrdersView(db, "", SybStagePurchaseReady, 1)
if err != nil {
t.Fatal(err)
}
if list.Total != 1 || !list.Rows[0].CanPurchase || list.Rows[0].DefaultMaxPrice != "39.90" {
t.Fatalf("可采购阶段不对: %+v", list)
}
+16
View File
@@ -292,6 +292,22 @@ func submitInTx(
// 5. 记下幂等键。和业务写入在同一个事务里——
// 分开写的话,业务写成功但幂等没记上,客户端重试会被重复处理。
if err := repository.SaveIdempotent(tx, idemKey, hash, string(respJSON)); err != nil {
// 两个首次请求可能同时通过前面的“不存在”检查。唯一键会让其中一个
// 等待另一个提交后返回重复键;此时回滚本事务的业务写入,再读取赢家
// 保存的响应,保证并发重试拿到完全相同的结果。
if errors.Is(err, repository.ErrIdempotencyAlreadySaved) {
if rollbackErr := tx.Rollback(); rollbackErr != nil && !errors.Is(rollbackErr, sql.ErrTxDone) {
return "", fmt.Errorf("回滚重复幂等请求失败: %w", rollbackErr)
}
body, done, lookupErr := repository.LookupIdempotent(db, idemKey, hash)
if lookupErr != nil {
return "", lookupErr
}
if !done {
return "", fmt.Errorf("并发幂等请求已提交但未找到保存结果")
}
return body, nil
}
return "", err
}
+1 -9
View File
@@ -359,15 +359,7 @@ func writeEnvelope(t *testing.T, w http.ResponseWriter, status bool, msg string,
func newSyncTestDB(t *testing.T) *sql.DB {
t.Helper()
db, err := repository.Open(t.TempDir())
if err != nil {
t.Fatalf("打开测试库失败: %v", err)
}
t.Cleanup(func() { db.Close() })
if err := repository.Migrate(db); err != nil {
t.Fatalf("迁移失败: %v", err)
}
return db
return newTestDB(t)
}
func TestRunSybSync_已有的ShopeeSKUID同步后仍在(t *testing.T) {