feat: 统一顺运宝日期同步并记录历史 (#59)

This commit is contained in:
chengma
2026-08-09 18:22:58 +08:00
parent ea7932db63
commit 05a54b044d
18 changed files with 934 additions and 144 deletions
+92 -4
View File
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
@@ -119,6 +120,37 @@ func TestNewSybSyncOptions_指定日期校验(t *testing.T) {
}
}
func TestDefaultSybSyncRange_最近三天与缺口分段(t *testing.T) {
now := time.Date(2026, 8, 9, 12, 0, 0, 0, time.UTC)
t.Run("没有游标默认最近三天", func(t *testing.T) {
db := newSyncTestDB(t)
got, err := DefaultSybSyncRange(db, now)
if err != nil || got.From != "2026-08-07" || got.To != "2026-08-09" || got.Warning != "" {
t.Fatalf("默认范围错误: got=%+v err=%v", got, err)
}
})
t.Run("短缺口自动扩展到今天", func(t *testing.T) {
db := newSyncTestDB(t)
if err := repository.SetSybLastSyncedAt(db, "2026-08-05T00:00:00Z"); err != nil {
t.Fatal(err)
}
got, err := DefaultSybSyncRange(db, now)
if err != nil || got.From != "2026-08-05" || got.To != "2026-08-09" || got.Warning == "" {
t.Fatalf("短缺口范围错误: got=%+v err=%v", got, err)
}
})
t.Run("长缺口优先选择最早三十一天", func(t *testing.T) {
db := newSyncTestDB(t)
if err := repository.SetSybLastSyncedAt(db, "2026-07-01T00:00:00Z"); err != nil {
t.Fatal(err)
}
got, err := DefaultSybSyncRange(db, now)
if err != nil || got.From != "2026-07-01" || got.To != "2026-07-31" || !strings.Contains(got.Warning, "继续同步下一段") {
t.Fatalf("长缺口分段错误: got=%+v err=%v", got, err)
}
})
}
// ── 金额换算 ──────────────────────────────────────────
func TestRoundYuanToCent_先四舍五入再转整数(t *testing.T) {
@@ -489,8 +521,8 @@ func TestRunSybSyncWithOptions_历史补拉不推进游标(t *testing.T) {
if report.Err != nil {
t.Fatalf("历史补拉失败: %v", report.Err)
}
if !report.Specified || !strings.HasPrefix(report.Summary(), "指定日期同步完成") {
t.Fatalf("报告没有标明指定日期同步:%+v / %s", report, report.Summary())
if !report.Specified || !strings.HasPrefix(report.Summary(), "同步完成") {
t.Fatalf("明确日期范围的报告不正确:%+v / %s", report, report.Summary())
}
at, _, err := repository.GetSybLastSyncedAt(db)
if err != nil {
@@ -522,8 +554,64 @@ func TestRunSybSyncWithOptions_完整覆盖自动范围才推进游标(t *testin
if err != nil {
t.Fatalf("读取同步游标失败: %v", err)
}
if at == original || at == "" {
t.Fatalf("完整覆盖自动范围后应该推进游标,实际仍为 %q", at)
if at != "2026-07-27T16:00:00Z" || !report.CursorAdvanced {
t.Fatalf("连续覆盖后应该把游标推进到所选结束日,实际 %q,报告 %+v", at, report)
}
}
func TestRunSybSyncWithOptions_跳过缺口不推进游标(t *testing.T) {
srv := fakeSybServer(t, nil, 0)
defer srv.Close()
db := newSyncTestDB(t)
client, _ := syb.New(srv.URL)
const original = "2026-07-01T00:00:00Z"
if err := repository.SetSybLastSyncedAt(db, original); err != nil {
t.Fatal(err)
}
report := RunSybSyncWithOptions(context.Background(), db, client,
config.SybConfig{BaseURL: srv.URL, PageSize: 20, MaxMatches: 500},
time.Date(2026, 8, 9, 12, 0, 0, 0, time.UTC),
SybSyncOptions{From: "2026-07-05", To: "2026-07-10"})
if report.Err != nil {
t.Fatalf("同步失败: %v", report.Err)
}
at, _, err := repository.GetSybLastSyncedAt(db)
if err != nil || at != original || report.CursorAdvanced {
t.Fatalf("跳过缺口不应推进游标: at=%q report=%+v err=%v", at, report, err)
}
}
func TestSybSyncHistory_服务层创建失败记录并展示(t *testing.T) {
db := newSyncTestDB(t)
actor := model.User{
UserID: "USR-HISTORY", Username: "buyer", PasswordHash: "test-hash",
Role: model.RolePurchaser, Status: model.UserActive,
PasswordChangedAt: "2026-08-09T00:00:00Z", CreatedAt: "2026-08-09T00:00:00Z",
UpdatedAt: "2026-08-09T00:00:00Z",
}
if err := repository.CreateInitialAdmin(db, actor); err != nil {
t.Fatal(err)
}
runID, err := CreateSybSyncRun(db, &actor,
SybSyncOptions{From: "2026-08-07", To: "2026-08-09"},
time.Date(2026, 8, 9, 1, 0, 0, 0, time.UTC))
if err != nil {
t.Fatal(err)
}
if err := FinishSybSyncRun(db, runID, SyncReport{
From: "2026-08-07", To: "2026-08-09", Err: fmt.Errorf("测试失败"),
FinishedAt: time.Date(2026, 8, 9, 1, 1, 0, 0, time.UTC),
}); err != nil {
t.Fatal(err)
}
history, err := ListSybSyncHistory(db, 1)
if err != nil || history.Total != 1 || len(history.Rows) != 1 {
t.Fatalf("读取记录失败: history=%+v err=%v", history, err)
}
row := history.Rows[0]
if row.Username != "buyer" || row.StatusText != "失败" || row.ErrorMessage != "测试失败" || row.CursorText != "未推进" {
t.Fatalf("同步记录视图不正确: %+v", row)
}
}