325 lines
11 KiB
Go
325 lines
11 KiB
Go
|
|
package repository
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
|
||
|
|
"with_you/internal/model"
|
||
|
|
"with_you/internal/pkg/cursor"
|
||
|
|
|
||
|
|
"gorm.io/gorm"
|
||
|
|
)
|
||
|
|
|
||
|
|
type TradeRepository interface {
|
||
|
|
Create(ctx context.Context, item *model.TradeItem, imageUrls []string) error
|
||
|
|
GetByID(ctx context.Context, id string) (*model.TradeItem, error)
|
||
|
|
Update(ctx context.Context, item *model.TradeItem) error
|
||
|
|
UpdateWithImages(ctx context.Context, item *model.TradeItem, imageUrls *[]string) error
|
||
|
|
Delete(ctx context.Context, id string) error
|
||
|
|
UpdateStatus(ctx context.Context, id string, status model.TradeItemStatus) error
|
||
|
|
ListByCursor(ctx context.Context, tradeType *model.TradeType, category *model.TradeCategory, status *model.TradeItemStatus, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.TradeItem], error)
|
||
|
|
SearchByCursor(ctx context.Context, keyword string, tradeType *model.TradeType, category *model.TradeCategory, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.TradeItem], error)
|
||
|
|
ListByUserByCursor(ctx context.Context, userID string, status *model.TradeItemStatus, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.TradeItem], error)
|
||
|
|
IncrementViews(ctx context.Context, id string) error
|
||
|
|
IncrementFavorites(ctx context.Context, id string) error
|
||
|
|
DecrementFavorites(ctx context.Context, id string) error
|
||
|
|
|
||
|
|
CreateFavorite(ctx context.Context, tradeItemID, userID string) error
|
||
|
|
DeleteFavorite(ctx context.Context, tradeItemID, userID string) error
|
||
|
|
IsFavorited(ctx context.Context, tradeItemID, userID string) (bool, error)
|
||
|
|
IsFavoritedBatch(ctx context.Context, tradeItemIDs []string, userID string) (map[string]bool, error)
|
||
|
|
}
|
||
|
|
|
||
|
|
type tradeRepository struct {
|
||
|
|
db *gorm.DB
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewTradeRepository(db *gorm.DB) TradeRepository {
|
||
|
|
return &tradeRepository{db: db}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) getDB(ctx context.Context) *gorm.DB {
|
||
|
|
if tx := GetTxFromContext(ctx); tx != nil {
|
||
|
|
return tx
|
||
|
|
}
|
||
|
|
return r.db
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) Create(ctx context.Context, item *model.TradeItem, imageUrls []string) error {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
return db.Transaction(func(tx *gorm.DB) error {
|
||
|
|
if err := tx.Create(item).Error; err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
for i, url := range imageUrls {
|
||
|
|
img := model.TradeImage{
|
||
|
|
TradeItemID: item.ID,
|
||
|
|
URL: url,
|
||
|
|
SortOrder: i,
|
||
|
|
}
|
||
|
|
if err := tx.Create(&img).Error; err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) GetByID(ctx context.Context, id string) (*model.TradeItem, error) {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
var item model.TradeItem
|
||
|
|
if err := db.Preload("User").Preload("Images", func(db *gorm.DB) *gorm.DB {
|
||
|
|
return db.Order("sort_order ASC")
|
||
|
|
}).First(&item, "id = ?", id).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return &item, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) Update(ctx context.Context, item *model.TradeItem) error {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
return db.Save(item).Error
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) UpdateWithImages(ctx context.Context, item *model.TradeItem, imageUrls *[]string) error {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
return db.Transaction(func(tx *gorm.DB) error {
|
||
|
|
if err := tx.Save(item).Error; err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if imageUrls != nil {
|
||
|
|
if err := tx.Where("trade_item_id = ?", item.ID).Delete(&model.TradeImage{}).Error; err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
for i, url := range *imageUrls {
|
||
|
|
img := model.TradeImage{
|
||
|
|
TradeItemID: item.ID,
|
||
|
|
URL: url,
|
||
|
|
SortOrder: i,
|
||
|
|
}
|
||
|
|
if err := tx.Create(&img).Error; err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) Delete(ctx context.Context, id string) error {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
return db.Transaction(func(tx *gorm.DB) error {
|
||
|
|
if err := tx.Where("trade_item_id = ?", id).Delete(&model.TradeImage{}).Error; err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if err := tx.Where("trade_item_id = ?", id).Delete(&model.TradeFavorite{}).Error; err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
return tx.Delete(&model.TradeItem{}, "id = ?", id).Error
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) UpdateStatus(ctx context.Context, id string, status model.TradeItemStatus) error {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
return db.Model(&model.TradeItem{}).Where("id = ?", id).Update("status", status).Error
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) ListByCursor(ctx context.Context, tradeType *model.TradeType, category *model.TradeCategory, status *model.TradeItemStatus, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.TradeItem], error) {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
query := db.Model(&model.TradeItem{})
|
||
|
|
|
||
|
|
if tradeType != nil {
|
||
|
|
query = query.Where("trade_type = ?", *tradeType)
|
||
|
|
}
|
||
|
|
if category != nil {
|
||
|
|
query = query.Where("category = ?", *category)
|
||
|
|
}
|
||
|
|
defaultStatus := model.TradeItemActive
|
||
|
|
if status != nil {
|
||
|
|
defaultStatus = *status
|
||
|
|
}
|
||
|
|
query = query.Where("status = ?", defaultStatus)
|
||
|
|
|
||
|
|
builder := cursor.NewBuilder(query, cursor.SortByCreatedAtDesc).
|
||
|
|
WithCursor(req.Cursor, req.Direction).
|
||
|
|
WithPageSize(req.PageSize)
|
||
|
|
|
||
|
|
if builder.Error() != nil {
|
||
|
|
return cursor.NewCursorPageResult([]*model.TradeItem{}, "", "", false), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
var items []*model.TradeItem
|
||
|
|
q := builder.Build().Preload("User").Preload("Images", func(db *gorm.DB) *gorm.DB {
|
||
|
|
return db.Order("sort_order ASC")
|
||
|
|
})
|
||
|
|
if err := q.Find(&items).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
pageSize := builder.GetPageSize()
|
||
|
|
hasMore := cursor.HasMore(len(items), pageSize)
|
||
|
|
if hasMore {
|
||
|
|
items = items[:pageSize]
|
||
|
|
}
|
||
|
|
|
||
|
|
var nextCursor, prevCursor string
|
||
|
|
if len(items) > 0 {
|
||
|
|
if hasMore {
|
||
|
|
last := items[len(items)-1]
|
||
|
|
nextCursor = cursor.NewCursor(cursor.FormatTime(last.CreatedAt), last.ID, cursor.SortByCreatedAtDesc).Encode()
|
||
|
|
}
|
||
|
|
first := items[0]
|
||
|
|
prevCursor = cursor.NewCursor(cursor.FormatTime(first.CreatedAt), first.ID, cursor.SortByCreatedAtDesc).Encode()
|
||
|
|
}
|
||
|
|
|
||
|
|
return cursor.NewCursorPageResult(items, nextCursor, prevCursor, hasMore), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) SearchByCursor(ctx context.Context, keyword string, tradeType *model.TradeType, category *model.TradeCategory, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.TradeItem], error) {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
query := db.Model(&model.TradeItem{}).Where("status = ?", model.TradeItemActive)
|
||
|
|
|
||
|
|
if tradeType != nil {
|
||
|
|
query = query.Where("trade_type = ?", *tradeType)
|
||
|
|
}
|
||
|
|
if category != nil {
|
||
|
|
query = query.Where("category = ?", *category)
|
||
|
|
}
|
||
|
|
if keyword != "" {
|
||
|
|
searchPattern := "%" + keyword + "%"
|
||
|
|
query = query.Where("title LIKE ? OR content LIKE ?", searchPattern, searchPattern)
|
||
|
|
}
|
||
|
|
|
||
|
|
builder := cursor.NewBuilder(query, cursor.SortByCreatedAtDesc).
|
||
|
|
WithCursor(req.Cursor, req.Direction).
|
||
|
|
WithPageSize(req.PageSize)
|
||
|
|
|
||
|
|
if builder.Error() != nil {
|
||
|
|
return cursor.NewCursorPageResult([]*model.TradeItem{}, "", "", false), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
var items []*model.TradeItem
|
||
|
|
q := builder.Build().Preload("User").Preload("Images", func(db *gorm.DB) *gorm.DB {
|
||
|
|
return db.Order("sort_order ASC")
|
||
|
|
})
|
||
|
|
if err := q.Find(&items).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
pageSize := builder.GetPageSize()
|
||
|
|
hasMore := cursor.HasMore(len(items), pageSize)
|
||
|
|
if hasMore {
|
||
|
|
items = items[:pageSize]
|
||
|
|
}
|
||
|
|
|
||
|
|
var nextCursor, prevCursor string
|
||
|
|
if len(items) > 0 {
|
||
|
|
if hasMore {
|
||
|
|
last := items[len(items)-1]
|
||
|
|
nextCursor = cursor.NewCursor(cursor.FormatTime(last.CreatedAt), last.ID, cursor.SortByCreatedAtDesc).Encode()
|
||
|
|
}
|
||
|
|
first := items[0]
|
||
|
|
prevCursor = cursor.NewCursor(cursor.FormatTime(first.CreatedAt), first.ID, cursor.SortByCreatedAtDesc).Encode()
|
||
|
|
}
|
||
|
|
|
||
|
|
return cursor.NewCursorPageResult(items, nextCursor, prevCursor, hasMore), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) ListByUserByCursor(ctx context.Context, userID string, status *model.TradeItemStatus, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.TradeItem], error) {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
query := db.Model(&model.TradeItem{}).Where("user_id = ?", userID)
|
||
|
|
|
||
|
|
if status != nil {
|
||
|
|
query = query.Where("status = ?", *status)
|
||
|
|
}
|
||
|
|
|
||
|
|
builder := cursor.NewBuilder(query, cursor.SortByCreatedAtDesc).
|
||
|
|
WithCursor(req.Cursor, req.Direction).
|
||
|
|
WithPageSize(req.PageSize)
|
||
|
|
|
||
|
|
if builder.Error() != nil {
|
||
|
|
return cursor.NewCursorPageResult([]*model.TradeItem{}, "", "", false), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
var items []*model.TradeItem
|
||
|
|
q := builder.Build().Preload("User").Preload("Images", func(db *gorm.DB) *gorm.DB {
|
||
|
|
return db.Order("sort_order ASC")
|
||
|
|
})
|
||
|
|
if err := q.Find(&items).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
pageSize := builder.GetPageSize()
|
||
|
|
hasMore := cursor.HasMore(len(items), pageSize)
|
||
|
|
if hasMore {
|
||
|
|
items = items[:pageSize]
|
||
|
|
}
|
||
|
|
|
||
|
|
var nextCursor, prevCursor string
|
||
|
|
if len(items) > 0 {
|
||
|
|
if hasMore {
|
||
|
|
last := items[len(items)-1]
|
||
|
|
nextCursor = cursor.NewCursor(cursor.FormatTime(last.CreatedAt), last.ID, cursor.SortByCreatedAtDesc).Encode()
|
||
|
|
}
|
||
|
|
first := items[0]
|
||
|
|
prevCursor = cursor.NewCursor(cursor.FormatTime(first.CreatedAt), first.ID, cursor.SortByCreatedAtDesc).Encode()
|
||
|
|
}
|
||
|
|
|
||
|
|
return cursor.NewCursorPageResult(items, nextCursor, prevCursor, hasMore), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) IncrementViews(ctx context.Context, id string) error {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
return db.Model(&model.TradeItem{}).Where("id = ?", id).UpdateColumn("views_count", gorm.Expr("views_count + 1")).Error
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) IncrementFavorites(ctx context.Context, id string) error {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
return db.Model(&model.TradeItem{}).Where("id = ?", id).UpdateColumn("favorites_count", gorm.Expr("favorites_count + 1")).Error
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) DecrementFavorites(ctx context.Context, id string) error {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
return db.Model(&model.TradeItem{}).Where("id = ? AND favorites_count > 0", id).UpdateColumn("favorites_count", gorm.Expr("favorites_count - 1")).Error
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) CreateFavorite(ctx context.Context, tradeItemID, userID string) error {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
fav := model.TradeFavorite{
|
||
|
|
TradeItemID: tradeItemID,
|
||
|
|
UserID: userID,
|
||
|
|
}
|
||
|
|
return db.Create(&fav).Error
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) DeleteFavorite(ctx context.Context, tradeItemID, userID string) error {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
return db.Where("trade_item_id = ? AND user_id = ?", tradeItemID, userID).Delete(&model.TradeFavorite{}).Error
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) IsFavorited(ctx context.Context, tradeItemID, userID string) (bool, error) {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
var count int64
|
||
|
|
if err := db.Model(&model.TradeFavorite{}).Where("trade_item_id = ? AND user_id = ?", tradeItemID, userID).Count(&count).Error; err != nil {
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
return count > 0, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *tradeRepository) IsFavoritedBatch(ctx context.Context, tradeItemIDs []string, userID string) (map[string]bool, error) {
|
||
|
|
db := r.getDB(ctx).WithContext(ctx)
|
||
|
|
result := make(map[string]bool, len(tradeItemIDs))
|
||
|
|
if len(tradeItemIDs) == 0 {
|
||
|
|
return result, nil
|
||
|
|
}
|
||
|
|
for _, id := range tradeItemIDs {
|
||
|
|
result[id] = false
|
||
|
|
}
|
||
|
|
var favorites []model.TradeFavorite
|
||
|
|
if err := db.Where("trade_item_id IN ? AND user_id = ?", tradeItemIDs, userID).Find(&favorites).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
for _, fav := range favorites {
|
||
|
|
result[fav.TradeItemID] = true
|
||
|
|
}
|
||
|
|
return result, nil
|
||
|
|
}
|