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:
187
internal/handler/admin_verification_handler.go
Normal file
187
internal/handler/admin_verification_handler.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/model"
|
||||
"carrot_bbs/internal/pkg/response"
|
||||
"carrot_bbs/internal/repository"
|
||||
"carrot_bbs/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AdminVerificationHandler struct {
|
||||
adminVerificationService service.AdminVerificationService
|
||||
userRepo repository.UserRepository
|
||||
}
|
||||
|
||||
func NewAdminVerificationHandler(
|
||||
adminVerificationService service.AdminVerificationService,
|
||||
userRepo repository.UserRepository,
|
||||
) *AdminVerificationHandler {
|
||||
return &AdminVerificationHandler{
|
||||
adminVerificationService: adminVerificationService,
|
||||
userRepo: userRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *AdminVerificationHandler) ListVerifications(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
status := c.Query("status")
|
||||
keyword := c.Query("keyword")
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 100 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
if status != "" && status != string(model.VerificationStatusPending) &&
|
||||
status != string(model.VerificationStatusApproved) &&
|
||||
status != string(model.VerificationStatusRejected) {
|
||||
response.BadRequest(c, "无效的状态值")
|
||||
return
|
||||
}
|
||||
|
||||
records, total, err := h.adminVerificationService.ListVerifications(c.Request.Context(), page, pageSize, status, keyword)
|
||||
if err != nil {
|
||||
response.InternalServerError(c, "获取认证列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
responses := make([]*dto.AdminVerificationListResponse, len(records))
|
||||
for i, record := range records {
|
||||
responses[i] = h.convertToAdminListResponse(record)
|
||||
}
|
||||
|
||||
response.Paginated(c, responses, total, page, pageSize)
|
||||
}
|
||||
|
||||
func (h *AdminVerificationHandler) GetVerificationDetail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
response.BadRequest(c, "认证ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
record, err := h.adminVerificationService.GetVerificationDetail(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
response.NotFound(c, "认证记录不存在")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, h.convertToAdminDetailResponse(record))
|
||||
}
|
||||
|
||||
func (h *AdminVerificationHandler) ReviewVerification(c *gin.Context) {
|
||||
reviewerID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Unauthorized(c, "未授权")
|
||||
return
|
||||
}
|
||||
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
response.BadRequest(c, "认证ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var req dto.AdminReviewVerificationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "请求参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !req.Approve && req.RejectReason == "" {
|
||||
response.BadRequest(c, "拒绝时必须填写拒绝原因")
|
||||
return
|
||||
}
|
||||
|
||||
record, err := h.adminVerificationService.ReviewVerification(
|
||||
c.Request.Context(),
|
||||
reviewerID.(string),
|
||||
id,
|
||||
req.Approve,
|
||||
req.RejectReason,
|
||||
)
|
||||
if err != nil {
|
||||
response.HandleError(c, err, "审核失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, h.convertToAdminDetailResponse(record))
|
||||
}
|
||||
|
||||
func (h *AdminVerificationHandler) convertToAdminListResponse(record *model.VerificationRecord) *dto.AdminVerificationListResponse {
|
||||
resp := &dto.AdminVerificationListResponse{
|
||||
ID: record.ID,
|
||||
UserID: record.UserID,
|
||||
Identity: string(record.Identity),
|
||||
Status: string(record.Status),
|
||||
RealName: record.RealName,
|
||||
StudentID: record.StudentID,
|
||||
EmployeeID: record.EmployeeID,
|
||||
Department: record.Department,
|
||||
CreatedAt: dto.FormatTime(record.CreatedAt),
|
||||
}
|
||||
|
||||
user, err := h.userRepo.GetByID(record.UserID)
|
||||
if err == nil && user != nil {
|
||||
resp.Username = user.Username
|
||||
resp.Nickname = user.Nickname
|
||||
resp.Avatar = user.Avatar
|
||||
}
|
||||
|
||||
if record.ReviewedAt != nil {
|
||||
resp.ReviewedAt = dto.FormatTime(*record.ReviewedAt)
|
||||
if record.ReviewedBy != nil {
|
||||
reviewer, err := h.userRepo.GetByID(*record.ReviewedBy)
|
||||
if err == nil && reviewer != nil {
|
||||
resp.ReviewerName = reviewer.Nickname
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (h *AdminVerificationHandler) convertToAdminDetailResponse(record *model.VerificationRecord) *dto.AdminVerificationDetailResponse {
|
||||
resp := &dto.AdminVerificationDetailResponse{
|
||||
ID: record.ID,
|
||||
UserID: record.UserID,
|
||||
Identity: string(record.Identity),
|
||||
Status: string(record.Status),
|
||||
RealName: record.RealName,
|
||||
StudentID: record.StudentID,
|
||||
EmployeeID: record.EmployeeID,
|
||||
Department: record.Department,
|
||||
ProofMaterials: record.ProofMaterials,
|
||||
CreatedAt: dto.FormatTime(record.CreatedAt),
|
||||
}
|
||||
|
||||
user, err := h.userRepo.GetByID(record.UserID)
|
||||
if err == nil && user != nil {
|
||||
resp.Username = user.Username
|
||||
resp.Nickname = user.Nickname
|
||||
resp.Avatar = user.Avatar
|
||||
}
|
||||
|
||||
if record.ReviewedAt != nil {
|
||||
resp.ReviewedAt = dto.FormatTime(*record.ReviewedAt)
|
||||
}
|
||||
if record.ReviewedBy != nil {
|
||||
resp.ReviewedBy = *record.ReviewedBy
|
||||
if reviewer, err := h.userRepo.GetByID(*record.ReviewedBy); err == nil && reviewer != nil {
|
||||
resp.ReviewerName = reviewer.Nickname
|
||||
}
|
||||
}
|
||||
if record.RejectReason != "" {
|
||||
resp.RejectReason = record.RejectReason
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
137
internal/handler/verification_handler.go
Normal file
137
internal/handler/verification_handler.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/model"
|
||||
"carrot_bbs/internal/pkg/response"
|
||||
"carrot_bbs/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type VerificationHandler struct {
|
||||
verificationService service.VerificationService
|
||||
}
|
||||
|
||||
func NewVerificationHandler(verificationService service.VerificationService) *VerificationHandler {
|
||||
return &VerificationHandler{
|
||||
verificationService: verificationService,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *VerificationHandler) SubmitVerification(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Unauthorized(c, "未授权")
|
||||
return
|
||||
}
|
||||
|
||||
var req dto.SubmitVerificationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "请求参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
record, err := h.verificationService.SubmitVerification(
|
||||
c.Request.Context(),
|
||||
userID.(string),
|
||||
model.UserIdentity(req.Identity),
|
||||
req.RealName,
|
||||
req.StudentID,
|
||||
req.EmployeeID,
|
||||
req.Department,
|
||||
req.ProofMaterials,
|
||||
)
|
||||
if err != nil {
|
||||
response.HandleError(c, err, "提交认证申请失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, convertVerificationRecordToResponse(record))
|
||||
}
|
||||
|
||||
func (h *VerificationHandler) GetVerificationStatus(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Unauthorized(c, "未授权")
|
||||
return
|
||||
}
|
||||
|
||||
status, err := h.verificationService.GetVerificationStatus(c.Request.Context(), userID.(string))
|
||||
if err != nil {
|
||||
response.HandleError(c, err, "获取认证状态失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, &dto.VerificationStatusResponse{
|
||||
Identity: status.Identity,
|
||||
VerificationStatus: status.VerificationStatus,
|
||||
HasPendingRequest: status.HasPendingRequest,
|
||||
LatestRecord: convertVerificationRecordToResponse(status.LatestRecord),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *VerificationHandler) GetVerificationRecords(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Unauthorized(c, "未授权")
|
||||
return
|
||||
}
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 || pageSize > 50 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
records, total, err := h.verificationService.GetVerificationRecords(c.Request.Context(), userID.(string), page, pageSize)
|
||||
if err != nil {
|
||||
response.HandleError(c, err, "获取认证记录失败")
|
||||
return
|
||||
}
|
||||
|
||||
responses := make([]*dto.VerificationRecordResponse, len(records))
|
||||
for i, record := range records {
|
||||
responses[i] = convertVerificationRecordToResponse(record)
|
||||
}
|
||||
|
||||
response.Paginated(c, responses, total, page, pageSize)
|
||||
}
|
||||
|
||||
func convertVerificationRecordToResponse(record *model.VerificationRecord) *dto.VerificationRecordResponse {
|
||||
if record == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp := &dto.VerificationRecordResponse{
|
||||
ID: record.ID,
|
||||
UserID: record.UserID,
|
||||
Identity: string(record.Identity),
|
||||
Status: string(record.Status),
|
||||
RealName: record.RealName,
|
||||
StudentID: record.StudentID,
|
||||
EmployeeID: record.EmployeeID,
|
||||
Department: record.Department,
|
||||
ProofMaterials: record.ProofMaterials,
|
||||
CreatedAt: dto.FormatTime(record.CreatedAt),
|
||||
UpdatedAt: dto.FormatTime(record.UpdatedAt),
|
||||
}
|
||||
|
||||
if record.ReviewedAt != nil {
|
||||
resp.ReviewedAt = dto.FormatTime(*record.ReviewedAt)
|
||||
}
|
||||
if record.ReviewedBy != nil {
|
||||
resp.ReviewedBy = *record.ReviewedBy
|
||||
}
|
||||
if record.RejectReason != "" {
|
||||
resp.RejectReason = record.RejectReason
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
Reference in New Issue
Block a user