179 lines
4.1 KiB
Go
179 lines
4.1 KiB
Go
package socialrating
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
|
|
"social-raiting.nekiiinkognito.ru/internal/models"
|
|
)
|
|
|
|
type Handler struct {
|
|
Service Service
|
|
}
|
|
|
|
type ChangeRequest struct {
|
|
TargetUserID uint `json:"targetUserId" binding:"required"`
|
|
Amount int `json:"amount"`
|
|
Reason string `json:"reason"`
|
|
Source string `json:"source"`
|
|
}
|
|
|
|
type ChangeResponse struct {
|
|
Operation models.SocialRatingOperation `json:"operation"`
|
|
CurrentRating models.UserSocialRating `json:"currentRating"`
|
|
}
|
|
|
|
type UsersResponse struct {
|
|
Users []UserWithRating `json:"users"`
|
|
}
|
|
|
|
type UserRatingResponse struct {
|
|
User UserWithRating `json:"user"`
|
|
}
|
|
|
|
type HistoryResponse struct {
|
|
Operations []models.SocialRatingOperation `json:"operations"`
|
|
}
|
|
|
|
func NewHandler(service Service) Handler {
|
|
return Handler{Service: service}
|
|
}
|
|
|
|
func (h Handler) ListUsers(ctx *gin.Context) {
|
|
limit := parsePositiveLimit(ctx.Query("limit"), 50)
|
|
|
|
users, err := h.Service.ListUsersWithRatings(limit)
|
|
if err != nil {
|
|
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load users"})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, UsersResponse{Users: users})
|
|
}
|
|
|
|
func (h Handler) GetUser(ctx *gin.Context) {
|
|
userID, err := parseUintParam(ctx.Param("userId"))
|
|
if err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
user, err := h.Service.GetUserRating(userID)
|
|
if err != nil {
|
|
handleApplyChangeError(ctx, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, UserRatingResponse{User: user})
|
|
}
|
|
|
|
func (h Handler) GetUserHistory(ctx *gin.Context) {
|
|
userID, err := parseUintParam(ctx.Param("userId"))
|
|
if err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
limit := parsePositiveLimit(ctx.Query("limit"), 50)
|
|
operations, err := h.Service.GetUserHistory(userID, limit)
|
|
if err != nil {
|
|
handleApplyChangeError(ctx, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, HistoryResponse{Operations: operations})
|
|
}
|
|
|
|
func (h Handler) GetRecentOperations(ctx *gin.Context) {
|
|
limit := parsePositiveLimit(ctx.Query("limit"), 50)
|
|
|
|
operations, err := h.Service.GetRecentOperations(limit)
|
|
if err != nil {
|
|
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load operations"})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, HistoryResponse{Operations: operations})
|
|
}
|
|
|
|
func (h Handler) Increase(ctx *gin.Context) {
|
|
h.applySignedChange(ctx, "increase", 1)
|
|
}
|
|
|
|
func (h Handler) Decrease(ctx *gin.Context) {
|
|
h.applySignedChange(ctx, "decrease", -1)
|
|
}
|
|
|
|
func (h Handler) applySignedChange(ctx *gin.Context, operationType string, direction int) {
|
|
var req ChangeRequest
|
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
actorUserID, ok := getActorUserID(ctx)
|
|
if !ok {
|
|
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "missing user context"})
|
|
return
|
|
}
|
|
|
|
amount := req.Amount
|
|
if amount == 0 {
|
|
amount = 1
|
|
}
|
|
if amount < 0 {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "amount must be greater than zero"})
|
|
return
|
|
}
|
|
|
|
operation, currentRating, err := h.Service.ApplyChange(ChangeInput{
|
|
TargetUserID: req.TargetUserID,
|
|
ActorUserID: &actorUserID,
|
|
Delta: amount * direction,
|
|
OperationType: operationType,
|
|
Reason: req.Reason,
|
|
Source: req.Source,
|
|
})
|
|
if err != nil {
|
|
handleApplyChangeError(ctx, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, ChangeResponse{
|
|
Operation: operation,
|
|
CurrentRating: currentRating,
|
|
})
|
|
}
|
|
|
|
func getActorUserID(ctx *gin.Context) (uint, bool) {
|
|
value, exists := ctx.Get("userID")
|
|
if !exists {
|
|
return 0, false
|
|
}
|
|
|
|
userID, ok := value.(uint)
|
|
return userID, ok
|
|
}
|
|
|
|
func handleApplyChangeError(ctx *gin.Context, err error) {
|
|
switch {
|
|
case errors.Is(err, gorm.ErrRecordNotFound):
|
|
ctx.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
|
default:
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
}
|
|
}
|
|
|
|
func parseUintParam(raw string) (uint, error) {
|
|
value, err := strconv.ParseUint(raw, 10, 64)
|
|
if err != nil || value == 0 {
|
|
return 0, errors.New("invalid user id")
|
|
}
|
|
|
|
return uint(value), nil
|
|
}
|