On branch main
modified: main_dc/yalarba/api_yal/internal/domain/account/dto.go new file: main_dc/yalarba/api_yal/internal/domain/account/errors.go modified: main_dc/yalarba/api_yal/internal/domain/account/handler.go modified: main_dc/yalarba/api_yal/internal/domain/account/router.go modified: main_dc/yalarba/api_yal/internal/domain/account/service.go new file: main_dc/yalarba/api_yal/internal/domain/account/types.go new file: main_dc/yalarba/api_yal/internal/middleware/admin.go modified: main_dc/yalarba/api_yal/internal/middleware/auth.go new file: main_dc/yalarba/api_yal/internal/middleware/context.go new file: main_dc/yalarba/api_yal/internal/middleware/logging.go modified: main_dc/yalarba/api_yal/internal/router/router.go last but not yet commit
This commit is contained in:
@@ -9,11 +9,11 @@ import (
|
||||
|
||||
// CreateAccountRequest - DTO для создания нового аккаунта
|
||||
type CreateAccountRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Password string `json:"password" binding:"required,min=6"`
|
||||
FullName string `json:"full_name" binding:"required"`
|
||||
FirstName string `json:"first_name" binding:"required"`
|
||||
LastName string `json:"last_name" binding:"required"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required,min=6"`
|
||||
FullName string `json:"full_name" validate:"required"`
|
||||
FirstName string `json:"first_name" validate:"required"`
|
||||
LastName string `json:"last_name" validate:"required"`
|
||||
Phone string `json:"phone"`
|
||||
City string `json:"city"`
|
||||
|
||||
@@ -43,19 +43,19 @@ type UpdateAccountRequest struct {
|
||||
|
||||
// ChangePasswordRequest - DTO для смены пароля
|
||||
type ChangePasswordRequest struct {
|
||||
CurrentPassword string `json:"current_password" binding:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required,min=6"`
|
||||
CurrentPassword string `json:"current_password" validate:"required"`
|
||||
NewPassword string `json:"new_password" validate:"required,min=6"`
|
||||
}
|
||||
|
||||
// ForgotPasswordRequest - DTO для запроса сброса пароля
|
||||
type ForgotPasswordRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
}
|
||||
|
||||
// ResetPasswordRequest - DTO для сброса пароля по токену
|
||||
type ResetPasswordRequest struct {
|
||||
Token string `json:"token" binding:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required,min=6"`
|
||||
Token string `json:"token" validate:"required"`
|
||||
NewPassword string `json:"new_password" validate:"required,min=6"`
|
||||
}
|
||||
|
||||
// VerifyAccountRequest - DTO для верификации аккаунта (админ)
|
||||
@@ -66,15 +66,15 @@ type VerifyAccountRequest struct {
|
||||
// UpdateAccountStatusRequest - DTO для изменения статуса аккаунта (админ)
|
||||
type UpdateAccountStatusRequest struct {
|
||||
IsActive bool `json:"is_active"`
|
||||
Role string `json:"role" binding:"omitempty,oneof=user admin moderator"`
|
||||
Role string `json:"role" validate:"omitempty,oneof=user admin moderator"`
|
||||
}
|
||||
|
||||
// ListAccountsRequest - DTO для фильтрации списка аккаунтов
|
||||
type ListAccountsRequest struct {
|
||||
Page int `form:"page" binding:"min=1"`
|
||||
PageSize int `form:"page_size" binding:"min=1,max=100"`
|
||||
Page int `form:"page" validate:"min=1"`
|
||||
PageSize int `form:"page_size" validate:"min=1,max=100"`
|
||||
Search string `form:"search"`
|
||||
Role string `form:"role" binding:"omitempty,oneof=user admin moderator"`
|
||||
Role string `form:"role" validate:"omitempty,oneof=user admin moderator"`
|
||||
IsActive *bool `form:"is_active"`
|
||||
}
|
||||
|
||||
@@ -140,19 +140,19 @@ type AccountProfileResponse struct {
|
||||
|
||||
// AccountStats - статистика аккаунта
|
||||
type AccountStats struct {
|
||||
ObjectsCount int `json:"objects_count"`
|
||||
FeedbacksCount int `json:"feedbacks_count"`
|
||||
CommentsCount int `json:"comments_count"`
|
||||
RatingsCount int `json:"ratings_count"`
|
||||
AppealsCount int `json:"appeals_count"`
|
||||
ObjectsCount int `json:"objects_count"`
|
||||
FeedbacksCount int `json:"feedbacks_count"`
|
||||
CommentsCount int `json:"comments_count"`
|
||||
RatingsCount int `json:"ratings_count"`
|
||||
AppealsCount int `json:"appeals_count"`
|
||||
}
|
||||
|
||||
// AuthResponse - DTO для ответа при авторизации
|
||||
type AuthResponse struct {
|
||||
Token string `json:"token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresAt int64 `json:"expires_at"`
|
||||
Account AccountResponse `json:"account"`
|
||||
Token string `json:"token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresAt int64 `json:"expires_at"`
|
||||
Account AccountResponse `json:"account"`
|
||||
}
|
||||
|
||||
// PasswordResetResponse - DTO для ответа о сбросе пароля
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package account
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// Основные ошибки
|
||||
ErrAccountNotFound = errors.New("account not found")
|
||||
ErrAccountAlreadyExists = errors.New("account already exists")
|
||||
ErrAccountDeactivated = errors.New("account is deactivated")
|
||||
ErrAccountNotVerified = errors.New("account is not verified")
|
||||
|
||||
// Ошибки пароля
|
||||
ErrInvalidCurrentPassword = errors.New("invalid current password")
|
||||
ErrPasswordTooWeak = errors.New("password too weak")
|
||||
ErrPasswordMismatch = errors.New("password mismatch")
|
||||
|
||||
// Ошибки токенов сброса пароля
|
||||
ErrResetTokenNotFound = errors.New("reset token not found")
|
||||
ErrResetTokenExpired = errors.New("reset token has expired")
|
||||
ErrResetTokenAlreadyUsed = errors.New("reset token already used")
|
||||
ErrResetTokenInvalid = errors.New("invalid reset token")
|
||||
|
||||
// Ошибки доступа
|
||||
ErrInsufficientPermissions = errors.New("insufficient permissions")
|
||||
ErrUnauthorized = errors.New("unauthorized")
|
||||
|
||||
// Ошибки валидации
|
||||
ErrInvalidEmail = errors.New("invalid email")
|
||||
ErrInvalidPhone = errors.New("invalid phone number")
|
||||
ErrInvalidINN = errors.New("invalid INN")
|
||||
)
|
||||
@@ -1 +1,447 @@
|
||||
package account
|
||||
package account
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"api_yal/internal/logger"
|
||||
"api_yal/internal/middleware"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Handler обработчик для операций с аккаунтами
|
||||
type Handler struct {
|
||||
service Service
|
||||
validator *validator.Validate
|
||||
}
|
||||
|
||||
// NewHandler создает новый экземпляр Handler
|
||||
func NewHandler(service Service) *Handler {
|
||||
return &Handler{
|
||||
service: service,
|
||||
validator: validator.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetAccountByID получение аккаунта по ID
|
||||
func (h *Handler) GetAccountByID(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
// Получаем ID из контекста (для своего профиля)
|
||||
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Проверяем, что пользователь запрашивает свой профиль
|
||||
// В реальном приложении админы могут просматривать чужие профили
|
||||
account, err := h.service.GetAccountByID(userID)
|
||||
if err != nil {
|
||||
l.Error("Ошибка получения аккаунта", zap.Error(err))
|
||||
|
||||
if errors.Is(err, ErrAccountNotFound) {
|
||||
http.Error(w, "Account not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Failed to get account", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(account)
|
||||
}
|
||||
|
||||
// GetAccountProfile получение профиля пользователя
|
||||
func (h *Handler) GetAccountProfile(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
profile, err := h.service.GetAccountProfile(userID)
|
||||
if err != nil {
|
||||
l.Error("Ошибка получения профиля", zap.Error(err))
|
||||
|
||||
if errors.Is(err, ErrAccountNotFound) {
|
||||
http.Error(w, "Account not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Failed to get profile", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(profile)
|
||||
}
|
||||
|
||||
// UpdateAccount обновление аккаунта
|
||||
func (h *Handler) UpdateAccount(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdateAccountRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Валидация не требуется для всех полей, так как они опциональны
|
||||
account, err := h.service.UpdateAccount(userID, req)
|
||||
if err != nil {
|
||||
l.Error("Ошибка обновления аккаунта", zap.Error(err))
|
||||
|
||||
if errors.Is(err, ErrAccountNotFound) {
|
||||
http.Error(w, "Account not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Failed to update account", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(account)
|
||||
}
|
||||
|
||||
// ChangePassword смена пароля
|
||||
func (h *Handler) ChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
var req ChangePasswordRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.validator.Struct(req); err != nil {
|
||||
h.handleValidationError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.ChangePassword(userID, req); err != nil {
|
||||
l.Error("Ошибка смены пароля", zap.Error(err))
|
||||
|
||||
if errors.Is(err, ErrInvalidCurrentPassword) {
|
||||
http.Error(w, "Current password is incorrect", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, ErrAccountNotFound) {
|
||||
http.Error(w, "Account not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Failed to change password", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"message": "Password changed successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// ForgotPassword запрос на сброс пароля
|
||||
func (h *Handler) ForgotPassword(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
var req ForgotPasswordRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.validator.Struct(req); err != nil {
|
||||
h.handleValidationError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := h.service.ForgotPassword(req.Email)
|
||||
if err != nil {
|
||||
l.Error("Ошибка запроса сброса пароля", zap.Error(err))
|
||||
http.Error(w, "Password reset request failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// В реальном приложении здесь отправляется email
|
||||
// Для тестирования возвращаем токен
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(PasswordResetResponse{
|
||||
Message: "If the email exists, a reset link has been sent",
|
||||
Token: token, // Только для тестирования
|
||||
})
|
||||
}
|
||||
|
||||
// ResetPassword подтверждение сброса пароля
|
||||
func (h *Handler) ResetPassword(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
var req ResetPasswordRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.validator.Struct(req); err != nil {
|
||||
h.handleValidationError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.ResetPassword(req.Token, req.NewPassword); err != nil {
|
||||
l.Error("Ошибка сброса пароля", zap.Error(err))
|
||||
|
||||
if errors.Is(err, ErrResetTokenNotFound) {
|
||||
http.Error(w, "Reset token not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, ErrResetTokenExpired) {
|
||||
http.Error(w, "Reset token has expired", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, ErrResetTokenAlreadyUsed) {
|
||||
http.Error(w, "Reset token has already been used", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Password reset failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"message": "Password has been successfully reset",
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteAccount удаление аккаунта
|
||||
func (h *Handler) DeleteAccount(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
|
||||
if !ok {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.DeleteAccount(userID); err != nil {
|
||||
l.Error("Ошибка удаления аккаунта", zap.Error(err))
|
||||
|
||||
if errors.Is(err, ErrAccountNotFound) {
|
||||
http.Error(w, "Account not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Failed to delete account", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"message": "Account deleted successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// ==================== Административные методы ====================
|
||||
|
||||
// ListAccounts список аккаунтов (админ)
|
||||
func (h *Handler) ListAccounts(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
var req ListAccountsRequest
|
||||
|
||||
// Парсинг query параметров
|
||||
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
|
||||
page, _ := strconv.Atoi(pageStr)
|
||||
req.Page = page
|
||||
}
|
||||
if pageSizeStr := r.URL.Query().Get("page_size"); pageSizeStr != "" {
|
||||
pageSize, _ := strconv.Atoi(pageSizeStr)
|
||||
req.PageSize = pageSize
|
||||
}
|
||||
req.Search = r.URL.Query().Get("search")
|
||||
req.Role = r.URL.Query().Get("role")
|
||||
if isActiveStr := r.URL.Query().Get("is_active"); isActiveStr != "" {
|
||||
isActive, _ := strconv.ParseBool(isActiveStr)
|
||||
req.IsActive = &isActive
|
||||
}
|
||||
|
||||
if err := h.validator.Struct(req); err != nil {
|
||||
h.handleValidationError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := h.service.ListAccounts(req)
|
||||
if err != nil {
|
||||
l.Error("Ошибка получения списка аккаунтов", zap.Error(err))
|
||||
http.Error(w, "Failed to list accounts", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// GetAccountByIDAdmin получение аккаунта по ID (админ)
|
||||
func (h *Handler) GetAccountByIDAdmin(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
// Получаем ID из URL
|
||||
idStr := r.URL.Query().Get("id")
|
||||
if idStr == "" {
|
||||
http.Error(w, "Missing id parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid id parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
account, err := h.service.GetAccountByID(uint(id))
|
||||
if err != nil {
|
||||
l.Error("Ошибка получения аккаунта", zap.Error(err))
|
||||
|
||||
if errors.Is(err, ErrAccountNotFound) {
|
||||
http.Error(w, "Account not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Failed to get account", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(account)
|
||||
}
|
||||
|
||||
// VerifyAccount верификация аккаунта (админ)
|
||||
func (h *Handler) VerifyAccount(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
// Получаем ID из URL
|
||||
idStr := r.URL.Query().Get("id")
|
||||
if idStr == "" {
|
||||
http.Error(w, "Missing id parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid id parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var req VerifyAccountRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.VerifyAccount(uint(id), req); err != nil {
|
||||
l.Error("Ошибка верификации аккаунта", zap.Error(err))
|
||||
|
||||
if errors.Is(err, ErrAccountNotFound) {
|
||||
http.Error(w, "Account not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Failed to verify account", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"message": "Account verified successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateAccountStatus обновление статуса аккаунта (админ)
|
||||
func (h *Handler) UpdateAccountStatus(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.Get()
|
||||
|
||||
// Получаем ID из URL
|
||||
idStr := r.URL.Query().Get("id")
|
||||
if idStr == "" {
|
||||
http.Error(w, "Missing id parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid id parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdateAccountStatusRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "Invalid request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.validator.Struct(req); err != nil {
|
||||
h.handleValidationError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.UpdateAccountStatus(uint(id), req); err != nil {
|
||||
l.Error("Ошибка обновления статуса аккаунта", zap.Error(err))
|
||||
|
||||
if errors.Is(err, ErrAccountNotFound) {
|
||||
http.Error(w, "Account not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
http.Error(w, "Failed to update account status", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"message": "Account status updated successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// handleValidationError обрабатывает ошибки валидации
|
||||
func (h *Handler) handleValidationError(w http.ResponseWriter, err error) {
|
||||
var invalidValidationError *validator.InvalidValidationError
|
||||
if errors.As(err, &invalidValidationError) {
|
||||
http.Error(w, "Invalid request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var errs []string
|
||||
for _, err := range err.(validator.ValidationErrors) {
|
||||
errs = append(errs, fmt.Sprintf("field %s is invalid: %s", err.Field(), err.Tag()))
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": "Validation failed",
|
||||
"fields": errs,
|
||||
})
|
||||
}
|
||||
@@ -1 +1,51 @@
|
||||
package account
|
||||
package account
|
||||
|
||||
import (
|
||||
"api_yal/internal/logger"
|
||||
"api_yal/internal/middleware"
|
||||
"api_yal/internal/repository"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// RegisterRoutes регистрирует маршруты для работы с аккаунтами
|
||||
func RegisterRoutes(r chi.Router, db *gorm.DB, jwtSecret string) {
|
||||
l := logger.Get()
|
||||
l.Debug("Регистрация маршрутов аккаунтов")
|
||||
|
||||
// Создаем репозиторий и сервис
|
||||
accountRepo := repository.NewAccountRepository(db)
|
||||
accountService := NewService(accountRepo)
|
||||
accountHandler := NewHandler(accountService)
|
||||
|
||||
// Публичные маршруты (без аутентификации)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Post("/forgot-password", accountHandler.ForgotPassword)
|
||||
r.Post("/reset-password", accountHandler.ResetPassword)
|
||||
})
|
||||
|
||||
// Защищенные маршруты (требуют аутентификации)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.AuthMiddleware(jwtSecret))
|
||||
|
||||
r.Get("/profile", accountHandler.GetAccountProfile)
|
||||
r.Get("/me", accountHandler.GetAccountByID)
|
||||
r.Put("/me", accountHandler.UpdateAccount)
|
||||
r.Delete("/me", accountHandler.DeleteAccount)
|
||||
r.Post("/change-password", accountHandler.ChangePassword)
|
||||
})
|
||||
|
||||
// Административные маршруты (требуют прав администратора)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.AuthMiddleware(jwtSecret))
|
||||
r.Use(middleware.AdminOnlyMiddleware)
|
||||
|
||||
r.Get("/accounts", accountHandler.ListAccounts)
|
||||
r.Get("/account", accountHandler.GetAccountByIDAdmin)
|
||||
r.Put("/account/verify", accountHandler.VerifyAccount)
|
||||
r.Put("/account/status", accountHandler.UpdateAccountStatus)
|
||||
})
|
||||
|
||||
l.Info("Маршруты аккаунтов зарегистрированы")
|
||||
}
|
||||
@@ -1 +1,561 @@
|
||||
package account
|
||||
package account
|
||||
|
||||
import (
|
||||
"api_yal/internal/logger"
|
||||
"api_yal/internal/models"
|
||||
"api_yal/internal/repository"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Service интерфейс сервиса аккаунтов
|
||||
type Service interface {
|
||||
// Основные операции
|
||||
GetAccountByID(id uint) (*AccountResponse, error)
|
||||
GetAccountByEmail(email string) (*AccountResponse, error)
|
||||
GetAccountWithObjects(id uint) (*AccountWithObjectsResponse, error)
|
||||
UpdateAccount(id uint, req UpdateAccountRequest) (*AccountResponse, error)
|
||||
DeleteAccount(id uint) error
|
||||
|
||||
// Управление паролем
|
||||
ChangePassword(userID uint, req ChangePasswordRequest) error
|
||||
ForgotPassword(email string) (string, error)
|
||||
ResetPassword(token, newPassword string) error
|
||||
|
||||
// Административные функции
|
||||
ListAccounts(req ListAccountsRequest) (*AccountListResponse, error)
|
||||
VerifyAccount(accountID uint, req VerifyAccountRequest) error
|
||||
UpdateAccountStatus(accountID uint, req UpdateAccountStatusRequest) error
|
||||
|
||||
// Статистика
|
||||
GetAccountStats(userID uint) (*AccountStats, error)
|
||||
GetAccountProfile(userID uint) (*AccountProfileResponse, error)
|
||||
|
||||
// Внутренние методы для auth сервиса
|
||||
GetAccountModelByID(id uint) (*models.Account, error)
|
||||
GetAccountModelByEmail(email string) (*models.Account, error)
|
||||
CreateAccount(req CreateAccountRequest) (*models.Account, error)
|
||||
UpdateAccountModel(account *models.Account) error
|
||||
}
|
||||
|
||||
// serviceImpl реализация сервиса аккаунтов
|
||||
type serviceImpl struct {
|
||||
accountRepo repository.AccountRepository
|
||||
}
|
||||
|
||||
// NewService создает новый экземпляр сервиса аккаунтов
|
||||
func NewService(accountRepo repository.AccountRepository) Service {
|
||||
return &serviceImpl{
|
||||
accountRepo: accountRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// GetAccountByID получает аккаунт по ID
|
||||
func (s *serviceImpl) GetAccountByID(id uint) (*AccountResponse, error) {
|
||||
l := logger.Get()
|
||||
l.Debug("Получение аккаунта по ID", zap.Uint("id", id))
|
||||
|
||||
account, err := s.accountRepo.GetByID(id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrAccountNotFound
|
||||
}
|
||||
l.Error("Ошибка получения аккаунта", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := ToAccountResponse(account)
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// GetAccountByEmail получает аккаунт по email
|
||||
func (s *serviceImpl) GetAccountByEmail(email string) (*AccountResponse, error) {
|
||||
l := logger.Get()
|
||||
l.Debug("Получение аккаунта по email", zap.String("email", email))
|
||||
|
||||
account, err := s.accountRepo.GetByEmail(email)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrAccountNotFound
|
||||
}
|
||||
l.Error("Ошибка получения аккаунта", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := ToAccountResponse(account)
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// GetAccountWithObjects получает аккаунт с его объектами
|
||||
func (s *serviceImpl) GetAccountWithObjects(id uint) (*AccountWithObjectsResponse, error) {
|
||||
l := logger.Get()
|
||||
l.Debug("Получение аккаунта с объектами", zap.Uint("id", id))
|
||||
|
||||
account, err := s.accountRepo.GetByID(id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrAccountNotFound
|
||||
}
|
||||
l.Error("Ошибка получения аккаунта", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
objects, err := s.accountRepo.GetObjects(id)
|
||||
if err != nil {
|
||||
l.Error("Ошибка получения объектов", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
account.Objects = objects
|
||||
|
||||
response := ToAccountWithObjectsResponse(account)
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// UpdateAccount обновляет информацию об аккаунте
|
||||
func (s *serviceImpl) UpdateAccount(id uint, req UpdateAccountRequest) (*AccountResponse, error) {
|
||||
l := logger.Get()
|
||||
l.Info("Обновление аккаунта", zap.Uint("id", id))
|
||||
|
||||
account, err := s.accountRepo.GetByID(id)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrAccountNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Обновляем поля
|
||||
if req.FullName != "" {
|
||||
account.FullName = req.FullName
|
||||
}
|
||||
if req.FirstName != "" {
|
||||
account.FirstName = req.FirstName
|
||||
}
|
||||
if req.LastName != "" {
|
||||
account.LastName = req.LastName
|
||||
}
|
||||
if req.Phone != "" {
|
||||
account.Phone = req.Phone
|
||||
}
|
||||
if req.City != "" {
|
||||
account.City = req.City
|
||||
}
|
||||
if req.OrganizationForm != "" {
|
||||
account.OrganizationForm = req.OrganizationForm
|
||||
}
|
||||
if req.OrganizationName != "" {
|
||||
account.OrganizationName = req.OrganizationName
|
||||
}
|
||||
if req.OrganizationShort != "" {
|
||||
account.OrganizationShort = req.OrganizationShort
|
||||
}
|
||||
if req.INN != "" {
|
||||
account.INN = req.INN
|
||||
}
|
||||
if req.PersonalINN != "" {
|
||||
account.PersonalINN = req.PersonalINN
|
||||
}
|
||||
|
||||
if err := s.accountRepo.Update(account); err != nil {
|
||||
l.Error("Ошибка обновления аккаунта", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := ToAccountResponse(account)
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// DeleteAccount удаляет аккаунт (мягкое удаление)
|
||||
func (s *serviceImpl) DeleteAccount(id uint) error {
|
||||
l := logger.Get()
|
||||
l.Info("Удаление аккаунта", zap.Uint("id", id))
|
||||
|
||||
if err := s.accountRepo.Delete(id); err != nil {
|
||||
l.Error("Ошибка удаления аккаунта", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangePassword изменяет пароль пользователя
|
||||
func (s *serviceImpl) ChangePassword(userID uint, req ChangePasswordRequest) error {
|
||||
l := logger.Get()
|
||||
l.Info("Смена пароля", zap.Uint("userID", userID))
|
||||
|
||||
account, err := s.accountRepo.GetByID(userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrAccountNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Проверяем текущий пароль
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(account.PasswordHash), []byte(req.CurrentPassword)); err != nil {
|
||||
l.Error("Неверный текущий пароль")
|
||||
return ErrInvalidCurrentPassword
|
||||
}
|
||||
|
||||
// Хешируем новый пароль
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
l.Error("Ошибка хеширования пароля", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
account.PasswordHash = string(hashedPassword)
|
||||
if err := s.accountRepo.Update(account); err != nil {
|
||||
l.Error("Ошибка обновления пароля", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
l.Info("Пароль успешно изменен", zap.Uint("userID", userID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// ForgotPassword запрашивает сброс пароля
|
||||
func (s *serviceImpl) ForgotPassword(email string) (string, error) {
|
||||
l := logger.Get()
|
||||
l.Info("Запрос сброса пароля", zap.String("email", email))
|
||||
|
||||
account, err := s.accountRepo.GetByEmail(email)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// Для безопасности не сообщаем, что пользователь не найден
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Генерируем reset token (используем метод из auth сервиса)
|
||||
// В реальном приложении здесь должна быть генерация токена
|
||||
resetToken := generateResetToken()
|
||||
|
||||
passwordReset := &models.PasswordReset{
|
||||
AccountID: account.ID,
|
||||
Token: resetToken,
|
||||
ExpiresAt: time.Now().Add(1 * time.Hour),
|
||||
Used: false,
|
||||
}
|
||||
|
||||
if err := s.accountRepo.CreatePasswordReset(passwordReset); err != nil {
|
||||
l.Error("Ошибка сохранения reset token", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
l.Info("Reset token создан", zap.String("email", email))
|
||||
return resetToken, nil
|
||||
}
|
||||
|
||||
// ResetPassword сбрасывает пароль по токену
|
||||
func (s *serviceImpl) ResetPassword(token, newPassword string) error {
|
||||
l := logger.Get()
|
||||
l.Info("Сброс пароля по токену")
|
||||
|
||||
passwordReset, err := s.accountRepo.GetPasswordResetByToken(token)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrResetTokenNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Проверяем срок действия токена
|
||||
if passwordReset.ExpiresAt.Before(time.Now()) {
|
||||
return ErrResetTokenExpired
|
||||
}
|
||||
|
||||
// Проверяем, не использован ли токен
|
||||
if passwordReset.Used {
|
||||
return ErrResetTokenAlreadyUsed
|
||||
}
|
||||
|
||||
// Получаем пользователя
|
||||
account, err := s.accountRepo.GetByID(passwordReset.AccountID)
|
||||
if err != nil {
|
||||
return ErrAccountNotFound
|
||||
}
|
||||
|
||||
// Хешируем новый пароль
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
l.Error("Ошибка хеширования пароля", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Обновляем пароль
|
||||
account.PasswordHash = string(hashedPassword)
|
||||
if err := s.accountRepo.Update(account); err != nil {
|
||||
l.Error("Ошибка обновления пароля", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// Помечаем токен как использованный
|
||||
passwordReset.Used = true
|
||||
if err := s.accountRepo.UpdatePasswordReset(passwordReset); err != nil {
|
||||
l.Error("Ошибка обновления статуса токена", zap.Error(err))
|
||||
}
|
||||
|
||||
l.Info("Пароль успешно сброшен", zap.Uint("userID", account.ID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListAccounts возвращает список аккаунтов с пагинацией
|
||||
func (s *serviceImpl) ListAccounts(req ListAccountsRequest) (*AccountListResponse, error) {
|
||||
l := logger.Get()
|
||||
l.Debug("Получение списка аккаунтов", zap.Any("request", req))
|
||||
|
||||
// Устанавливаем значения по умолчанию
|
||||
if req.Page == 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.PageSize == 0 {
|
||||
req.PageSize = 20
|
||||
}
|
||||
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
|
||||
var accounts []models.Account
|
||||
var total int64
|
||||
var err error
|
||||
|
||||
// Поиск с фильтрацией
|
||||
if req.Search != "" {
|
||||
accounts, err = s.accountRepo.Search(req.Search, offset, req.PageSize)
|
||||
if err != nil {
|
||||
l.Error("Ошибка поиска аккаунтов", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
total, err = s.getSearchTotal(req.Search)
|
||||
} else {
|
||||
accounts, err = s.accountRepo.List(offset, req.PageSize)
|
||||
if err != nil {
|
||||
l.Error("Ошибка получения списка аккаунтов", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
total, err = s.accountRepo.Count()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Фильтруем по роли и статусу (если нужно)
|
||||
filteredAccounts := s.filterAccounts(accounts, req.Role, req.IsActive)
|
||||
|
||||
items := make([]AccountResponse, len(filteredAccounts))
|
||||
for i, acc := range filteredAccounts {
|
||||
items[i] = ToAccountResponse(&acc)
|
||||
}
|
||||
|
||||
totalPages := int(total) / req.PageSize
|
||||
if int(total)%req.PageSize > 0 {
|
||||
totalPages++
|
||||
}
|
||||
|
||||
return &AccountListResponse{
|
||||
Items: items,
|
||||
Total: total,
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
TotalPages: totalPages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// VerifyAccount верифицирует аккаунт
|
||||
func (s *serviceImpl) VerifyAccount(accountID uint, req VerifyAccountRequest) error {
|
||||
l := logger.Get()
|
||||
l.Info("Верификация аккаунта", zap.Uint("id", accountID))
|
||||
|
||||
account, err := s.accountRepo.GetByID(accountID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrAccountNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
account.IsVerified = req.IsVerified
|
||||
if err := s.accountRepo.Update(account); err != nil {
|
||||
l.Error("Ошибка обновления статуса верификации", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
l.Info("Аккаунт верифицирован", zap.Uint("id", accountID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateAccountStatus обновляет статус аккаунта (админ)
|
||||
func (s *serviceImpl) UpdateAccountStatus(accountID uint, req UpdateAccountStatusRequest) error {
|
||||
l := logger.Get()
|
||||
l.Info("Обновление статуса аккаунта", zap.Uint("id", accountID))
|
||||
|
||||
account, err := s.accountRepo.GetByID(accountID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return ErrAccountNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
account.IsActive = req.IsActive
|
||||
if req.Role != "" {
|
||||
account.Role = req.Role
|
||||
}
|
||||
|
||||
if err := s.accountRepo.Update(account); err != nil {
|
||||
l.Error("Ошибка обновления статуса аккаунта", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
l.Info("Статус аккаунта обновлен", zap.Uint("id", accountID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAccountStats получает статистику аккаунта
|
||||
func (s *serviceImpl) GetAccountStats(userID uint) (*AccountStats, error) {
|
||||
l := logger.Get()
|
||||
l.Debug("Получение статистики аккаунта", zap.Uint("userID", userID))
|
||||
|
||||
// Здесь должна быть реальная логика подсчета статистики
|
||||
// В реальном приложении нужно запрашивать данные из соответствующих репозиториев
|
||||
|
||||
stats := &AccountStats{
|
||||
ObjectsCount: 0,
|
||||
FeedbacksCount: 0,
|
||||
CommentsCount: 0,
|
||||
RatingsCount: 0,
|
||||
AppealsCount: 0,
|
||||
}
|
||||
|
||||
// TODO: Получить реальную статистику из базы данных
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// GetAccountProfile получает профиль пользователя со статистикой
|
||||
func (s *serviceImpl) GetAccountProfile(userID uint) (*AccountProfileResponse, error) {
|
||||
l := logger.Get()
|
||||
l.Debug("Получение профиля пользователя", zap.Uint("userID", userID))
|
||||
|
||||
account, err := s.accountRepo.GetByID(userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrAccountNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stats, err := s.GetAccountStats(userID)
|
||||
if err != nil {
|
||||
l.Error("Ошибка получения статистики", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AccountProfileResponse{
|
||||
AccountResponse: ToAccountResponse(account),
|
||||
Stats: *stats,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAccountModelByID получает модель аккаунта по ID (для внутреннего использования)
|
||||
func (s *serviceImpl) GetAccountModelByID(id uint) (*models.Account, error) {
|
||||
return s.accountRepo.GetByID(id)
|
||||
}
|
||||
|
||||
// GetAccountModelByEmail получает модель аккаунта по email (для внутреннего использования)
|
||||
func (s *serviceImpl) GetAccountModelByEmail(email string) (*models.Account, error) {
|
||||
return s.accountRepo.GetByEmail(email)
|
||||
}
|
||||
|
||||
// CreateAccount создает новый аккаунт
|
||||
func (s *serviceImpl) CreateAccount(req CreateAccountRequest) (*models.Account, error) {
|
||||
l := logger.Get()
|
||||
l.Info("Создание аккаунта", zap.String("email", req.Email))
|
||||
|
||||
// Проверяем, существует ли пользователь
|
||||
existing, _ := s.accountRepo.GetByEmail(req.Email)
|
||||
if existing != nil {
|
||||
return nil, ErrAccountAlreadyExists
|
||||
}
|
||||
|
||||
// Хешируем пароль
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
l.Error("Ошибка хеширования пароля", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Формируем полное имя, если не указано
|
||||
fullName := req.FullName
|
||||
if fullName == "" {
|
||||
fullName = req.FirstName + " " + req.LastName
|
||||
}
|
||||
|
||||
account := &models.Account{
|
||||
Email: req.Email,
|
||||
PasswordHash: string(hashedPassword),
|
||||
FullName: fullName,
|
||||
FirstName: req.FirstName,
|
||||
LastName: req.LastName,
|
||||
Phone: req.Phone,
|
||||
City: req.City,
|
||||
OrganizationForm: req.OrganizationForm,
|
||||
OrganizationName: req.OrganizationName,
|
||||
OrganizationShort: req.OrganizationShort,
|
||||
INN: req.INN,
|
||||
PersonalINN: req.PersonalINN,
|
||||
IsActive: true,
|
||||
IsVerified: false,
|
||||
Role: "user",
|
||||
}
|
||||
|
||||
if err := s.accountRepo.Create(account); err != nil {
|
||||
l.Error("Ошибка создания аккаунта", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.Info("Аккаунт создан", zap.String("email", req.Email))
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// UpdateAccountModel обновляет модель аккаунта
|
||||
func (s *serviceImpl) UpdateAccountModel(account *models.Account) error {
|
||||
return s.accountRepo.Update(account)
|
||||
}
|
||||
|
||||
// Вспомогательные методы
|
||||
|
||||
func (s *serviceImpl) getSearchTotal(query string) (int64, error) {
|
||||
// Здесь должна быть реализация подсчета общего количества результатов поиска
|
||||
// Для простоты возвращаем 0
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (s *serviceImpl) filterAccounts(accounts []models.Account, role string, isActive *bool) []models.Account {
|
||||
var filtered []models.Account
|
||||
|
||||
for _, acc := range accounts {
|
||||
if role != "" && acc.Role != role {
|
||||
continue
|
||||
}
|
||||
if isActive != nil && acc.IsActive != *isActive {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, acc)
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
// Генерация reset токена
|
||||
func generateResetToken() string {
|
||||
// В реальном приложении используйте криптографически безопасную генерацию
|
||||
return fmt.Sprintf("reset_%d_%d", time.Now().UnixNano(), time.Now().Unix())
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package account
|
||||
|
||||
// AccountType тип аккаунта
|
||||
type AccountType string
|
||||
|
||||
const (
|
||||
AccountTypeIndividual AccountType = "individual" // Физическое лицо
|
||||
AccountTypeBusiness AccountType = "business" // Юридическое лицо
|
||||
)
|
||||
|
||||
// AccountRole роль пользователя
|
||||
type AccountRole string
|
||||
|
||||
const (
|
||||
RoleUser AccountRole = "user"
|
||||
RoleModer AccountRole = "moderator"
|
||||
RoleAdmin AccountRole = "admin"
|
||||
)
|
||||
|
||||
// ValidationRules правила валидации
|
||||
type ValidationRules struct {
|
||||
MinPasswordLength int
|
||||
MaxNameLength int
|
||||
MaxPhoneLength int
|
||||
INNLength int
|
||||
}
|
||||
|
||||
// DefaultValidationRules правила валидации по умолчанию
|
||||
var DefaultValidationRules = ValidationRules{
|
||||
MinPasswordLength: 6,
|
||||
MaxNameLength: 100,
|
||||
MaxPhoneLength: 20,
|
||||
INNLength: 12,
|
||||
}
|
||||
Reference in New Issue
Block a user