321 lines
9.5 KiB
Go
321 lines
9.5 KiB
Go
package admin
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math"
|
|
"rijig/model"
|
|
"rijig/utils"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type ApprovalService interface {
|
|
GetPendingUsers(ctx context.Context, req *GetPendingUsersRequest) (*PendingUsersListResponse, error)
|
|
ApproveUser(ctx context.Context, userID, adminID string, notes string) (*ApprovalActionResponse, error)
|
|
RejectUser(ctx context.Context, userID, adminID string, notes string) (*ApprovalActionResponse, error)
|
|
ProcessApprovalAction(ctx context.Context, req *ApprovalActionRequest, adminID string) (*ApprovalActionResponse, error)
|
|
BulkProcessApproval(ctx context.Context, req *BulkApprovalRequest, adminID string) (*BulkApprovalResponse, error)
|
|
GetUserApprovalDetails(ctx context.Context, userID string) (*PendingUserResponse, error)
|
|
}
|
|
|
|
type approvalService struct {
|
|
repo ApprovalRepository
|
|
}
|
|
|
|
func NewApprovalService(repo ApprovalRepository) ApprovalService {
|
|
return &approvalService{
|
|
repo: repo,
|
|
}
|
|
}
|
|
|
|
func (s *approvalService) GetPendingUsers(ctx context.Context, req *GetPendingUsersRequest) (*PendingUsersListResponse, error) {
|
|
|
|
req.SetDefaults()
|
|
|
|
users, totalRecords, err := s.repo.GetPendingUsers(ctx, req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get pending users: %w", err)
|
|
}
|
|
|
|
summary, err := s.repo.GetApprovalSummary(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get approval summary: %w", err)
|
|
}
|
|
|
|
totalPages := int(math.Ceil(float64(totalRecords) / float64(req.Limit)))
|
|
pagination := PaginationInfo{
|
|
Page: req.Page,
|
|
Limit: req.Limit,
|
|
TotalPages: totalPages,
|
|
TotalRecords: totalRecords,
|
|
HasNext: req.Page < totalPages,
|
|
HasPrev: req.Page > 1,
|
|
}
|
|
|
|
userResponses := make([]PendingUserResponse, len(users))
|
|
for i, user := range users {
|
|
userResponses[i] = s.convertToUserResponse(user)
|
|
}
|
|
|
|
return &PendingUsersListResponse{
|
|
Users: userResponses,
|
|
Pagination: pagination,
|
|
Summary: *summary,
|
|
}, nil
|
|
}
|
|
|
|
func (s *approvalService) ApproveUser(ctx context.Context, userID, adminID string, notes string) (*ApprovalActionResponse, error) {
|
|
|
|
user, err := s.repo.GetUserByID(ctx, userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get user: %w", err)
|
|
}
|
|
|
|
if err := s.validateUserForApproval(user, "approved"); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
previousStatus := user.RegistrationStatus
|
|
|
|
newStatus := utils.RegStatusConfirmed
|
|
newProgress := utils.ProgressDataSubmitted
|
|
|
|
if err := s.repo.UpdateUserRegistrationStatus(ctx, userID, newStatus, int8(newProgress)); err != nil {
|
|
return nil, fmt.Errorf("failed to approved user: %w", err)
|
|
}
|
|
|
|
if err := s.revokeUserTokens(userID); err != nil {
|
|
|
|
fmt.Printf("Warning: failed to revoke tokens for user %s: %v\n", userID, err)
|
|
}
|
|
|
|
return &ApprovalActionResponse{
|
|
UserID: userID,
|
|
Action: "approved",
|
|
PreviousStatus: previousStatus,
|
|
NewStatus: newStatus,
|
|
ProcessedAt: time.Now(),
|
|
ProcessedBy: adminID,
|
|
Notes: notes,
|
|
}, nil
|
|
}
|
|
|
|
func (s *approvalService) RejectUser(ctx context.Context, userID, adminID string, notes string) (*ApprovalActionResponse, error) {
|
|
|
|
user, err := s.repo.GetUserByID(ctx, userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get user: %w", err)
|
|
}
|
|
|
|
if err := s.validateUserForApproval(user, "rejected"); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
previousStatus := user.RegistrationStatus
|
|
|
|
newStatus := utils.RegStatusRejected
|
|
newProgress := utils.ProgressOTPVerified
|
|
|
|
if err := s.repo.UpdateUserRegistrationStatus(ctx, userID, newStatus, int8(newProgress)); err != nil {
|
|
return nil, fmt.Errorf("failed to rejected user: %w", err)
|
|
}
|
|
|
|
if err := s.revokeUserTokens(userID); err != nil {
|
|
|
|
fmt.Printf("Warning: failed to revoke tokens for user %s: %v\n", userID, err)
|
|
}
|
|
|
|
return &ApprovalActionResponse{
|
|
UserID: userID,
|
|
Action: "rejected",
|
|
PreviousStatus: previousStatus,
|
|
NewStatus: newStatus,
|
|
ProcessedAt: time.Now(),
|
|
ProcessedBy: adminID,
|
|
Notes: notes,
|
|
}, nil
|
|
}
|
|
|
|
func (s *approvalService) ProcessApprovalAction(ctx context.Context, req *ApprovalActionRequest, adminID string) (*ApprovalActionResponse, error) {
|
|
switch req.Action {
|
|
case "approved":
|
|
return s.ApproveUser(ctx, req.UserID, adminID, req.Notes)
|
|
case "rejected":
|
|
return s.RejectUser(ctx, req.UserID, adminID, req.Notes)
|
|
default:
|
|
return nil, fmt.Errorf("invalid action: %s", req.Action)
|
|
}
|
|
}
|
|
|
|
func (s *approvalService) BulkProcessApproval(ctx context.Context, req *BulkApprovalRequest, adminID string) (*BulkApprovalResponse, error) {
|
|
|
|
users, err := s.repo.GetUsersByIDs(ctx, req.UserIDs)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get users: %w", err)
|
|
}
|
|
|
|
var results []ApprovalActionResponse
|
|
var failures []ApprovalFailure
|
|
successCount := 0
|
|
|
|
for _, user := range users {
|
|
|
|
if err := s.validateUserForApproval(&user, req.Action); err != nil {
|
|
failures = append(failures, ApprovalFailure{
|
|
UserID: user.ID,
|
|
Error: "validation_failed",
|
|
Reason: err.Error(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
actionReq := &ApprovalActionRequest{
|
|
UserID: user.ID,
|
|
Action: req.Action,
|
|
Notes: req.Notes,
|
|
}
|
|
|
|
result, err := s.ProcessApprovalAction(ctx, actionReq, adminID)
|
|
if err != nil {
|
|
failures = append(failures, ApprovalFailure{
|
|
UserID: user.ID,
|
|
Error: "processing_failed",
|
|
Reason: err.Error(),
|
|
})
|
|
continue
|
|
}
|
|
|
|
results = append(results, *result)
|
|
successCount++
|
|
}
|
|
|
|
return &BulkApprovalResponse{
|
|
SuccessCount: successCount,
|
|
FailureCount: len(failures),
|
|
Results: results,
|
|
Failures: failures,
|
|
}, nil
|
|
}
|
|
|
|
func (s *approvalService) GetUserApprovalDetails(ctx context.Context, userID string) (*PendingUserResponse, error) {
|
|
user, err := s.repo.GetUserByID(ctx, userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get user details: %w", err)
|
|
}
|
|
|
|
userResponse := s.convertToUserResponse(*user)
|
|
return &userResponse, nil
|
|
}
|
|
|
|
func (s *approvalService) validateUserForApproval(user *model.User, action string) error {
|
|
|
|
if user.Role == nil {
|
|
return fmt.Errorf("user role not found")
|
|
}
|
|
|
|
roleName := strings.ToLower(user.Role.RoleName)
|
|
if roleName != "pengelola" && roleName != "pengepul" {
|
|
return fmt.Errorf("only pengelola and pengepul can be approved/rejected")
|
|
}
|
|
|
|
if user.RegistrationStatus != utils.RegStatusPending {
|
|
return fmt.Errorf("user is not in awaiting_approval status")
|
|
}
|
|
|
|
if user.RegistrationProgress < utils.ProgressDataSubmitted {
|
|
return fmt.Errorf("user has not submitted required data yet")
|
|
}
|
|
|
|
if roleName == "pengepul" && user.IdentityCard == nil {
|
|
return fmt.Errorf("pengepul must have identity card data")
|
|
}
|
|
|
|
if roleName == "pengelola" && user.CompanyProfile == nil {
|
|
return fmt.Errorf("pengelola must have company profile data")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *approvalService) revokeUserTokens(userID string) error {
|
|
|
|
return utils.RevokeAllRefreshTokens(userID)
|
|
}
|
|
|
|
func (s *approvalService) convertToUserResponse(user model.User) PendingUserResponse {
|
|
response := PendingUserResponse{
|
|
ID: user.ID,
|
|
Name: user.Name,
|
|
Phone: user.Phone,
|
|
Email: user.Email,
|
|
RegistrationStatus: user.RegistrationStatus,
|
|
RegistrationProgress: user.RegistrationProgress,
|
|
SubmittedAt: user.UpdatedAt,
|
|
}
|
|
|
|
if user.Role != nil {
|
|
response.Role = RoleInfo{
|
|
ID: user.Role.ID,
|
|
RoleName: user.Role.RoleName,
|
|
}
|
|
}
|
|
|
|
stepInfo := utils.GetRegistrationStepInfo(
|
|
user.Role.RoleName,
|
|
int(user.RegistrationProgress),
|
|
user.RegistrationStatus,
|
|
)
|
|
if stepInfo != nil {
|
|
response.RegistrationStepInfo = &RegistrationStepResponse{
|
|
Step: stepInfo.Step,
|
|
Status: stepInfo.Status,
|
|
Description: stepInfo.Description,
|
|
RequiresAdminApproval: stepInfo.RequiresAdminApproval,
|
|
IsAccessible: stepInfo.IsAccessible,
|
|
IsCompleted: stepInfo.IsCompleted,
|
|
}
|
|
}
|
|
|
|
if user.IdentityCard != nil {
|
|
response.IdentityCard = &IdentityCardInfo{
|
|
ID: user.IdentityCard.ID,
|
|
IdentificationNumber: user.IdentityCard.Identificationumber,
|
|
Fullname: user.IdentityCard.Fullname,
|
|
Placeofbirth: user.IdentityCard.Placeofbirth,
|
|
Dateofbirth: user.IdentityCard.Dateofbirth,
|
|
Gender: user.IdentityCard.Gender,
|
|
BloodType: user.IdentityCard.BloodType,
|
|
Province: user.IdentityCard.Province,
|
|
District: user.IdentityCard.District,
|
|
SubDistrict: user.IdentityCard.SubDistrict,
|
|
Village: user.IdentityCard.Village,
|
|
PostalCode: user.IdentityCard.PostalCode,
|
|
Religion: user.IdentityCard.Religion,
|
|
Maritalstatus: user.IdentityCard.Maritalstatus,
|
|
Job: user.IdentityCard.Job,
|
|
Citizenship: user.IdentityCard.Citizenship,
|
|
Validuntil: user.IdentityCard.Validuntil,
|
|
Cardphoto: user.IdentityCard.Cardphoto,
|
|
}
|
|
}
|
|
|
|
if user.CompanyProfile != nil {
|
|
response.CompanyProfile = &CompanyProfileInfo{
|
|
ID: user.CompanyProfile.ID,
|
|
CompanyName: user.CompanyProfile.CompanyName,
|
|
CompanyAddress: user.CompanyProfile.CompanyAddress,
|
|
CompanyPhone: user.CompanyProfile.CompanyPhone,
|
|
CompanyEmail: user.CompanyProfile.CompanyEmail,
|
|
CompanyLogo: user.CompanyProfile.CompanyLogo,
|
|
CompanyWebsite: user.CompanyProfile.CompanyWebsite,
|
|
TaxID: user.CompanyProfile.TaxID,
|
|
FoundedDate: user.CompanyProfile.FoundedDate,
|
|
CompanyType: user.CompanyProfile.CompanyType,
|
|
CompanyDescription: user.CompanyProfile.CompanyDescription,
|
|
}
|
|
}
|
|
|
|
return response
|
|
}
|