perf(query): 全面治理后台跨表模糊查询慢SQL并升级SQL门禁守护

- 解决 MySQL 8.4 无法跨表 Index Merge 导致的索引失效与全表扫描问题
- 订单/提号/纠纷/用户/商品/会话列表实现智能意图路由分流直达单表唯一索引
- 剥离 COUNT(*) 阶段无关多表关联及标量子查询,实现首页条数快速短路
- check-sql-guard.sh 增加 forbid_pattern 拦截器与核心路由强校验防线
This commit is contained in:
yml2213
2026-09-05 10:37:04 +08:00
parent ba5aa48c8f
commit a9125b1939
10 changed files with 535 additions and 89 deletions
@@ -44,11 +44,6 @@ func (r *Repository) List(ctx context.Context, page, pageSize int, query ListQue
if err != nil {
return nil, err
}
var total int64
countTx := applyUserFilter(r.db.WithContext(ctx).Table("users AS u"), query, growthConfig)
if err := countTx.Count(&total).Error; err != nil {
return nil, err
}
offset := (page - 1) * pageSize
var rows []userRow
listTx := r.db.WithContext(ctx).Table("users AS u").
@@ -72,6 +67,18 @@ func (r *Repository) List(ctx context.Context, page, pageSize int, query ListQue
if err != nil {
return nil, err
}
var total int64
// 首页查询且返回条数小于 pageSize 时,总数等于当前返回条数,彻底免除第二次 COUNT(*) 扫描
if page == 1 && len(rows) < pageSize {
total = int64(len(rows))
} else {
countTx := applyUserFilter(r.db.WithContext(ctx).Table("users AS u"), query, growthConfig)
if err := countTx.Count(&total).Error; err != nil {
return nil, err
}
}
items := make([]UserDTO, 0, len(rows))
for _, row := range rows {
items = append(items, row.toDTO(growthConfig))
@@ -92,11 +99,15 @@ func applyUserFilter(tx *gorm.DB, query ListQuery, growthConfig rentergrowth.Con
}
}
if keyword := strings.TrimSpace(query.Keyword); keyword != "" {
like := "%" + keyword + "%"
// 关键词为纯数字时一并按用户 ID 精确匹配
if id, err := strconv.ParseUint(keyword, 10, 64); err == nil {
if len(keyword) == 11 && keyword[0] == '1' && isAllDigits(keyword) {
// 11 位手机号:命中 users(phone) 唯一索引
tx = tx.Where("u.phone = ?", keyword)
} else if id, err := strconv.ParseUint(keyword, 10, 64); err == nil && id > 0 && keyword[0] != '0' && id < 100000000 {
// 纯数字 ID:一并按用户 ID 精确匹配
like := "%" + keyword + "%"
tx = tx.Where("u.id = ? OR u.phone LIKE ? OR u.nickname LIKE ?", id, like, like)
} else {
like := "%" + keyword + "%"
tx = tx.Where("u.phone LIKE ? OR u.nickname LIKE ?", like, like)
}
}
@@ -568,3 +579,16 @@ func appendAuditLog(tx *gorm.DB, actorID uint64, action string, bizID uint64, me
func IsNotFound(err error) bool {
return errors.Is(err, gorm.ErrRecordNotFound)
}
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
}