feat(pickup): 线下提号支持扩展字段店铺管理CRUD及时间筛选
- 线下提号创建表单扩展:增加「操作人」、「第三方单号」、「下单金额」、「押金金额」字段,重构弹窗布局为紧凑双列/三列金额卡片排布,消除过度垂直滚动并默认预填操作人 - 店铺管理 CRUD:新增 pickup_shops 配置表与配套 API,支持在提号页面直接管理店铺(增删改查、启用/禁用、筛选),下拉选项自动优先加载活跃配置店铺并兼顾历史店铺 - 列表时间范围筛选:增加精确到秒的「日期+时间」时间范围选择器,支持快捷时段与后端时间范围过滤 - 生产环境数据核查:只读提取线上全部 61 项历史提号店铺名称与单量分布,完整整理沉淀至实施文档供后续映射规则参考 - 验证方式:执行并通过全套端到端代码门禁(scripts/check.sh:SQL约束、go vet、go build、MySQL真实测试库单元测试及覆盖率、vue大小检查、prettier、eslint、vue-tsc及vite构建)
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"hfb_sys/backend/internal/auditlog"
|
||||
"hfb_sys/backend/internal/database"
|
||||
@@ -621,6 +622,7 @@ func newPickupTestRepo(t *testing.T) (*Repository, *gorm.DB) {
|
||||
&model.ListingUpload{},
|
||||
&model.AdminPickup{},
|
||||
&model.AdminPickupFinancialAdjustment{},
|
||||
&model.PickupShop{},
|
||||
&model.WalletAccount{},
|
||||
&model.WalletLedger{},
|
||||
&model.AuditLog{},
|
||||
@@ -761,3 +763,158 @@ func TestApplyAdminPickupFiltersSmartRouting(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryCreateExtraFieldsAndQueryTimeRange(t *testing.T) {
|
||||
repo, db := newPickupTestRepo(t)
|
||||
listing := seedAvailableListing(t, db, "LST-TEST-EXTRA-01", "测试商品带扩展字段", false)
|
||||
|
||||
created, err := repo.Create(t.Context(), CreateRequest{
|
||||
ListingID: listing.ID,
|
||||
Operator: "运营小张",
|
||||
Platform: "淘宝",
|
||||
ThirdPartyOrderNo: "TB-20261009-8888",
|
||||
ShopName: "测试大锤专营",
|
||||
OrderAmountCent: 12800,
|
||||
DepositAmountCent: 5000,
|
||||
ProfitAmountCent: 3000,
|
||||
Remark: "测试提号备注",
|
||||
}, 1, auditlog.Meta{})
|
||||
if err != nil {
|
||||
t.Fatalf("Create() error = %v", err)
|
||||
}
|
||||
|
||||
if created.Operator != "运营小张" {
|
||||
t.Fatalf("Operator = %q, want %q", created.Operator, "运营小张")
|
||||
}
|
||||
if created.ThirdPartyOrderNo != "TB-20261009-8888" {
|
||||
t.Fatalf("ThirdPartyOrderNo = %q, want %q", created.ThirdPartyOrderNo, "TB-20261009-8888")
|
||||
}
|
||||
assertInt64(t, "OrderAmountCent", created.OrderAmountCent, 12800)
|
||||
assertInt64(t, "DepositAmountCent", created.DepositAmountCent, 5000)
|
||||
|
||||
detail, err := repo.FindByID(t.Context(), created.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("FindByID() error = %v", err)
|
||||
}
|
||||
if detail.Operator != "运营小张" || detail.ThirdPartyOrderNo != "TB-20261009-8888" {
|
||||
t.Fatalf("FindByID fields mismatch: %+v", detail)
|
||||
}
|
||||
|
||||
// 测试时间范围筛选
|
||||
now := time.Now()
|
||||
before1Hour := now.Add(-1 * time.Hour)
|
||||
after1Hour := now.Add(1 * time.Hour)
|
||||
before2Hour := now.Add(-2 * time.Hour)
|
||||
|
||||
// 命中的时间范围
|
||||
hitRes, err := repo.ListAdmin(t.Context(), AdminPickupQuery{
|
||||
StartTime: &before1Hour,
|
||||
EndTime: &after1Hour,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ListAdmin() with time range error = %v", err)
|
||||
}
|
||||
if hitRes.Total < 1 {
|
||||
t.Fatalf("ListAdmin() in-range total = %d, want >= 1", hitRes.Total)
|
||||
}
|
||||
|
||||
// 未命中的时间范围(过去)
|
||||
missRes, err := repo.ListAdmin(t.Context(), AdminPickupQuery{
|
||||
StartTime: &before2Hour,
|
||||
EndTime: &before1Hour,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ListAdmin() with miss range error = %v", err)
|
||||
}
|
||||
if missRes.Total != 0 {
|
||||
t.Fatalf("ListAdmin() out-of-range total = %d, want 0", missRes.Total)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryPickupShopCRUDAndOptions(t *testing.T) {
|
||||
repo, _ := newPickupTestRepo(t)
|
||||
|
||||
// 1. 创建店铺
|
||||
shop1, err := repo.CreateShop(t.Context(), CreatePickupShopRequest{
|
||||
Name: "旗舰一店",
|
||||
Platform: "淘宝",
|
||||
Status: "active",
|
||||
SortOrder: 10,
|
||||
Remark: "测试店铺一",
|
||||
}, 1, auditlog.Meta{})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateShop() error = %v", err)
|
||||
}
|
||||
if shop1.ID == 0 || shop1.Name != "旗舰一店" {
|
||||
t.Fatalf("CreateShop() result = %+v", shop1)
|
||||
}
|
||||
|
||||
// 2. 重名校验
|
||||
_, err = repo.CreateShop(t.Context(), CreatePickupShopRequest{
|
||||
Name: "旗舰一店",
|
||||
}, 1, auditlog.Meta{})
|
||||
if !errors.Is(err, ErrPickupShopNameExists) {
|
||||
t.Fatalf("CreateShop() duplicate error = %v, want ErrPickupShopNameExists", err)
|
||||
}
|
||||
|
||||
// 3. 创建第二个店铺
|
||||
shop2, err := repo.CreateShop(t.Context(), CreatePickupShopRequest{
|
||||
Name: "拼多多二店",
|
||||
Platform: "拼多多",
|
||||
Status: "active",
|
||||
SortOrder: 20,
|
||||
Remark: "测试店铺二",
|
||||
}, 1, auditlog.Meta{})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateShop(2) error = %v", err)
|
||||
}
|
||||
|
||||
// 4. 查询店铺列表
|
||||
shops, err := repo.ListShops(t.Context(), PickupShopQuery{})
|
||||
if err != nil {
|
||||
t.Fatalf("ListShops() error = %v", err)
|
||||
}
|
||||
if len(shops) != 2 {
|
||||
t.Fatalf("ListShops() length = %d, want 2", len(shops))
|
||||
}
|
||||
// 权重 20 的应该排在前面
|
||||
if shops[0].Name != "拼多多二店" {
|
||||
t.Fatalf("ListShops() first = %s, want 拼多多二店", shops[0].Name)
|
||||
}
|
||||
|
||||
// 5. 更新店铺
|
||||
updated, err := repo.UpdateShop(t.Context(), shop1.ID, UpdatePickupShopRequest{
|
||||
Name: "旗舰一店改名",
|
||||
Platform: "淘宝",
|
||||
Status: "disabled",
|
||||
SortOrder: 5,
|
||||
Remark: "修改备注",
|
||||
}, 1, auditlog.Meta{})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateShop() error = %v", err)
|
||||
}
|
||||
if updated.Name != "旗舰一店改名" || updated.Status != "disabled" {
|
||||
t.Fatalf("UpdateShop() result = %+v", updated)
|
||||
}
|
||||
|
||||
// 6. ListShopNames 选项列表(仅返回 active 店铺)
|
||||
options, err := repo.ListShopNames(t.Context(), "")
|
||||
if err != nil {
|
||||
t.Fatalf("ListShopNames() error = %v", err)
|
||||
}
|
||||
if len(options) != 1 || options[0] != "拼多多二店" {
|
||||
t.Fatalf("ListShopNames() options = %v, want [拼多多二店]", options)
|
||||
}
|
||||
|
||||
// 7. 删除店铺
|
||||
if err := repo.DeleteShop(t.Context(), shop2.ID, 1, auditlog.Meta{}); err != nil {
|
||||
t.Fatalf("DeleteShop() error = %v", err)
|
||||
}
|
||||
shopsAfterDelete, err := repo.ListShops(t.Context(), PickupShopQuery{Status: "active"})
|
||||
if err != nil {
|
||||
t.Fatalf("ListShops() after delete error = %v", err)
|
||||
}
|
||||
if len(shopsAfterDelete) != 0 {
|
||||
t.Fatalf("active shops after delete length = %d, want 0", len(shopsAfterDelete))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user