feat: add file upload workflow

This commit is contained in:
yml
2026-05-22 18:50:32 +08:00
parent 6f915b37d3
commit 236518ade4
22 changed files with 819 additions and 117 deletions
+114 -65
View File
@@ -3,6 +3,7 @@ package listing
import (
"encoding/json"
"errors"
"strings"
"time"
"hfb_sys/backend/internal/model"
@@ -24,16 +25,21 @@ func NewRepository(db *gorm.DB) *Repository {
func (r *Repository) Create(ownerID uint64, req CreateRequest) (*ListingDTO, error) {
var dto *ListingDTO
err := r.db.Transaction(func(tx *gorm.DB) error {
screenshots, err := marshalScreenshots(req.ScreenshotURLS)
if err != nil {
return err
}
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",
OwnerID: ownerID,
GameName: "delta_force",
ServerRegion: req.ServerRegion,
LoginPlatform: req.LoginPlatform,
Title: req.Title,
Description: req.Description,
RankLevel: req.RankLevel,
HafCoinAmount: req.HafCoinAmount,
ScreenshotURLS: screenshots,
Status: "draft",
}
if err := tx.Create(&account).Error; err != nil {
return err
@@ -76,6 +82,11 @@ func (r *Repository) Update(ownerID uint64, listingID uint64, req UpdateRequest)
account.LoginPlatform = req.LoginPlatform
account.RankLevel = req.RankLevel
account.HafCoinAmount = req.HafCoinAmount
screenshots, err := marshalScreenshots(req.ScreenshotURLS)
if err != nil {
return err
}
account.ScreenshotURLS = screenshots
if err := tx.Save(account).Error; err != nil {
return err
}
@@ -386,7 +397,7 @@ func (r *Repository) findDTO(where string, args ...any) (*ListingDTO, error) {
func (r *Repository) baseQuery() *gorm.DB {
return 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, COALESCE(u.phone, '') AS owner_phone, COALESCE(u.nickname, '') AS owner_nickname`).
a.haf_coin_amount, a.screenshot_urls, COALESCE(u.phone, '') AS owner_phone, COALESCE(u.nickname, '') AS owner_nickname`).
Joins("JOIN game_accounts AS a ON a.id = l.account_id").
Joins("LEFT JOIN users AS u ON u.id = l.owner_id")
}
@@ -405,15 +416,16 @@ func (r *Repository) findForReviewUpdate(tx *gorm.DB, listingID uint64) (*model.
type listingRow struct {
model.RentalListing
Title string
OwnerPhone string
OwnerNickname string
Description string
GameName string
ServerRegion string
LoginPlatform string
RankLevel string
HafCoinAmount int64
Title string
OwnerPhone string
OwnerNickname string
Description string
GameName string
ServerRegion string
LoginPlatform string
RankLevel string
HafCoinAmount int64
ScreenshotURLS datatypes.JSON
}
func rowsToDTO(rows []listingRow) []ListingDTO {
@@ -426,60 +438,97 @@ func rowsToDTO(rows []listingRow) []ListingDTO {
func (row listingRow) toDTO() ListingDTO {
return ListingDTO{
ID: row.ID,
AccountID: row.AccountID,
OwnerID: row.OwnerID,
OwnerPhone: row.OwnerPhone,
OwnerNickname: row.OwnerNickname,
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,
ID: row.ID,
AccountID: row.AccountID,
OwnerID: row.OwnerID,
OwnerPhone: row.OwnerPhone,
OwnerNickname: row.OwnerNickname,
Title: row.Title,
Description: row.Description,
GameName: row.GameName,
ServerRegion: row.ServerRegion,
LoginPlatform: row.LoginPlatform,
RankLevel: row.RankLevel,
HafCoinAmount: row.HafCoinAmount,
ScreenshotURLS: decodeScreenshots(row.ScreenshotURLS),
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,
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,
ScreenshotURLS: decodeScreenshots(account.ScreenshotURLS),
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 marshalScreenshots(urls []string) (datatypes.JSON, error) {
cleaned := make([]string, 0, len(urls))
seen := make(map[string]struct{}, len(urls))
for _, url := range urls {
url = strings.TrimSpace(url)
if url == "" {
continue
}
if _, ok := seen[url]; ok {
continue
}
seen[url] = struct{}{}
cleaned = append(cleaned, url)
if len(cleaned) >= 12 {
break
}
}
raw, err := json.Marshal(cleaned)
if err != nil {
return nil, err
}
return datatypes.JSON(raw), nil
}
func decodeScreenshots(raw datatypes.JSON) []string {
if len(raw) == 0 {
return []string{}
}
var urls []string
if err := json.Unmarshal(raw, &urls); err != nil {
return []string{}
}
return urls
}
func appendAuditLog(tx *gorm.DB, actorID uint64, action string, bizType string, bizID uint64, meta AuditMeta, detail map[string]any) error {
raw, err := json.Marshal(detail)
if err != nil {