280 lines
7.8 KiB
Go
280 lines
7.8 KiB
Go
package listing
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"hfb_sys/backend/internal/model"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type Repository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewRepository(db *gorm.DB) *Repository {
|
|
return &Repository{db: db}
|
|
}
|
|
|
|
func (r *Repository) Create(ownerID uint64, req CreateRequest) (*ListingDTO, error) {
|
|
var dto *ListingDTO
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
account := model.GameAccount{
|
|
OwnerID: ownerID,
|
|
GameName: "delta_force",
|
|
ServerRegion: req.ServerRegion,
|
|
LoginPlatform: req.LoginPlatform,
|
|
Title: req.Title,
|
|
Description: req.Description,
|
|
RankLevel: req.RankLevel,
|
|
HafCoinAmount: req.HafCoinAmount,
|
|
Status: "draft",
|
|
}
|
|
if err := tx.Create(&account).Error; err != nil {
|
|
return err
|
|
}
|
|
listing := model.RentalListing{
|
|
AccountID: account.ID,
|
|
OwnerID: ownerID,
|
|
PriceHourly: req.PriceHourly,
|
|
PriceDaily: req.PriceDaily,
|
|
PriceWeekly: req.PriceWeekly,
|
|
DepositAmount: req.DepositAmount,
|
|
MinRentHours: req.MinRentHours,
|
|
MaxRentHours: req.MaxRentHours,
|
|
Status: "draft",
|
|
ReviewStatus: "none",
|
|
}
|
|
if err := tx.Create(&listing).Error; err != nil {
|
|
return err
|
|
}
|
|
dto = toDTO(account, listing)
|
|
return nil
|
|
})
|
|
return dto, err
|
|
}
|
|
|
|
func (r *Repository) Update(ownerID uint64, listingID uint64, req UpdateRequest) (*ListingDTO, error) {
|
|
var dto *ListingDTO
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if listing.Status == "rented" {
|
|
return ErrListingLocked
|
|
}
|
|
|
|
account.Title = req.Title
|
|
account.Description = req.Description
|
|
account.ServerRegion = req.ServerRegion
|
|
account.LoginPlatform = req.LoginPlatform
|
|
account.RankLevel = req.RankLevel
|
|
account.HafCoinAmount = req.HafCoinAmount
|
|
if err := tx.Save(account).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
listing.PriceHourly = req.PriceHourly
|
|
listing.PriceDaily = req.PriceDaily
|
|
listing.PriceWeekly = req.PriceWeekly
|
|
listing.DepositAmount = req.DepositAmount
|
|
listing.MinRentHours = req.MinRentHours
|
|
listing.MaxRentHours = req.MaxRentHours
|
|
listing.Status = "draft"
|
|
listing.ReviewStatus = "none"
|
|
listing.ReviewReason = ""
|
|
listing.PublishedAt = nil
|
|
if err := tx.Save(listing).Error; err != nil {
|
|
return err
|
|
}
|
|
dto = toDTO(*account, *listing)
|
|
return nil
|
|
})
|
|
return dto, err
|
|
}
|
|
|
|
func (r *Repository) SubmitReview(ownerID uint64, listingID uint64) (*ListingDTO, error) {
|
|
var dto *ListingDTO
|
|
err := r.db.Transaction(func(tx *gorm.DB) error {
|
|
listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
now := time.Now()
|
|
listing.Status = "published"
|
|
listing.ReviewStatus = "approved"
|
|
listing.ReviewReason = ""
|
|
listing.PublishedAt = &now
|
|
account.Status = "published"
|
|
if err := tx.Save(account).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Save(listing).Error; err != nil {
|
|
return err
|
|
}
|
|
dto = toDTO(*account, *listing)
|
|
return nil
|
|
})
|
|
return dto, err
|
|
}
|
|
|
|
func (r *Repository) Offline(ownerID uint64, listingID uint64) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if listing.Status == "rented" {
|
|
return ErrListingLocked
|
|
}
|
|
listing.Status = "offline"
|
|
account.Status = "offline"
|
|
if err := tx.Save(account).Error; err != nil {
|
|
return err
|
|
}
|
|
return tx.Save(listing).Error
|
|
})
|
|
}
|
|
|
|
func (r *Repository) ListPublic() ([]ListingDTO, error) {
|
|
var rows []listingRow
|
|
err := r.db.Table("rental_listings AS l").
|
|
Select("l.*, a.title, a.description, a.game_name, a.server_region, a.login_platform, a.rank_level, a.haf_coin_amount").
|
|
Joins("JOIN game_accounts AS a ON a.id = l.account_id").
|
|
Where("l.status = ? AND l.review_status = ?", "published", "approved").
|
|
Order("l.published_at DESC, l.id DESC").
|
|
Limit(100).
|
|
Scan(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return rowsToDTO(rows), nil
|
|
}
|
|
|
|
func (r *Repository) ListMine(ownerID uint64) ([]ListingDTO, error) {
|
|
var rows []listingRow
|
|
err := r.db.Table("rental_listings AS l").
|
|
Select("l.*, a.title, a.description, a.game_name, a.server_region, a.login_platform, a.rank_level, a.haf_coin_amount").
|
|
Joins("JOIN game_accounts AS a ON a.id = l.account_id").
|
|
Where("l.owner_id = ?", ownerID).
|
|
Order("l.id DESC").
|
|
Scan(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return rowsToDTO(rows), nil
|
|
}
|
|
|
|
func (r *Repository) FindPublic(id uint64) (*ListingDTO, error) {
|
|
return r.findDTO("l.id = ? AND l.status = ? AND l.review_status = ?", id, "published", "approved")
|
|
}
|
|
|
|
func (r *Repository) FindMine(ownerID uint64, id uint64) (*ListingDTO, error) {
|
|
return r.findDTO("l.id = ? AND l.owner_id = ?", id, ownerID)
|
|
}
|
|
|
|
func (r *Repository) findOwnedForUpdate(tx *gorm.DB, ownerID uint64, listingID uint64) (*model.RentalListing, *model.GameAccount, error) {
|
|
var listing model.RentalListing
|
|
if err := tx.Where("id = ? AND owner_id = ?", listingID, ownerID).First(&listing).Error; err != nil {
|
|
return nil, nil, err
|
|
}
|
|
var account model.GameAccount
|
|
if err := tx.Where("id = ? AND owner_id = ?", listing.AccountID, ownerID).First(&account).Error; err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return &listing, &account, nil
|
|
}
|
|
|
|
func (r *Repository) findDTO(where string, args ...any) (*ListingDTO, error) {
|
|
var row listingRow
|
|
err := r.db.Table("rental_listings AS l").
|
|
Select("l.*, a.title, a.description, a.game_name, a.server_region, a.login_platform, a.rank_level, a.haf_coin_amount").
|
|
Joins("JOIN game_accounts AS a ON a.id = l.account_id").
|
|
Where(where, args...).
|
|
First(&row).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dto := row.toDTO()
|
|
return &dto, nil
|
|
}
|
|
|
|
type listingRow struct {
|
|
model.RentalListing
|
|
Title string
|
|
Description string
|
|
GameName string
|
|
ServerRegion string
|
|
LoginPlatform string
|
|
RankLevel string
|
|
HafCoinAmount int64
|
|
}
|
|
|
|
func rowsToDTO(rows []listingRow) []ListingDTO {
|
|
items := make([]ListingDTO, 0, len(rows))
|
|
for _, row := range rows {
|
|
items = append(items, row.toDTO())
|
|
}
|
|
return items
|
|
}
|
|
|
|
func (row listingRow) toDTO() ListingDTO {
|
|
return ListingDTO{
|
|
ID: row.ID,
|
|
AccountID: row.AccountID,
|
|
OwnerID: row.OwnerID,
|
|
Title: row.Title,
|
|
Description: row.Description,
|
|
GameName: row.GameName,
|
|
ServerRegion: row.ServerRegion,
|
|
LoginPlatform: row.LoginPlatform,
|
|
RankLevel: row.RankLevel,
|
|
HafCoinAmount: row.HafCoinAmount,
|
|
PriceHourly: row.PriceHourly,
|
|
PriceDaily: row.PriceDaily,
|
|
PriceWeekly: row.PriceWeekly,
|
|
DepositAmount: row.DepositAmount,
|
|
MinRentHours: row.MinRentHours,
|
|
MaxRentHours: row.MaxRentHours,
|
|
Status: row.Status,
|
|
ReviewStatus: row.ReviewStatus,
|
|
ReviewReason: row.ReviewReason,
|
|
PublishedAt: row.PublishedAt,
|
|
CreatedAt: row.CreatedAt,
|
|
UpdatedAt: row.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
func toDTO(account model.GameAccount, listing model.RentalListing) *ListingDTO {
|
|
return &ListingDTO{
|
|
ID: listing.ID,
|
|
AccountID: account.ID,
|
|
OwnerID: listing.OwnerID,
|
|
Title: account.Title,
|
|
Description: account.Description,
|
|
GameName: account.GameName,
|
|
ServerRegion: account.ServerRegion,
|
|
LoginPlatform: account.LoginPlatform,
|
|
RankLevel: account.RankLevel,
|
|
HafCoinAmount: account.HafCoinAmount,
|
|
PriceHourly: listing.PriceHourly,
|
|
PriceDaily: listing.PriceDaily,
|
|
PriceWeekly: listing.PriceWeekly,
|
|
DepositAmount: listing.DepositAmount,
|
|
MinRentHours: listing.MinRentHours,
|
|
MaxRentHours: listing.MaxRentHours,
|
|
Status: listing.Status,
|
|
ReviewStatus: listing.ReviewStatus,
|
|
ReviewReason: listing.ReviewReason,
|
|
PublishedAt: listing.PublishedAt,
|
|
CreatedAt: listing.CreatedAt,
|
|
UpdatedAt: listing.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
func IsNotFound(err error) bool {
|
|
return errors.Is(err, gorm.ErrRecordNotFound)
|
|
}
|