Skip to content

Commit

Permalink
Merge pull request #170 from JokerTrickster/improvement/169/rank_api
Browse files Browse the repository at this point in the history
{improvement} - 랭킹api 랭킹  변동 제거 개선\n #169
  • Loading branch information
JokerTrickster authored Oct 3, 2024
2 parents 1534607 + bdaf2de commit dd83804
Show file tree
Hide file tree
Showing 13 changed files with 27 additions and 122 deletions.
3 changes: 0 additions & 3 deletions src/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1292,9 +1292,6 @@ const docTemplate = `{
},
"rank": {
"type": "integer"
},
"rankChange": {
"type": "string"
}
}
},
Expand Down
3 changes: 0 additions & 3 deletions src/docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1281,9 +1281,6 @@
},
"rank": {
"type": "integer"
},
"rankChange": {
"type": "string"
}
}
},
Expand Down
2 changes: 0 additions & 2 deletions src/docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,6 @@ definitions:
type: string
rank:
type: integer
rankChange:
type: string
type: object
response.RecommendFood:
properties:
Expand Down
1 change: 1 addition & 0 deletions src/features/food/model/entity/rankingFoodEntity.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ package entity

type RankFoodRedis struct {
FoodID string
Name string
Score float64
}
6 changes: 1 addition & 5 deletions src/features/food/model/interface/IFoodRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,8 @@ type IMetaFoodRepository interface {

type IRankingFoodRepository interface {
RankingTop(ctx context.Context) ([]*entity.RankFoodRedis, error)
PreviousRanking(ctx context.Context, food string) (int, error)
FindRankingTop10FoodHistories(ctx context.Context) ([]*entity.RankFoodRedis, error)
FindRankingFoodHistories(ctx context.Context) ([]*entity.RankFoodRedis, error)
IncrementFoodRanking(ctx context.Context, redisKey string, foodName string, score float64) error
PreviousRankingExist(ctx context.Context) (int, error)
FindOneFoods(ctx context.Context, foodID int) (string, error)
ExpireRanking(ctx context.Context, key string) error
}

type IImageUploadFoodRepository interface {
Expand Down
5 changes: 2 additions & 3 deletions src/features/food/model/response/rankingFood.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ type ResRankingFood struct {
}

type RankFood struct {
Rank int `json:"rank"`
Name string `json:"name"`
RankChange string `json:"rankChange"`
Rank int `json:"rank"`
Name string `json:"name"`
}
68 changes: 9 additions & 59 deletions src/features/food/repository/rankingFoodRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@ package repository

import (
"context"
"fmt"
"main/features/food/model/entity"
_interface "main/features/food/model/interface"
"main/utils"
"main/utils/db/mysql"
_redis "main/utils/db/redis"
"strconv"
"time"

"github.com/redis/go-redis/v9"
"gorm.io/gorm"
Expand All @@ -31,40 +27,26 @@ func (g *RankingFoodRepository) RankingTop(ctx context.Context) ([]*entity.RankF
result := []*entity.RankFoodRedis{}
for _, z := range currentRankings {
rankFood := &entity.RankFoodRedis{
FoodID: z.Member.(string),
Score: z.Score,
Name: z.Member.(string),
Score: z.Score,
}
result = append(result, rankFood)
}

return result, nil
}

func (g *RankingFoodRepository) PreviousRanking(ctx context.Context, food string) (int, error) {
// Get previous ranking of food
previousRank, err := _redis.Client.ZRevRank(ctx, _redis.PrevRankingKey, food).Result()
if err != nil {
if err == redis.Nil {
return _redis.NewRank, nil
}
return 0, utils.ErrorMsg(ctx, utils.ErrInternalDB, utils.Trace(), utils.HandleError(err.Error(), food), utils.ErrFromRedis)
}

return int(previousRank), nil
}

func (g *RankingFoodRepository) FindRankingTop10FoodHistories(ctx context.Context) ([]*entity.RankFoodRedis, error) {
func (g *RankingFoodRepository) FindRankingFoodHistories(ctx context.Context) ([]*entity.RankFoodRedis, error) {
// gorm에서 food_histories 테이블에서 top 10가져오기
var results []struct {
FoodID int
Count int64
name string
Count int64
}
// SQL 쿼리 실행
err := g.GormDB.Table("food_histories").
Select("food_id, COUNT(food_id) as count").
Group("food_id").
Select("name, COUNT(name) as count").
Group("name").
Order("count DESC").
Limit(10).
Scan(&results).Error

if err != nil {
Expand All @@ -75,8 +57,8 @@ func (g *RankingFoodRepository) FindRankingTop10FoodHistories(ctx context.Contex
topFoods := make([]*entity.RankFoodRedis, 0)
for _, r := range results {
topFoods = append(topFoods, &entity.RankFoodRedis{
FoodID: strconv.Itoa(r.FoodID),
Score: float64(r.Count),
Name: r.name,
Score: float64(r.Count),
})
}

Expand All @@ -92,35 +74,3 @@ func (g *RankingFoodRepository) IncrementFoodRanking(ctx context.Context, redisK

return nil
}

func (g *RankingFoodRepository) PreviousRankingExist(ctx context.Context) (int, error) {
// Check if previous ranking exists
previousExist, err := _redis.Client.Exists(ctx, _redis.PrevRankingKey).Result()
if err != nil {
return 0, utils.ErrorMsg(ctx, utils.ErrInternalDB, utils.Trace(), utils.HandleError(err.Error()), utils.ErrFromRedis)
}

return int(previousExist), nil
}

func (g *RankingFoodRepository) FindOneFoods(ctx context.Context, foodID int) (string, error) {
// Find food name by food ID
var foodName string
fmt.Println(foodID)
err := g.GormDB.WithContext(ctx).Model(&mysql.Foods{}).Select("name").Where("id = ?", foodID).First(&foodName).Error
if err != nil {
return "", utils.ErrorMsg(ctx, utils.ErrInternalDB, utils.Trace(), utils.HandleError(err.Error(), foodID), utils.ErrFromMysqlDB)
}

return foodName, nil
}

func (g *RankingFoodRepository) ExpireRanking(ctx context.Context, key string) error {
// Set expiration time for key
err := g.RedisClient.Expire(ctx, key, 30*time.Minute).Err() // RedisClient는 Redis 연결을 나타내는 변수입니다.
if err != nil {
return utils.ErrorMsg(ctx, utils.ErrInternalDB, utils.Trace(), utils.HandleError(err.Error(), key), utils.ErrFromRedis)
}

return nil
}
6 changes: 3 additions & 3 deletions src/features/food/repository/selectFoodRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ func (g *SelectFoodRepository) InsertOneFoodHistory(ctx context.Context, foodHis
return nil
}

func (g *SelectFoodRepository) IncrementFoodRanking(ctx context.Context, foodID string, score float64) error {
func (g *SelectFoodRepository) IncrementFoodRanking(ctx context.Context, name string, score float64) error {
redisKey := _redis.RankingKey
_, err := g.RedisClient.ZAdd(ctx, redisKey, redis.Z{Score: score, Member: foodID}).Result()
_, err := g.RedisClient.ZIncrBy(ctx, redisKey, score, name).Result()
if err != nil {
return utils.ErrorMsg(ctx, utils.ErrInternalDB, utils.Trace(), utils.HandleError(_errors.ErrServerError.Error()+err.Error(), foodID), utils.ErrFromRedis)
return utils.ErrorMsg(ctx, utils.ErrInternalDB, utils.Trace(), utils.HandleError(_errors.ErrServerError.Error()+err.Error(), name), utils.ErrFromRedis)
}
return nil
}
41 changes: 3 additions & 38 deletions src/features/food/usecase/rankingFoodUseCase.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package usecase

import (
"context"
"strconv"

"main/features/food/model/entity"
_interface "main/features/food/model/interface"
Expand Down Expand Up @@ -32,63 +31,29 @@ func (d *RankingFoodUseCase) Ranking(c context.Context) (response.ResRankingFood
//현재 랭킹 데이터가 비어 있다면
if len(currentRankings) == 0 {
//rdb에서 데이터를 가져와 현재 랭킹에 저장을 한다.
currentRankings, err = d.Repository.FindRankingTop10FoodHistories(ctx)
currentRankings, err = d.Repository.FindRankingFoodHistories(ctx)
if err != nil {
return response.ResRankingFood{}, err
}
//현재 랭킹 레디스에 저장한다.
for _, food := range currentRankings {
err := d.Repository.IncrementFoodRanking(ctx, _redis.RankingKey, food.FoodID, 1)
err := d.Repository.IncrementFoodRanking(ctx, _redis.RankingKey, food.Name, food.Score)
if err != nil {
return response.ResRankingFood{}, err
}
}
}

// 이전 데이터가 비어 있다면 현재 랭킹 데이터를 저장한다.
previousExist, err := d.Repository.PreviousRankingExist(ctx)
if err != nil {
return response.ResRankingFood{}, err
}
if previousExist == 0 {
//이전 데이터가 없다면 현재 랭킹 데이터를 이전 랭킹 데이터로 저장한다.
for _, food := range currentRankings {
err := d.Repository.IncrementFoodRanking(ctx, _redis.PrevRankingKey, food.FoodID, food.Score)
if err != nil {
return response.ResRankingFood{}, err
}
}
//이전 데이터 만료시간을 설정한다.
err := d.Repository.ExpireRanking(ctx, _redis.PrevRankingKey)
if err != nil {
return response.ResRankingFood{}, err
}
}
//이전 데이터가 있다면 랭킹 변동을 계산한다.
res := response.ResRankingFood{}
for i, food := range currentRankings {
if i == 10 {
break
}
rank := i + 1
previousRank, err := d.Repository.PreviousRanking(ctx, food.FoodID)
if err != nil {
return response.ResRankingFood{}, err
}
fID, _ := strconv.Atoi(food.FoodID)
foodName, err := d.Repository.FindOneFoods(ctx, fID)
if err != nil {
return response.ResRankingFood{}, err
}
rankFood := response.RankFood{
Rank: rank,
Name: foodName,
}
if previousRank == _redis.NewRank {
rankFood.RankChange = "new"
} else {
rankChange := previousRank - rank
rankFood.RankChange = strconv.Itoa(rankChange)
Name: food.Name,
}

res.Foods = append(res.Foods, rankFood)
Expand Down
5 changes: 2 additions & 3 deletions src/features/food/usecase/selectFoodUseCase.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package usecase
import (
"context"
"encoding/json"
"strconv"
"strings"

"main/features/food/model/entity"
Expand Down Expand Up @@ -39,7 +38,7 @@ func (d *SelectFoodUseCase) Select(c context.Context, e entity.SelectFoodEntity)
foodDTO.ID = foodID

//디비에 저장한다.
foodHistoryDTO := CreateFoodHistoryDTO(foodID, e.UserID)
foodHistoryDTO := CreateFoodHistoryDTO(foodID, e.UserID, e.Name)
if err := d.Repository.InsertOneFoodHistory(ctx, foodHistoryDTO); err != nil {
return response.ResSelectFood{}, err
}
Expand Down Expand Up @@ -88,7 +87,7 @@ func (d *SelectFoodUseCase) Select(c context.Context, e entity.SelectFoodEntity)
}
//레디스 저장한다.

if err := d.Repository.IncrementFoodRanking(ctx, strconv.Itoa(int(foodDTO.ID)), 1); err != nil {
if err := d.Repository.IncrementFoodRanking(ctx, foodDTO.Name, 1); err != nil {
return response.ResSelectFood{}, err
}

Expand Down
3 changes: 2 additions & 1 deletion src/features/food/usecase/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,11 @@ func CreateSelectFoodDTO(entity entity.SelectFoodEntity) *mysql.Foods {
Name: entity.Name,
}
}
func CreateFoodHistoryDTO(foodID, userID uint) *mysql.FoodHistory {
func CreateFoodHistoryDTO(foodID, userID uint, name string) *mysql.FoodHistory {
return &mysql.FoodHistory{
FoodID: foodID,
UserID: userID,
Name: name,
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/utils/db/mysql/gormDB.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ type Foods struct {
}
type FoodHistory struct {
gorm.Model
UserID uint `json:"userID" gorm:"column:user_id"`
FoodID uint `json:"foodID" gorm:"column:food_id"`
UserID uint `json:"userID" gorm:"column:user_id"`
FoodID uint `json:"foodID" gorm:"column:food_id"`
Name string `json:"name" gorm:"column:name"`
}

type MetaTables struct {
Expand Down
1 change: 1 addition & 0 deletions src/utils/db/mysql/table.sql
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ CREATE TABLE food_histories (
deleted_at TIMESTAMP NULL DEFAULT NULL,
food_id INT,
user_id INT,
name varchar(255),
FOREIGN KEY (food_id) REFERENCES foods(id)
);

Expand Down

0 comments on commit dd83804

Please sign in to comment.