package identity import ( "context" "database/sql" "hyapp/pkg/appcode" "hyapp/pkg/xerr" userdomain "hyapp/services/user-service/internal/domain/user" ) func (r *Repository) assertDisplayUserIDAvailable(ctx context.Context, tx *sql.Tx, displayUserID string, userID int64, allowSameOwner bool) error { // FOR UPDATE 锁住目标短号记录,避免并发抢占同一个 display_user_id。 var existingUserID int64 err := tx.QueryRowContext(ctx, ` SELECT user_id FROM user_display_user_ids WHERE app_code = ? AND display_user_id = ? AND status IN (?, ?, ?, ?) FOR UPDATE `, appcode.FromContext(ctx), displayUserID, string(userdomain.DisplayUserIDStatusActive), string(userdomain.DisplayUserIDStatusHeld), string(userdomain.DisplayUserIDStatusReserved), string(userdomain.DisplayUserIDStatusBlocked)).Scan(&existingUserID) if err == sql.ErrNoRows { // 没有 active/held/reserved/blocked 记录时可用。 return nil } if err != nil { return err } if allowSameOwner && existingUserID == userID { // 默认短号修改允许同用户幂等检查通过。 return nil } return xerr.New(xerr.DisplayUserIDExists, "display_user_id already exists") } func (r *Repository) assertChangeCooldown(ctx context.Context, tx *sql.Tx, command userdomain.DisplayUserIDChangeCommand) error { // 冷却期只统计默认短号修改,不统计靓号 apply/expire。 var lastChangedAt sql.Null[int64] err := tx.QueryRowContext(ctx, ` SELECT MAX(created_at_ms) FROM display_user_id_change_logs WHERE app_code = ? AND user_id = ? AND change_type = ? `, appcode.Normalize(command.AppCode), command.UserID, "default_change").Scan(&lastChangedAt) if err != nil { return err } if lastChangedAt.Valid && command.ChangedAtMs-lastChangedAt.V < command.CooldownMs { // 仍在冷却期内时拒绝修改默认短号。 return xerr.New(xerr.DisplayUserIDCooldown, "display_user_id change is cooling down") } return nil }