fix(query): 支持12位纯数字时间编号(如202609040184)商品编号的精准索引路由
This commit is contained in:
@@ -311,9 +311,9 @@ func (r *Repository) adminConversationBase(ctx context.Context, principal Princi
|
|||||||
db = db.Joins("LEFT JOIN users AS renter ON renter.id = explicit_lo.renter_id").
|
db = db.Joins("LEFT JOIN users AS renter ON renter.id = explicit_lo.renter_id").
|
||||||
Joins("LEFT JOIN users AS owner ON owner.id = COALESCE(explicit_lo.owner_id, l.owner_id)").
|
Joins("LEFT JOIN users AS owner ON owner.id = COALESCE(explicit_lo.owner_id, l.owner_id)").
|
||||||
Where("(renter.phone = ? OR owner.phone = ?)", keyword, keyword)
|
Where("(renter.phone = ? OR owner.phone = ?)", keyword, keyword)
|
||||||
} else if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") || (len(keyword) >= 12 && isAllDigits(keyword)) {
|
} else if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") {
|
||||||
db = db.Where("(explicit_lo.order_no = ? OR c.latest_order_no = ?)", keyword, keyword)
|
db = db.Where("(explicit_lo.order_no = ? OR c.latest_order_no = ?)", keyword, keyword)
|
||||||
} else if strings.HasPrefix(upper, "L") {
|
} else if strings.HasPrefix(upper, "L") || (len(keyword) >= 12 && isAllDigits(keyword)) {
|
||||||
db = db.Where("l.listing_no = ?", keyword)
|
db = db.Where("l.listing_no = ?", keyword)
|
||||||
} else {
|
} else {
|
||||||
db = db.Joins("LEFT JOIN users AS renter ON renter.id = explicit_lo.renter_id").
|
db = db.Joins("LEFT JOIN users AS renter ON renter.id = explicit_lo.renter_id").
|
||||||
|
|||||||
@@ -125,11 +125,11 @@ func (r *Repository) adminDisputeCountQuery(ctx context.Context, query AdminList
|
|||||||
return r.db.WithContext(ctx).Table("disputes AS d").
|
return r.db.WithContext(ctx).Table("disputes AS d").
|
||||||
Joins("JOIN rental_orders AS o ON o.id = d.order_id")
|
Joins("JOIN rental_orders AS o ON o.id = d.order_id")
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") || (len(kw) >= 12 && isAllDigits(kw)) {
|
if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") {
|
||||||
return r.db.WithContext(ctx).Table("disputes AS d").
|
return r.db.WithContext(ctx).Table("disputes AS d").
|
||||||
Joins("JOIN rental_orders AS o ON o.id = d.order_id")
|
Joins("JOIN rental_orders AS o ON o.id = d.order_id")
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(upper, "L") {
|
if strings.HasPrefix(upper, "L") || (len(kw) >= 12 && isAllDigits(kw)) {
|
||||||
return r.db.WithContext(ctx).Table("disputes AS d").
|
return r.db.WithContext(ctx).Table("disputes AS d").
|
||||||
Joins("JOIN rental_orders AS o ON o.id = d.order_id").
|
Joins("JOIN rental_orders AS o ON o.id = d.order_id").
|
||||||
Joins("JOIN rental_listings AS l ON l.id = o.listing_id")
|
Joins("JOIN rental_listings AS l ON l.id = o.listing_id")
|
||||||
@@ -155,11 +155,11 @@ func applyAdminListFilters(db *gorm.DB, query AdminListQuery) *gorm.DB {
|
|||||||
if len(keyword) == 11 && keyword[0] == '1' && isAllDigits(keyword) {
|
if len(keyword) == 11 && keyword[0] == '1' && isAllDigits(keyword) {
|
||||||
// 11 位手机号:命中 users(phone) 唯一索引
|
// 11 位手机号:命中 users(phone) 唯一索引
|
||||||
db = db.Where("(owner.phone = ? OR renter.phone = ?)", keyword, keyword)
|
db = db.Where("(owner.phone = ? OR renter.phone = ?)", keyword, keyword)
|
||||||
} else if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") || (len(keyword) >= 12 && isAllDigits(keyword)) {
|
} else if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") {
|
||||||
// 订单号:命中 rental_orders(order_no) 唯一索引
|
// 订单号:命中 rental_orders(order_no) 唯一索引
|
||||||
db = db.Where("o.order_no = ?", keyword)
|
db = db.Where("o.order_no = ?", keyword)
|
||||||
} else if strings.HasPrefix(upper, "L") {
|
} else if strings.HasPrefix(upper, "L") || (len(keyword) >= 12 && isAllDigits(keyword)) {
|
||||||
// 商品编号:命中 rental_listings(listing_no) 唯一索引
|
// 商品编号(如 L前缀或时间计算生成的 12位纯数字如 202609040184):命中 rental_listings(listing_no) 唯一索引
|
||||||
db = db.Where("l.listing_no = ?", keyword)
|
db = db.Where("l.listing_no = ?", keyword)
|
||||||
} else if id, err := strconv.ParseUint(keyword, 10, 64); err == nil && id > 0 && keyword[0] != '0' && id < 100000000 {
|
} else if id, err := strconv.ParseUint(keyword, 10, 64); err == nil && id > 0 && keyword[0] != '0' && id < 100000000 {
|
||||||
// 短纯数字 ID(非零开头):命中纠纷 ID、订单 ID 或关联 ID
|
// 短纯数字 ID(非零开头):命中纠纷 ID、订单 ID 或关联 ID
|
||||||
|
|||||||
@@ -736,9 +736,9 @@ func TestApplyAdminListFiltersSmartRouting(t *testing.T) {
|
|||||||
wantSQLFrag: "d.id = ? OR d.order_id = ? OR o.listing_id = ? OR o.owner_id = ? OR o.renter_id = ?",
|
wantSQLFrag: "d.id = ? OR d.order_id = ? OR o.listing_id = ? OR o.owner_id = ? OR o.renter_id = ?",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "长纯数字路由至订单号",
|
name: "12位时间计算的纯数字商品编号路由至rental_listings(listing_no)",
|
||||||
query: AdminListQuery{Keyword: "2026090510001234"},
|
query: AdminListQuery{Keyword: "202609040184"},
|
||||||
wantSQLFrag: "o.order_no = ?",
|
wantSQLFrag: "l.listing_no = ?",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "普通文字退回多列LIKE模糊匹配",
|
name: "普通文字退回多列LIKE模糊匹配",
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
package listing
|
package listing
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
func TestFilterAdminListingsByAssetLevels(t *testing.T) {
|
func TestFilterAdminListingsByAssetLevels(t *testing.T) {
|
||||||
items := []ListingDTO{
|
items := []ListingDTO{
|
||||||
@@ -92,3 +98,49 @@ func TestFilterAdminListingsBySkinGroupOnly(t *testing.T) {
|
|||||||
t.Fatalf("expected only listing 2 by gold skin group, got %#v", filtered)
|
t.Fatalf("expected only listing 2 by gold skin group, got %#v", filtered)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApplyAdminListFiltersSmartRouting(t *testing.T) {
|
||||||
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{DryRun: true})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("打开测试数据库失败: %v", err)
|
||||||
|
}
|
||||||
|
repo := &Repository{}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
query AdminListQuery
|
||||||
|
wantSQLFrag string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "12位时间计算的纯数字商品编号路由至rental_listings(listing_no)",
|
||||||
|
query: AdminListQuery{Keyword: "202609040184"},
|
||||||
|
wantSQLFrag: "l.listing_no = ?",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "L前缀商品编号路由至rental_listings(listing_no)",
|
||||||
|
query: AdminListQuery{Keyword: "L20260905100"},
|
||||||
|
wantSQLFrag: "l.listing_no = ?",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RO前缀订单号路由至EXISTS订单唯一索引",
|
||||||
|
query: AdminListQuery{Keyword: "RO202609051000"},
|
||||||
|
wantSQLFrag: "o.order_no = ?",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "短数字ID路由至主键及关联ID",
|
||||||
|
query: AdminListQuery{Keyword: "12345"},
|
||||||
|
wantSQLFrag: "l.listing_no = ? OR EXISTS (SELECT 1 FROM rental_orders AS o WHERE o.listing_id = l.id AND o.order_no = ?) OR l.id = ?",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
stmt := repo.applyAdminListFilters(db.Table("rental_listings AS l"), tt.query).Find(&[]struct{}{}).Statement
|
||||||
|
sql := stmt.SQL.String()
|
||||||
|
if !strings.Contains(sql, tt.wantSQLFrag) {
|
||||||
|
t.Fatalf("期望 SQL 包含 %q, 实际生成: %s", tt.wantSQLFrag, sql)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ func (r *Repository) applyAdminListFilters(db *gorm.DB, query AdminListQuery) *g
|
|||||||
if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") {
|
if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") {
|
||||||
// 订单号:命中 rental_orders(order_no) 唯一索引
|
// 订单号:命中 rental_orders(order_no) 唯一索引
|
||||||
db = db.Where("EXISTS (SELECT 1 FROM rental_orders AS o WHERE o.listing_id = l.id AND o.order_no = ?)", keyword)
|
db = db.Where("EXISTS (SELECT 1 FROM rental_orders AS o WHERE o.listing_id = l.id AND o.order_no = ?)", keyword)
|
||||||
} else if strings.HasPrefix(upper, "L") {
|
} else if strings.HasPrefix(upper, "L") || (len(keyword) >= 12 && isAllDigits(keyword)) {
|
||||||
// 商品编号:命中 rental_listings(listing_no) 唯一索引
|
// 商品编号(如 L前缀或时间计算生成的 12位纯数字如 202609040184):命中 rental_listings(listing_no) 唯一索引
|
||||||
db = db.Where("l.listing_no = ?", keyword)
|
db = db.Where("l.listing_no = ?", keyword)
|
||||||
} else if id, err := strconv.ParseUint(keyword, 10, 64); err == nil && id > 0 && keyword[0] != '0' && id < 100000000 {
|
} else if id, err := strconv.ParseUint(keyword, 10, 64); err == nil && id > 0 && keyword[0] != '0' && id < 100000000 {
|
||||||
db = db.Where(
|
db = db.Where(
|
||||||
@@ -277,3 +277,16 @@ func (r *Repository) nextListingNo(tx *gorm.DB, now time.Time) (string, error) {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("%s%04d", bizDate, seq), nil
|
return fmt.Sprintf("%s%04d", bizDate, seq), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isAllDigits(s string) bool {
|
||||||
|
if s == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] < '0' || s[i] > '9' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ func (r *Repository) adminOrderCountQuery(ctx context.Context, query AdminOrderQ
|
|||||||
upper := strings.ToUpper(kw)
|
upper := strings.ToUpper(kw)
|
||||||
needsJoin := false
|
needsJoin := false
|
||||||
if kw != "" {
|
if kw != "" {
|
||||||
if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") || (len(kw) >= 12 && isAllDigits(kw)) {
|
if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") {
|
||||||
needsJoin = false
|
needsJoin = false
|
||||||
} else if id, err := strconv.ParseUint(kw, 10, 64); err == nil && id > 0 && kw[0] != '0' && len(kw) < 9 {
|
} else if id, err := strconv.ParseUint(kw, 10, 64); err == nil && id > 0 && kw[0] != '0' && len(kw) < 9 {
|
||||||
needsJoin = false
|
needsJoin = false
|
||||||
@@ -170,11 +170,11 @@ func applyAdminOrderFilters(db *gorm.DB, query AdminOrderQuery) *gorm.DB {
|
|||||||
if len(keyword) == 11 && keyword[0] == '1' && isAllDigits(keyword) {
|
if len(keyword) == 11 && keyword[0] == '1' && isAllDigits(keyword) {
|
||||||
// 11 位手机号:命中 users(phone) 唯一索引
|
// 11 位手机号:命中 users(phone) 唯一索引
|
||||||
db = db.Where("(owner.phone = ? OR renter.phone = ?)", keyword, keyword)
|
db = db.Where("(owner.phone = ? OR renter.phone = ?)", keyword, keyword)
|
||||||
} else if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") || (len(keyword) >= 12 && isAllDigits(keyword)) {
|
} else if strings.HasPrefix(upper, "RO") || strings.HasPrefix(upper, "ORD") {
|
||||||
// 订单号:命中 rental_orders(order_no) 唯一索引
|
// 订单号:命中 rental_orders(order_no) 唯一索引
|
||||||
db = db.Where("o.order_no = ?", keyword)
|
db = db.Where("o.order_no = ?", keyword)
|
||||||
} else if strings.HasPrefix(upper, "L") {
|
} else if strings.HasPrefix(upper, "L") || (len(keyword) >= 12 && isAllDigits(keyword)) {
|
||||||
// 商品编号:命中 rental_listings(listing_no) 唯一索引
|
// 商品编号(如 L前缀或时间计算生成的 12位纯数字如 202609040184):命中 rental_listings(listing_no) 唯一索引
|
||||||
db = db.Where("l.listing_no = ?", keyword)
|
db = db.Where("l.listing_no = ?", keyword)
|
||||||
} else if id, err := strconv.ParseUint(keyword, 10, 64); err == nil && id > 0 && keyword[0] != '0' && id < 100000000 {
|
} else if id, err := strconv.ParseUint(keyword, 10, 64); err == nil && id > 0 && keyword[0] != '0' && id < 100000000 {
|
||||||
// 短纯数字 ID(非零开头):命中 rental_orders(id) 主键及关联 ID
|
// 短纯数字 ID(非零开头):命中 rental_orders(id) 主键及关联 ID
|
||||||
|
|||||||
@@ -496,9 +496,9 @@ func TestApplyAdminOrderFiltersSmartRouting(t *testing.T) {
|
|||||||
wantSQLFrag: "o.order_no = ?",
|
wantSQLFrag: "o.order_no = ?",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "长纯数字订单号路由至rental_orders(order_no)",
|
name: "12位时间计算的纯数字商品编号路由至rental_listings(listing_no)",
|
||||||
query: AdminOrderQuery{Keyword: "2026090510001234"},
|
query: AdminOrderQuery{Keyword: "202609040184"},
|
||||||
wantSQLFrag: "o.order_no = ?",
|
wantSQLFrag: "l.listing_no = ?",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "L前缀商品编号路由至rental_listings(listing_no)",
|
name: "L前缀商品编号路由至rental_listings(listing_no)",
|
||||||
|
|||||||
@@ -743,15 +743,12 @@ func applyAdminPickupFilters(db *gorm.DB, query AdminPickupQuery) *gorm.DB {
|
|||||||
} else if strings.HasPrefix(upper, "PK") || strings.HasPrefix(upper, "P") {
|
} else if strings.HasPrefix(upper, "PK") || strings.HasPrefix(upper, "P") {
|
||||||
// 提号编号:命中 admin_pickups(pickup_no) 唯一索引
|
// 提号编号:命中 admin_pickups(pickup_no) 唯一索引
|
||||||
db = db.Where("p.pickup_no = ?", kw)
|
db = db.Where("p.pickup_no = ?", kw)
|
||||||
} else if strings.HasPrefix(upper, "L") {
|
} else if strings.HasPrefix(upper, "L") || (len(kw) >= 12 && isAllDigits(kw)) {
|
||||||
// 商品编号:命中 rental_listings(listing_no) 唯一索引
|
// 商品编号(如 L前缀或时间计算生成的 12位纯数字如 202609040184):命中 rental_listings(listing_no) 唯一索引
|
||||||
db = db.Where("l.listing_no = ?", kw)
|
db = db.Where("l.listing_no = ?", kw)
|
||||||
} else if id, err := strconv.ParseUint(kw, 10, 64); err == nil && id > 0 && kw[0] != '0' && id < 100000000 {
|
} else if id, err := strconv.ParseUint(kw, 10, 64); err == nil && id > 0 && kw[0] != '0' && id < 100000000 {
|
||||||
// 短纯数字 ID(非零开头):命中 admin_pickups 主键及关联 ID
|
// 短纯数字 ID(非零开头):命中 admin_pickups 主键及关联 ID
|
||||||
db = db.Where("(p.id = ? OR p.listing_id = ? OR p.account_id = ? OR p.owner_id = ?)", id, id, id, id)
|
db = db.Where("(p.id = ? OR p.listing_id = ? OR p.account_id = ? OR p.owner_id = ?)", id, id, id, id)
|
||||||
} else if len(kw) >= 12 && isAllDigits(kw) {
|
|
||||||
// 12位以上纯数字可能为商品编号或提号编号
|
|
||||||
db = db.Where("(p.pickup_no = ? OR l.listing_no = ?)", kw, kw)
|
|
||||||
} else {
|
} else {
|
||||||
like := "%" + kw + "%"
|
like := "%" + kw + "%"
|
||||||
db = db.Where(
|
db = db.Where(
|
||||||
@@ -782,7 +779,7 @@ func (r *Repository) adminPickupCountQuery(ctx context.Context, query AdminPicku
|
|||||||
return r.db.WithContext(ctx).Table("admin_pickups AS p").
|
return r.db.WithContext(ctx).Table("admin_pickups AS p").
|
||||||
Joins("JOIN users AS owner ON owner.id = p.owner_id")
|
Joins("JOIN users AS owner ON owner.id = p.owner_id")
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(upper, "L") {
|
if strings.HasPrefix(upper, "L") || (len(kw) >= 12 && isAllDigits(kw)) {
|
||||||
return r.db.WithContext(ctx).Table("admin_pickups AS p").
|
return r.db.WithContext(ctx).Table("admin_pickups AS p").
|
||||||
Joins("JOIN rental_listings AS l ON l.id = p.listing_id")
|
Joins("JOIN rental_listings AS l ON l.id = p.listing_id")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -736,9 +736,9 @@ func TestApplyAdminPickupFiltersSmartRouting(t *testing.T) {
|
|||||||
wantSQLFrag: "p.id = ? OR p.listing_id = ? OR p.account_id = ? OR p.owner_id = ?",
|
wantSQLFrag: "p.id = ? OR p.listing_id = ? OR p.account_id = ? OR p.owner_id = ?",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "长纯数字路由至提号编号或商品编号",
|
name: "12位时间计算的纯数字商品编号路由至rental_listings(listing_no)",
|
||||||
query: AdminPickupQuery{Keyword: "2026090510001234"},
|
query: AdminPickupQuery{Keyword: "202609040184"},
|
||||||
wantSQLFrag: "p.pickup_no = ? OR l.listing_no = ?",
|
wantSQLFrag: "l.listing_no = ?",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "普通文字退回多列LIKE模糊匹配",
|
name: "普通文字退回多列LIKE模糊匹配",
|
||||||
|
|||||||
Reference in New Issue
Block a user