feat(verification): add user identity verification system
Implement a complete user identity verification system with support for multiple identity types (student, teacher, staff, alumni). The system includes user-facing verification submission and status checking endpoints, admin endpoints for reviewing verification requests, middleware for protecting verification-required routes, and integration with the user model to track verification status.
This commit is contained in:
176
internal/service/verification_service.go
Normal file
176
internal/service/verification_service.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
apperrors "carrot_bbs/internal/errors"
|
||||
"carrot_bbs/internal/model"
|
||||
"carrot_bbs/internal/repository"
|
||||
)
|
||||
|
||||
type VerificationService interface {
|
||||
SubmitVerification(ctx context.Context, userID string, identity model.UserIdentity, realName, studentID, employeeID, department, proofMaterials string) (*model.VerificationRecord, error)
|
||||
GetVerificationStatus(ctx context.Context, userID string) (*VerificationStatusResponse, error)
|
||||
GetVerificationRecords(ctx context.Context, userID string, page, pageSize int) ([]*model.VerificationRecord, int64, error)
|
||||
}
|
||||
|
||||
type VerificationStatusResponse struct {
|
||||
UserID string `json:"user_id"`
|
||||
Identity string `json:"identity"`
|
||||
VerificationStatus string `json:"verification_status"`
|
||||
HasPendingRequest bool `json:"has_pending_request"`
|
||||
LatestRecord *model.VerificationRecord `json:"latest_record,omitempty"`
|
||||
}
|
||||
|
||||
type verificationServiceImpl struct {
|
||||
verificationRepo repository.VerificationRepository
|
||||
userRepo repository.UserRepository
|
||||
}
|
||||
|
||||
func NewVerificationService(
|
||||
verificationRepo repository.VerificationRepository,
|
||||
userRepo repository.UserRepository,
|
||||
) VerificationService {
|
||||
return &verificationServiceImpl{
|
||||
verificationRepo: verificationRepo,
|
||||
userRepo: userRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *verificationServiceImpl) SubmitVerification(ctx context.Context, userID string, identity model.UserIdentity, realName, studentID, employeeID, department, proofMaterials string) (*model.VerificationRecord, error) {
|
||||
user, err := s.userRepo.GetByID(userID)
|
||||
if err != nil {
|
||||
return nil, apperrors.ErrUserNotFound
|
||||
}
|
||||
|
||||
if user.VerificationStatus == model.VerificationStatusApproved {
|
||||
return nil, apperrors.ErrAlreadyVerified
|
||||
}
|
||||
|
||||
existing, err := s.verificationRepo.GetPendingByUserID(userID)
|
||||
if err == nil && existing != nil {
|
||||
return nil, apperrors.ErrVerificationPending
|
||||
}
|
||||
|
||||
record := &model.VerificationRecord{
|
||||
UserID: userID,
|
||||
Identity: identity,
|
||||
Status: model.VerificationStatusPending,
|
||||
RealName: realName,
|
||||
StudentID: studentID,
|
||||
EmployeeID: employeeID,
|
||||
Department: department,
|
||||
ProofMaterials: proofMaterials,
|
||||
}
|
||||
|
||||
if err := s.verificationRepo.Create(record); err != nil {
|
||||
return nil, fmt.Errorf("创建认证记录失败: %w", err)
|
||||
}
|
||||
|
||||
user.VerificationStatus = model.VerificationStatusPending
|
||||
user.Identity = identity
|
||||
if err := s.userRepo.Update(user); err != nil {
|
||||
return nil, fmt.Errorf("更新用户状态失败: %w", err)
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func (s *verificationServiceImpl) GetVerificationStatus(ctx context.Context, userID string) (*VerificationStatusResponse, error) {
|
||||
user, err := s.userRepo.GetByID(userID)
|
||||
if err != nil {
|
||||
return nil, apperrors.ErrUserNotFound
|
||||
}
|
||||
|
||||
response := &VerificationStatusResponse{
|
||||
UserID: userID,
|
||||
Identity: string(user.Identity),
|
||||
VerificationStatus: string(user.VerificationStatus),
|
||||
HasPendingRequest: false,
|
||||
}
|
||||
|
||||
latestRecord, err := s.verificationRepo.GetLatestByUserID(userID)
|
||||
if err == nil && latestRecord != nil {
|
||||
response.LatestRecord = latestRecord
|
||||
response.HasPendingRequest = latestRecord.Status == model.VerificationStatusPending
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *verificationServiceImpl) GetVerificationRecords(ctx context.Context, userID string, page, pageSize int) ([]*model.VerificationRecord, int64, error) {
|
||||
return s.verificationRepo.GetRecordsByUserID(userID, page, pageSize)
|
||||
}
|
||||
|
||||
type AdminVerificationService interface {
|
||||
ListVerifications(ctx context.Context, page, pageSize int, status, keyword string) ([]*model.VerificationRecord, int64, error)
|
||||
GetVerificationDetail(ctx context.Context, id string) (*model.VerificationRecord, error)
|
||||
ReviewVerification(ctx context.Context, reviewerID string, recordID string, approve bool, rejectReason string) (*model.VerificationRecord, error)
|
||||
}
|
||||
|
||||
type adminVerificationServiceImpl struct {
|
||||
verificationRepo repository.VerificationRepository
|
||||
userRepo repository.UserRepository
|
||||
}
|
||||
|
||||
func NewAdminVerificationService(
|
||||
verificationRepo repository.VerificationRepository,
|
||||
userRepo repository.UserRepository,
|
||||
) AdminVerificationService {
|
||||
return &adminVerificationServiceImpl{
|
||||
verificationRepo: verificationRepo,
|
||||
userRepo: userRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *adminVerificationServiceImpl) ListVerifications(ctx context.Context, page, pageSize int, status, keyword string) ([]*model.VerificationRecord, int64, error) {
|
||||
return s.verificationRepo.List(page, pageSize, status, keyword)
|
||||
}
|
||||
|
||||
func (s *adminVerificationServiceImpl) GetVerificationDetail(ctx context.Context, id string) (*model.VerificationRecord, error) {
|
||||
return s.verificationRepo.GetByID(id)
|
||||
}
|
||||
|
||||
func (s *adminVerificationServiceImpl) ReviewVerification(ctx context.Context, reviewerID string, recordID string, approve bool, rejectReason string) (*model.VerificationRecord, error) {
|
||||
record, err := s.verificationRepo.GetByID(recordID)
|
||||
if err != nil {
|
||||
return nil, apperrors.ErrVerificationNotFound
|
||||
}
|
||||
|
||||
if record.Status != model.VerificationStatusPending {
|
||||
return nil, apperrors.ErrVerificationAlreadyHandled
|
||||
}
|
||||
|
||||
if approve {
|
||||
record.Status = model.VerificationStatusApproved
|
||||
} else {
|
||||
record.Status = model.VerificationStatusRejected
|
||||
record.RejectReason = rejectReason
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
record.ReviewedBy = &reviewerID
|
||||
record.ReviewedAt = &now
|
||||
|
||||
if err := s.verificationRepo.Update(record); err != nil {
|
||||
return nil, fmt.Errorf("更新认证记录失败: %w", err)
|
||||
}
|
||||
|
||||
user, err := s.userRepo.GetByID(record.UserID)
|
||||
if err != nil {
|
||||
return record, nil
|
||||
}
|
||||
|
||||
if approve {
|
||||
user.VerificationStatus = model.VerificationStatusApproved
|
||||
user.Identity = record.Identity
|
||||
} else {
|
||||
user.VerificationStatus = model.VerificationStatusRejected
|
||||
}
|
||||
|
||||
_ = s.userRepo.Update(user)
|
||||
|
||||
return record, nil
|
||||
}
|
||||
Reference in New Issue
Block a user