2026-06-29 14:26:06 +08:00

131 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package shared
import (
"fmt"
"strings"
)
type UserIdentityFilter struct {
UserID string
DisplayUserID string
Username string
}
func (filter UserIdentityFilter) IsEmpty() bool {
return strings.TrimSpace(filter.UserID) == "" &&
strings.TrimSpace(filter.DisplayUserID) == "" &&
strings.TrimSpace(filter.Username) == ""
}
func UserIdentityKeywordSQL(userAlias string, userIDExpr string, keyword string, nowMs int64) (string, []any) {
keyword = strings.TrimSpace(keyword)
if keyword == "" {
return "1 = 1", nil
}
like := "%" + keyword + "%"
// 后台表格里的用户搜索统一覆盖长 user_id、当前展示号、默认短号、昵称和 active 靓号租约。
// 靓号不能只依赖 users.current_display_user_id历史修复、本地导入或过期恢复可能让 users 快照和租约审计表短暂不一致。
return fmt.Sprintf(`(CAST(%[2]s AS CHAR) LIKE ?
OR %[1]s.current_display_user_id LIKE ?
OR %[1]s.default_display_user_id LIKE ?
OR %[1]s.username LIKE ?
OR %[3]s)`, userAlias, userIDExpr, ActivePrettyDisplayIDSQL(userAlias, "LIKE ?")), []any{
like,
like,
like,
like,
nowMs,
like,
}
}
func UserIdentityFieldsSQL(userAlias string, userIDExpr string, filter UserIdentityFilter, nowMs int64) (string, []any) {
clauses := make([]string, 0, 3)
args := make([]any, 0, 8)
if userID := strings.TrimSpace(filter.UserID); userID != "" {
clauses = append(clauses, fmt.Sprintf("CAST(%s AS CHAR) = ?", userIDExpr))
args = append(args, userID)
}
if displayUserID := strings.TrimSpace(filter.DisplayUserID); displayUserID != "" {
displaySQL, displayArgs := UserDisplayIDSQL(userAlias, displayUserID, nowMs)
clauses = append(clauses, displaySQL)
args = append(args, displayArgs...)
}
if username := strings.TrimSpace(filter.Username); username != "" {
clauses = append(clauses, userAlias+".username LIKE ?")
args = append(args, "%"+username+"%")
}
if len(clauses) == 0 {
return "1 = 1", nil
}
// 字段拆分后每个输入都是一个明确约束;同时输入多个字段时取交集,防止短 ID 和昵称互相扩大结果集。
return "(" + strings.Join(clauses, " AND ") + ")", args
}
func UserIdentityExactSQL(userAlias string, userIDExpr string, keyword string, nowMs int64) (string, []any) {
keyword = strings.TrimSpace(keyword)
if keyword == "" {
return "1 = 1", nil
}
like := "%" + keyword + "%"
// 单用户解析先做精确 user_id/短号/靓号命中,再允许昵称模糊兜底,避免运营输入靓号时被当成普通昵称查询。
return fmt.Sprintf(`(CAST(%[2]s AS CHAR) = ?
OR %[1]s.current_display_user_id = ?
OR %[1]s.default_display_user_id = ?
OR %[3]s
OR %[1]s.username LIKE ?)`, userAlias, userIDExpr, ActivePrettyDisplayIDSQL(userAlias, "= ?")), []any{
keyword,
keyword,
keyword,
nowMs,
keyword,
like,
}
}
func UserIdentityExactOrderSQL(userAlias string, userIDExpr string, keyword string, nowMs int64) (string, []any) {
keyword = strings.TrimSpace(keyword)
return fmt.Sprintf(`CASE
WHEN CAST(%[2]s AS CHAR) = ? THEN 0
WHEN %[1]s.current_display_user_id = ? THEN 1
WHEN %[1]s.default_display_user_id = ? THEN 2
WHEN %[3]s THEN 3
ELSE 4
END`, userAlias, userIDExpr, ActivePrettyDisplayIDSQL(userAlias, "= ?")), []any{
keyword,
keyword,
keyword,
nowMs,
keyword,
}
}
func UserDisplayIDSQL(userAlias string, displayUserID string, nowMs int64) (string, []any) {
displayUserID = strings.TrimSpace(displayUserID)
if displayUserID == "" {
return "1 = 1", nil
}
// 精确短号筛选同样覆盖默认短号、当前展示号和 active 靓号,避免换靓号后只能按其中一种号码查到记录。
return fmt.Sprintf(`(%[1]s.current_display_user_id = ?
OR %[1]s.default_display_user_id = ?
OR %[2]s)`, userAlias, ActivePrettyDisplayIDSQL(userAlias, "= ?")), []any{
displayUserID,
displayUserID,
nowMs,
displayUserID,
}
}
func ActivePrettyDisplayIDSQL(userAlias string, displayPredicate string) string {
return fmt.Sprintf(`EXISTS (
SELECT 1
FROM pretty_display_user_id_leases lease
WHERE lease.app_code = %[1]s.app_code
AND lease.user_id = %[1]s.user_id
AND lease.status = 'active'
AND (COALESCE(lease.expires_at_ms, 0) = 0 OR lease.expires_at_ms > ?)
AND lease.display_user_id %[2]s
LIMIT 1
)`, userAlias, displayPredicate)
}