118 lines
3.8 KiB
Go
118 lines
3.8 KiB
Go
package user
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
|
|
"hyapp/pkg/appcode"
|
|
"hyapp/pkg/xerr"
|
|
userdomain "hyapp/services/user-service/internal/domain/user"
|
|
userservice "hyapp/services/user-service/internal/service/user"
|
|
)
|
|
|
|
// SetUserStatus 原子更新用户主状态,并在非 active 状态下吊销该用户所有 active refresh session。
|
|
func (r *Repository) SetUserStatus(ctx context.Context, command userservice.UserStatusCommand) (userservice.UserStatusPersistenceResult, error) {
|
|
if r == nil || r.db == nil {
|
|
return userservice.UserStatusPersistenceResult{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
|
|
}
|
|
appCode := appcode.Normalize(command.AppCode)
|
|
if appCode == "" {
|
|
appCode = appcode.FromContext(ctx)
|
|
}
|
|
|
|
tx, err := r.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return userservice.UserStatusPersistenceResult{}, err
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
var oldStatus string
|
|
if err := tx.QueryRowContext(ctx, `
|
|
SELECT status
|
|
FROM users
|
|
WHERE app_code = ? AND user_id = ?
|
|
FOR UPDATE
|
|
`, appCode, command.TargetUserID).Scan(&oldStatus); err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return userservice.UserStatusPersistenceResult{}, xerr.New(xerr.NotFound, "user not found")
|
|
}
|
|
return userservice.UserStatusPersistenceResult{}, err
|
|
}
|
|
|
|
_, err = tx.ExecContext(ctx, `
|
|
UPDATE users
|
|
SET status = ?, updated_at_ms = ?
|
|
WHERE app_code = ? AND user_id = ?
|
|
`, string(command.Status), command.NowMs, appCode, command.TargetUserID)
|
|
if err != nil {
|
|
return userservice.UserStatusPersistenceResult{}, err
|
|
}
|
|
if _, err := tx.ExecContext(ctx, `
|
|
INSERT INTO user_status_change_logs (
|
|
app_code, target_user_id, old_status, new_status, operator_type, operator_user_id, reason, request_id, created_at_ms
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`, appCode, command.TargetUserID, oldStatus, string(command.Status), command.OperatorType, command.OperatorUserID, command.Reason, command.RequestID, command.NowMs); err != nil {
|
|
return userservice.UserStatusPersistenceResult{}, err
|
|
}
|
|
|
|
sessionIDs := make([]string, 0)
|
|
if command.Status != userdomain.StatusActive {
|
|
rows, err := tx.QueryContext(ctx, `
|
|
SELECT session_id
|
|
FROM auth_sessions
|
|
WHERE app_code = ? AND user_id = ? AND expires_at_ms > ?
|
|
FOR UPDATE
|
|
`, appCode, command.TargetUserID, command.NowMs)
|
|
if err != nil {
|
|
return userservice.UserStatusPersistenceResult{}, err
|
|
}
|
|
for rows.Next() {
|
|
var sessionID string
|
|
if err := rows.Scan(&sessionID); err != nil {
|
|
_ = rows.Close()
|
|
return userservice.UserStatusPersistenceResult{}, err
|
|
}
|
|
sessionIDs = append(sessionIDs, sessionID)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return userservice.UserStatusPersistenceResult{}, err
|
|
}
|
|
if _, err := tx.ExecContext(ctx, `
|
|
UPDATE auth_sessions
|
|
SET revoked_at_ms = ?, revoked_reason = ?, revoked_request_id = ?, revoked_by = ?, updated_at_ms = ?
|
|
WHERE app_code = ? AND user_id = ? AND revoked_at_ms IS NULL
|
|
`, command.NowMs, userStatusRevokeReason(command.Status), command.RequestID, revokedBy(command.OperatorUserID), command.NowMs, appCode, command.TargetUserID); err != nil {
|
|
return userservice.UserStatusPersistenceResult{}, err
|
|
}
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return userservice.UserStatusPersistenceResult{}, err
|
|
}
|
|
|
|
user, err := r.GetUser(appcode.WithContext(ctx, appCode), command.TargetUserID)
|
|
if err != nil {
|
|
return userservice.UserStatusPersistenceResult{}, err
|
|
}
|
|
return userservice.UserStatusPersistenceResult{User: user, RevokedSessionIDs: sessionIDs}, nil
|
|
}
|
|
|
|
func userStatusRevokeReason(status userdomain.Status) string {
|
|
switch status {
|
|
case userdomain.StatusBanned:
|
|
return "USER_BANNED"
|
|
case userdomain.StatusDisabled:
|
|
return "USER_DISABLED"
|
|
default:
|
|
return "USER_STATUS_CHANGED"
|
|
}
|
|
}
|
|
|
|
func revokedBy(operatorUserID int64) string {
|
|
if operatorUserID > 0 {
|
|
return "user_status_operator"
|
|
}
|
|
return "user_status"
|
|
}
|