Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into feat/cd-pipeline-with-github-actions
Browse files Browse the repository at this point in the history
  • Loading branch information
mokhs00 committed Dec 1, 2024
2 parents 3e148de + 9e35028 commit eae7487
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 59 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/google/uuid v1.5.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
Expand Down
21 changes: 13 additions & 8 deletions pkg/fx/fiberfx/config.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package fiberfx

import (
"github.com/kelseyhightower/envconfig"
"github.com/pkg/errors"
)

type Config struct {
Port int `envconfig:"PORT" default:"8080"`
CORSAllowOrigins string `envconfig:"CORS_ALLOW_ORIGINS" default:"*"`
CORSAllowMethods string `envconfig:"CORS_ALLOW_METHODS" default:"GET,POST,HEAD,PUT,DELETE,PATCH"`
CORSAllowHeaders string `envconfig:"CORS_ALLOW_HEADERS" default:""`
CORSAllowHeaders string `envconfig:"CORS_ALLOW_HEADERS" default:"*"`
CORSAllowCredentials bool `envconfig:"CORS_ALLOW_CREDENTIALS" default:"false"`
}

func parseConfig() *Config {
return &Config{
Port: 8080,
CORSAllowOrigins: "*",
CORSAllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH",
CORSAllowHeaders: "*",
CORSAllowCredentials: false,
func parseConfig() (*Config, error) {
var config Config

if err := envconfig.Process("", &config); err != nil {
return nil, errors.New("failed to parse config from environment variables")
}

return &config, nil
}
40 changes: 20 additions & 20 deletions pkg/fx/gormfx/config.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
package gormfx

import (
"github.com/kelseyhightower/envconfig"
"github.com/pkg/errors"
)

type Config struct {
DBDriver string `envconfig:"DB_DRIVER" default:""`
DBUser string `envconfig:"DB_USER" default:""`
DBPassword string `envconfig:"DB_PASSWORD" default:""`
DBHost string `envconfig:"DB_HOST" default:""`
DBName string `envconfig:"DB_NAME" default:""`
DBPort string `envconfig:"DB_PORT" default:""`
ConnMaxLifetimeSeconds int64 `envconfig:"DB_CONN_MAX_LIFETIME_SECONDS" default:"0"`
MaxIdleConns int `envconfig:"DB_MAX_IDLE_CONNS" default:"0"`
MaxOpenConns int `envconfig:"DB_MAX_OPEN_CONNS" default:"0"`
DBDriver string `envconfig:"DB_DRIVER" default:"mysql"`
DBUser string `envconfig:"DB_USER" default:"root"`
DBPassword string `envconfig:"DB_PASSWORD" default:"password"`
DBHost string `envconfig:"DB_HOST" default:"localhost"`
DBName string `envconfig:"DB_NAME" default:"dittodining"`
DBPort string `envconfig:"DB_PORT" default:"3306"`
ConnMaxLifetimeSeconds int64 `envconfig:"DB_CONN_MAX_LIFETIME_SECONDS" default:"1800"`
MaxIdleConns int `envconfig:"DB_MAX_IDLE_CONNS" default:"10"` // TODO: DB 스펙에 따라 적절치로 조정
MaxOpenConns int `envconfig:"DB_MAX_OPEN_CONNS" default:"10"`
}

func parseConfig() *Config {
return &Config{
DBDriver: "mysql",
DBUser: "root",
DBPassword: "password",
DBHost: "localhost",
DBName: "dittodining",
DBPort: "3306",
ConnMaxLifetimeSeconds: 1800,
MaxIdleConns: 10, // TODO: DB 스펙에 따라 적절치로 조정
MaxOpenConns: 10,
func parseConfig() (*Config, error) {
var config Config
if err := envconfig.Process("", &config); err != nil {
return nil, errors.New("failed to parse config from environment variables")
}

return &config, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var ErrRestaurantRecommendationNotFound = errors.New("restaurant recommendation

type RestaurantRecommendationRepository interface {
FindAllByRestaurantRecommendationRequestID(ctx context.Context, db *gorm.DB, restaurantRecommendationRequestID int64, cursorRestaurantRecommendationRequestID *int64, limit *int64) ([]*recommendation.RestaurantRecommendation, error)
FindLastOneByRestaurantRecommendationRequestID(ctx context.Context, db *gorm.DB, restaurantRecommendationRequestID int64) (*recommendation.RestaurantRecommendation, error)
FindAllByIDs(ctx context.Context, db *gorm.DB, restaurantRecommendationIDs []int64) ([]*recommendation.RestaurantRecommendation, error)
FindByID(ctx context.Context, db *gorm.DB, restaurantRecommendationID int64) (*recommendation.RestaurantRecommendation, error)
SaveAll(ctx context.Context, db *gorm.DB, recommendations []*recommendation.RestaurantRecommendation) error
Expand Down Expand Up @@ -109,3 +110,24 @@ func (r *restaurantRecommendationRepository) SaveAll(

return nil
}

func (r *restaurantRecommendationRepository) FindLastOneByRestaurantRecommendationRequestID(ctx context.Context, db *gorm.DB, restaurantRecommendationRequestID int64) (*recommendation.RestaurantRecommendation, error) {
var existingRecommendation recommendation.RestaurantRecommendation

result := db.
Where(&recommendation.RestaurantRecommendation{
RestaurantRecommendationRequestID: restaurantRecommendationRequestID,
}).
Order("restaurant_recommendation_id DESC").
First(&existingRecommendation)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, ErrRestaurantRecommendationNotFound
}

return nil, result.Error
}

return &existingRecommendation, nil

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ func NewRestaurantRecommendationRequestRepository() RestaurantRecommendationRequ
return &restaurantRecommendationRequestRepository{}
}

type restaurantRecommendationRequestRepository struct {
db *gorm.DB
}
type restaurantRecommendationRequestRepository struct{}

func (r *restaurantRecommendationRequestRepository) Save(ctx context.Context, db *gorm.DB, request *recommendation.RestaurantRecommendationRequest) (*recommendation.RestaurantRecommendationRequest, error) {
result := db.Create(request)
Expand All @@ -34,7 +32,7 @@ func (r *restaurantRecommendationRequestRepository) Save(ctx context.Context, db
func (r *restaurantRecommendationRequestRepository) FindByID(ctx context.Context, db *gorm.DB, restaurantRecommendationRequestID int64) (*recommendation.RestaurantRecommendationRequest, error) {
var request *recommendation.RestaurantRecommendationRequest

result := r.db.Where(recommendation.RestaurantRecommendationRequest{
result := db.Where(recommendation.RestaurantRecommendationRequest{
RestaurantRecommendationRequestID: restaurantRecommendationRequestID,
}).Find(&request)
if result.Error != nil {
Expand Down
50 changes: 45 additions & 5 deletions server/repository/restaurant/restaurant_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package restaurant

import (
"context"
"fmt"
recommendation_domain "github.com/BOAZ-LKVK/LKVK-server/server/domain/recommendation"
"github.com/BOAZ-LKVK/LKVK-server/server/domain/restaurant"
"github.com/pkg/errors"
"gorm.io/gorm"
Expand All @@ -12,7 +14,14 @@ var ErrRestaurantNotFound = errors.New("restaurant not found")
type RestaurantRepository interface {
FindByID(ctx context.Context, db *gorm.DB, restaurantID int64) (*restaurant.Restaurant, error)
FindByIDs(ctx context.Context, db *gorm.DB, restaurantIDs []int64) ([]*restaurant.Restaurant, error)
FindAllOrderByRecommendationScoreDesc(ctx context.Context, db *gorm.DB, limit int) ([]*restaurant.Restaurant, error)
FindNearbyAllOrderByRecommendationScoreDesc(
ctx context.Context,
db *gorm.DB,
userLocation recommendation_domain.UserLocation,
minutes int64,
cursorRecommendationScore *int64,
limit *int64,
) ([]*restaurant.Restaurant, error)
}

func NewRestaurantRepository() RestaurantRepository {
Expand Down Expand Up @@ -51,11 +60,42 @@ func (r *restaurantRepository) FindByIDs(ctx context.Context, db *gorm.DB, resta
return existingRestaurants, nil
}

func (r *restaurantRepository) FindAllOrderByRecommendationScoreDesc(ctx context.Context, db *gorm.DB, limit int) ([]*restaurant.Restaurant, error) {
func (r *restaurantRepository) FindNearbyAllOrderByRecommendationScoreDesc(
ctx context.Context,
db *gorm.DB,
userLocation recommendation_domain.UserLocation,
minutes int64,
cursorRecommendationScore *int64,
limit *int64,
) ([]*restaurant.Restaurant, error) {
// 이동 반경 계산 (80m/min × 시간)
radius := 80 * minutes

// 거리 계산 SQL 표현식
distanceExpr := fmt.Sprintf(
"ST_Distance_Sphere(POINT(longitude, latitude), POINT(%f, %f))",
userLocation.Longitude.InexactFloat64(), userLocation.Latitude.InexactFloat64(),
)

queryBuilder := db.
Model(&restaurant.Restaurant{}).
Select("*, "+distanceExpr+" AS distance").
Where(distanceExpr+" <= ?", radius)

if cursorRecommendationScore != nil {
queryBuilder.Where("recommendation_score < ?", cursorRecommendationScore)
}

if limit != nil {
queryBuilder.Limit(int(*limit))
} else {
// default limit
queryBuilder.Limit(10)
}

var existingRestaurants []*restaurant.Restaurant
result := db.
Order("recommendation_score DESC").
Limit(limit).
result := queryBuilder.
Order("recommendation_score DESC, distance ASC").
Find(&existingRestaurants)
if result.Error != nil {
return nil, result.Error
Expand Down
109 changes: 87 additions & 22 deletions server/service/recommendation/restaurant_recommendation_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"time"
)

const minutesToFindNearbyRestaurants = 15

type RestaurantRecommendationService interface {
RequestRestaurantRecommendation(ctx context.Context, userID *int64, userLocation recommendation_domain.UserLocation, now time.Time) (*recommendation_model.RequestRestaurantRecommendationResult, error)
GetRestaurantRecommendationRequest(ctx context.Context, restaurantRecommendationRequestID int64) (*recommendation_domain.RestaurantRecommendationRequest, error)
Expand Down Expand Up @@ -76,25 +78,15 @@ func (s *restaurantRecommendationService) RequestRestaurantRecommendation(ctx co
}
createdRecommendationRequest = created

restaurantsOrderByRecommendationScoreDesc, err := s.restaurantRepository.FindAllOrderByRecommendationScoreDesc(ctx, s.db, 10)
if err != nil {
return err
}

recommendations := make([]*recommendation_domain.RestaurantRecommendation, 0, len(restaurantsOrderByRecommendationScoreDesc))
for _, r := range restaurantsOrderByRecommendationScoreDesc {
distanceInMeters := location.CalculateDistanceInMeters(userLocation.Latitude, userLocation.Longitude, r.Latitude, r.Longitude)

recommendations = append(recommendations,
recommendation_domain.NewRestaurantRecommendation(
recommendationRequest.RestaurantRecommendationRequestID,
r.RestaurantID,
distanceInMeters,
),
)
}

if err := s.restaurantRecommendationRepository.SaveAll(ctx, s.db, recommendations); err != nil {
if _, err := s.createRecommendations(
ctx,
tx,
userLocation,
recommendationRequest.RestaurantRecommendationRequestID,
minutesToFindNearbyRestaurants,
nil,
nil,
); err != nil {
return err
}

Expand All @@ -108,12 +100,58 @@ func (s *restaurantRecommendationService) RequestRestaurantRecommendation(ctx co
}, nil
}

func (s *restaurantRecommendationService) createRecommendations(
ctx context.Context,
tx *gorm.DB,
userLocation recommendation_domain.UserLocation,
restaurantRecommendationRequestID int64,
minutes int64,
cursorRecommendationScore *int64,
limit *int64,
) ([]*recommendation_domain.RestaurantRecommendation, error) {
restaurantsOrderByRecommendationScoreDesc, err := s.restaurantRepository.FindNearbyAllOrderByRecommendationScoreDesc(
ctx,
tx,
userLocation,
minutes,
cursorRecommendationScore,
limit,
)
if err != nil {
return nil, err
}

recommendations := make([]*recommendation_domain.RestaurantRecommendation, 0, len(restaurantsOrderByRecommendationScoreDesc))
for _, r := range restaurantsOrderByRecommendationScoreDesc {
distanceInMeters := location.CalculateDistanceInMeters(userLocation.Latitude, userLocation.Longitude, r.Latitude, r.Longitude)

recommendations = append(recommendations,
recommendation_domain.NewRestaurantRecommendation(
restaurantRecommendationRequestID,
r.RestaurantID,
distanceInMeters,
),
)
}

if err := s.restaurantRecommendationRepository.SaveAll(ctx, s.db, recommendations); err != nil {
return nil, err
}

return recommendations, nil
}

func (s *restaurantRecommendationService) GetRestaurantRecommendationRequest(ctx context.Context, restaurantRecommendationRequestID int64) (*recommendation_domain.RestaurantRecommendationRequest, error) {
return s.restaurantRecommendationRequestRepository.FindByID(ctx, s.db, restaurantRecommendationRequestID)
}

func (s *restaurantRecommendationService) ListRecommendedRestaurants(ctx context.Context, restaurantRecommendationRequestID int64, cursorRestaurantRecommendationID *int64, limit *int64) (*recommendation_model.ListRecommendedRestaurantsResult, error) {
_, err := s.GetRestaurantRecommendationRequest(ctx, restaurantRecommendationRequestID)
func (s *restaurantRecommendationService) ListRecommendedRestaurants(
ctx context.Context,
restaurantRecommendationRequestID int64,
cursorRestaurantRecommendationID *int64,
limit *int64,
) (*recommendation_model.ListRecommendedRestaurantsResult, error) {
recommendationRequest, err := s.GetRestaurantRecommendationRequest(ctx, restaurantRecommendationRequestID)
if err != nil {
return nil, err
}
Expand All @@ -129,7 +167,34 @@ func (s *restaurantRecommendationService) ListRecommendedRestaurants(ctx context
return nil, err
}

// TODO: if recommendations == nil -> 추천 데이터 더 추가하도록
if len(recommendations) == 0 {
lastRecommendation, err := s.restaurantRecommendationRepository.FindLastOneByRestaurantRecommendationRequestID(
ctx,
s.db,
restaurantRecommendationRequestID,
)
if err != nil {
return nil, err
}

lastRecommendedRestaurant, err := s.restaurantRepository.FindByID(ctx, s.db, lastRecommendation.RestaurantID)
if err != nil {
return nil, err
}

recommendations, err = s.createRecommendations(
ctx,
s.db,
recommendationRequest.UserLocation,
recommendationRequest.RestaurantRecommendationRequestID,
minutesToFindNearbyRestaurants,
lo.ToPtr(lastRecommendedRestaurant.RecommendationScore.IntPart()),
limit,
)
if err != nil {
return nil, err
}
}

restaurantIDs := lo.Map(recommendations, func(item *recommendation_domain.RestaurantRecommendation, index int) int64 {
return item.RestaurantID
Expand Down

0 comments on commit eae7487

Please sign in to comment.