fix(realname): 增加身份证唯一性校验,防止同一证件重复实名
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"hfb_sys/backend/internal/auditlog"
|
||||
"hfb_sys/backend/internal/model"
|
||||
"hfb_sys/backend/internal/modules/invitation"
|
||||
"hfb_sys/backend/internal/modules/realname"
|
||||
"hfb_sys/backend/internal/modules/rentergrowth"
|
||||
"hfb_sys/backend/internal/modules/wallet"
|
||||
"hfb_sys/backend/pkg/crypto"
|
||||
@@ -22,6 +23,7 @@ import (
|
||||
type Repository struct {
|
||||
db *gorm.DB
|
||||
encryptor crypto.Encryptor
|
||||
realnameIDHashKey string
|
||||
statusChangeNotifier func(userID uint64)
|
||||
}
|
||||
|
||||
@@ -35,6 +37,11 @@ func NewRepository(db *gorm.DB, encryptors ...crypto.Encryptor) *Repository {
|
||||
return &Repository{db: db, encryptor: encryptor}
|
||||
}
|
||||
|
||||
// SetRealnameIDHashKey 设置身份证盲索引密钥,后台人工实名同样执行唯一性校验。
|
||||
func (r *Repository) SetRealnameIDHashKey(key string) {
|
||||
r.realnameIDHashKey = strings.TrimSpace(key)
|
||||
}
|
||||
|
||||
// SetStatusChangeNotifier 设置状态变更后的会话撤销通知。
|
||||
func (r *Repository) SetStatusChangeNotifier(notifier func(userID uint64)) {
|
||||
r.statusChangeNotifier = notifier
|
||||
@@ -317,6 +324,7 @@ func (r *Repository) ManualRealname(ctx context.Context, adminID uint64, userID
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
idNoHash := realname.FingerprintIDNo(r.realnameIDHashKey, idNo)
|
||||
|
||||
now := time.Now()
|
||||
record := model.UserRealname{
|
||||
@@ -328,6 +336,7 @@ func (r *Repository) ManualRealname(ctx context.Context, adminID uint64, userID
|
||||
EncryptedName: encryptedName,
|
||||
MaskedIDNo: maskManualIDNo(idNo),
|
||||
EncryptedIDNo: encryptedIDNo,
|
||||
IDNoHash: &idNoHash,
|
||||
VerifiedAt: &now,
|
||||
FailReason: "",
|
||||
}
|
||||
@@ -340,28 +349,50 @@ func (r *Repository) ManualRealname(ctx context.Context, adminID uint64, userID
|
||||
if user.RealnameStatus == "verified" {
|
||||
return ErrRealnameAlreadyVerified
|
||||
}
|
||||
identityRepo := realname.NewRepository(tx, r.encryptor, r.realnameIDHashKey)
|
||||
used, err := identityRepo.ExistsVerifiedIdentity(ctx, userID, idNo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if used {
|
||||
return realname.ErrIDNoAlreadyUsed
|
||||
}
|
||||
|
||||
beforeRealnameStatus := user.RealnameStatus
|
||||
user.RealnameStatus = "verified"
|
||||
if err := tx.Save(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Clauses(clause.OnConflict{
|
||||
Columns: []clause.Column{{Name: "user_id"}},
|
||||
DoUpdates: clause.Assignments(map[string]any{
|
||||
"provider": record.Provider,
|
||||
"provider_order_no": record.ProviderOrderNo,
|
||||
"status": record.Status,
|
||||
"masked_name": record.MaskedName,
|
||||
"encrypted_name": record.EncryptedName,
|
||||
"masked_id_no": record.MaskedIDNo,
|
||||
"encrypted_id_no": record.EncryptedIDNo,
|
||||
"verified_at": record.VerifiedAt,
|
||||
"fail_reason": record.FailReason,
|
||||
"updated_at": now,
|
||||
}),
|
||||
}).Create(&record).Error; err != nil {
|
||||
return err
|
||||
var existingRealname model.UserRealname
|
||||
findErr := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Where("user_id = ?", userID).First(&existingRealname).Error
|
||||
switch {
|
||||
case findErr == nil:
|
||||
existingRealname.Provider = record.Provider
|
||||
existingRealname.ProviderOrderNo = record.ProviderOrderNo
|
||||
existingRealname.Status = record.Status
|
||||
existingRealname.MaskedName = record.MaskedName
|
||||
existingRealname.EncryptedName = record.EncryptedName
|
||||
existingRealname.MaskedIDNo = record.MaskedIDNo
|
||||
existingRealname.EncryptedIDNo = record.EncryptedIDNo
|
||||
existingRealname.IDNoHash = record.IDNoHash
|
||||
existingRealname.VerifiedAt = record.VerifiedAt
|
||||
existingRealname.FailReason = record.FailReason
|
||||
if err := tx.Save(&existingRealname).Error; err != nil {
|
||||
if realname.IsIDNoDuplicateError(err) {
|
||||
return realname.ErrIDNoAlreadyUsed
|
||||
}
|
||||
return err
|
||||
}
|
||||
case errors.Is(findErr, gorm.ErrRecordNotFound):
|
||||
if err := tx.Create(&record).Error; err != nil {
|
||||
if realname.IsIDNoDuplicateError(err) {
|
||||
return realname.ErrIDNoAlreadyUsed
|
||||
}
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return findErr
|
||||
}
|
||||
|
||||
_ = invitation.OnUserRealnameVerifiedTx(tx, user.ID)
|
||||
|
||||
Reference in New Issue
Block a user