feat: 顺运宝支持指定日期同步 (#53)

This commit is contained in:
chengma
2026-08-09 13:24:02 +08:00
parent 24b6c742c1
commit a18d4e67ae
10 changed files with 435 additions and 29 deletions
+88
View File
@@ -84,6 +84,39 @@ func TestSyncDateRange_结束日期按顺运宝服务端时区UTC加8算不是UT
}
}
func TestNewSybSyncOptions_指定日期校验(t *testing.T) {
now := time.Date(2026, 8, 9, 23, 0, 0, 0, time.UTC) // UTC+8 已是 8 月 10 日
cases := []struct {
name, from, to string
wantErr string
}{
{name: "两端都空是自动增量"},
{name: "合法闭区间", from: "2026-08-01", to: "2026-08-10"},
{name: "只填开始", from: "2026-08-01", wantErr: "同时填写"},
{name: "开始格式错误", from: "2026/08/01", to: "2026-08-02", wantErr: "开始日期格式"},
{name: "结束格式错误", from: "2026-08-01", to: "2026/08/02", wantErr: "结束日期格式"},
{name: "起止倒置", from: "2026-08-03", to: "2026-08-02", wantErr: "不能晚于"},
{name: "晚于UTC加8今天", from: "2026-08-10", to: "2026-08-11", wantErr: "不能晚于"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, err := NewSybSyncOptions(tc.from, tc.to, now)
if tc.wantErr == "" {
if err != nil {
t.Fatalf("不应该报错: %v", err)
}
if got.From != tc.from || got.To != tc.to {
t.Fatalf("范围被意外改变:%+v", got)
}
return
}
if err == nil || !strings.Contains(err.Error(), tc.wantErr) {
t.Fatalf("错误应包含 %q,实际 %v", tc.wantErr, err)
}
})
}
}
// ── 金额换算 ──────────────────────────────────────────
func TestRoundYuanToCent_先四舍五入再转整数(t *testing.T) {
@@ -367,6 +400,61 @@ func TestRunSybSync_全部成功后更新last_synced_at(t *testing.T) {
}
}
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-20T06:30:00Z"
if err := repository.SetSybLastSyncedAt(db, original); err != nil {
t.Fatalf("准备同步游标失败: %v", err)
}
report := RunSybSyncWithOptions(context.Background(), db, client,
config.SybConfig{BaseURL: srv.URL, PageSize: 20, MaxMatches: 500, SyncFrom: "2026-07-01"},
time.Date(2026, 7, 28, 12, 0, 0, 0, time.UTC),
SybSyncOptions{From: "2026-07-01", To: "2026-07-10"})
if report.Err != nil {
t.Fatalf("历史补拉失败: %v", report.Err)
}
if !report.Specified || !strings.HasPrefix(report.Summary(), "指定日期同步完成") {
t.Fatalf("报告没有标明指定日期同步:%+v / %s", report, report.Summary())
}
at, _, err := repository.GetSybLastSyncedAt(db)
if err != nil {
t.Fatalf("读取同步游标失败: %v", err)
}
if at != original {
t.Fatalf("局部历史补拉不应推进游标,原值 %q,实际 %q", original, at)
}
}
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-20T06:30:00Z" // UTC+8 仍是 7 月 20 日
if err := repository.SetSybLastSyncedAt(db, original); err != nil {
t.Fatalf("准备同步游标失败: %v", err)
}
report := RunSybSyncWithOptions(context.Background(), db, client,
config.SybConfig{BaseURL: srv.URL, PageSize: 20, MaxMatches: 500, SyncFrom: "2026-07-01"},
time.Date(2026, 7, 28, 12, 0, 0, 0, time.UTC),
SybSyncOptions{From: "2026-07-20", To: "2026-07-28"})
if report.Err != nil {
t.Fatalf("完整范围同步失败: %v", report.Err)
}
at, _, err := repository.GetSybLastSyncedAt(db)
if err != nil {
t.Fatalf("读取同步游标失败: %v", err)
}
if at == original || at == "" {
t.Fatalf("完整覆盖自动范围后应该推进游标,实际仍为 %q", at)
}
}
func TestRunSybSync_数量小于等于0被跳过并计入报告(t *testing.T) {
srv := fakeSybServer(t, []fakeStock{
{ID: 1, Code: "A", Details: []fakeDetail{