优化发布等各种状态
This commit is contained in:
@@ -39,10 +39,10 @@ type CreateRequest struct {
|
||||
HafCoinAmount int64 `json:"haf_coin_amount"`
|
||||
AssetSummary map[string]any `json:"asset_summary"`
|
||||
ScreenshotURLS []string `json:"screenshot_urls"`
|
||||
PriceHourly float64 `json:"price_hourly" binding:"required"`
|
||||
PriceHourly float64 `json:"price_hourly"`
|
||||
PriceDaily float64 `json:"price_daily"`
|
||||
PriceWeekly float64 `json:"price_weekly"`
|
||||
DepositAmount float64 `json:"deposit_amount" binding:"required"`
|
||||
DepositAmount float64 `json:"deposit_amount"`
|
||||
}
|
||||
|
||||
type UpdateRequest = CreateRequest
|
||||
|
||||
@@ -301,6 +301,18 @@ func writeListingError(c *gin.Context, err error) {
|
||||
switch {
|
||||
case errors.Is(err, ErrDependencyUnavailable):
|
||||
response.ServiceUnavailable(c, "数据库未连接")
|
||||
case errors.Is(err, ErrMissingTitle):
|
||||
response.BadRequest(c, "发布标题不能为空")
|
||||
case errors.Is(err, ErrMissingServerRegion):
|
||||
response.BadRequest(c, "请选择区服")
|
||||
case errors.Is(err, ErrInvalidPrice):
|
||||
response.BadRequest(c, "发布价格不正确")
|
||||
case errors.Is(err, ErrInvalidDeposit):
|
||||
response.BadRequest(c, "押金不能小于 0")
|
||||
case errors.Is(err, ErrInvalidHafCoin):
|
||||
response.BadRequest(c, "哈夫币数量不正确")
|
||||
case errors.Is(err, ErrMissingScreenshot):
|
||||
response.BadRequest(c, "请至少上传一张账号截图")
|
||||
case errors.Is(err, ErrInvalidInput):
|
||||
response.BadRequest(c, "发布信息不符合规则")
|
||||
case errors.Is(err, ErrListingLocked):
|
||||
|
||||
@@ -22,7 +22,15 @@ func NewRepository(db *gorm.DB) *Repository {
|
||||
return &Repository{db: db}
|
||||
}
|
||||
|
||||
func (r *Repository) Create(ownerID uint64, req CreateRequest) (*ListingDTO, error) {
|
||||
func initialPublishState(reviewRequired bool) (string, string, *time.Time) {
|
||||
if reviewRequired {
|
||||
return "draft", "pending", nil
|
||||
}
|
||||
now := time.Now()
|
||||
return "published", "approved", &now
|
||||
}
|
||||
|
||||
func (r *Repository) Create(ownerID uint64, req CreateRequest, reviewRequired bool) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
screenshots, err := marshalScreenshots(req.ScreenshotURLS)
|
||||
@@ -33,6 +41,7 @@ func (r *Repository) Create(ownerID uint64, req CreateRequest) (*ListingDTO, err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listingStatus, reviewStatus, publishedAt := initialPublishState(reviewRequired)
|
||||
account := model.GameAccount{
|
||||
OwnerID: ownerID,
|
||||
GameName: "delta_force",
|
||||
@@ -44,7 +53,7 @@ func (r *Repository) Create(ownerID uint64, req CreateRequest) (*ListingDTO, err
|
||||
HafCoinAmount: req.HafCoinAmount,
|
||||
AssetSummary: assetSummary,
|
||||
ScreenshotURLS: screenshots,
|
||||
Status: "draft",
|
||||
Status: listingStatus,
|
||||
}
|
||||
if err := tx.Create(&account).Error; err != nil {
|
||||
return err
|
||||
@@ -56,8 +65,9 @@ func (r *Repository) Create(ownerID uint64, req CreateRequest) (*ListingDTO, err
|
||||
PriceDaily: req.PriceDaily,
|
||||
PriceWeekly: req.PriceWeekly,
|
||||
DepositAmount: req.DepositAmount,
|
||||
Status: "draft",
|
||||
ReviewStatus: "none",
|
||||
Status: listingStatus,
|
||||
ReviewStatus: reviewStatus,
|
||||
PublishedAt: publishedAt,
|
||||
}
|
||||
if err := tx.Create(&listing).Error; err != nil {
|
||||
return err
|
||||
@@ -68,7 +78,7 @@ func (r *Repository) Create(ownerID uint64, req CreateRequest) (*ListingDTO, err
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (r *Repository) Update(ownerID uint64, listingID uint64, req UpdateRequest) (*ListingDTO, error) {
|
||||
func (r *Repository) Update(ownerID uint64, listingID uint64, req UpdateRequest, reviewRequired bool) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
|
||||
@@ -95,18 +105,20 @@ func (r *Repository) Update(ownerID uint64, listingID uint64, req UpdateRequest)
|
||||
return err
|
||||
}
|
||||
account.ScreenshotURLS = screenshots
|
||||
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.Status = "draft"
|
||||
listing.ReviewStatus = "none"
|
||||
listingStatus, reviewStatus, publishedAt := initialPublishState(reviewRequired)
|
||||
listing.Status = listingStatus
|
||||
listing.ReviewStatus = reviewStatus
|
||||
listing.ReviewReason = ""
|
||||
listing.PublishedAt = nil
|
||||
listing.PublishedAt = publishedAt
|
||||
account.Status = listingStatus
|
||||
if err := tx.Save(account).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Save(listing).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -116,7 +128,7 @@ func (r *Repository) Update(ownerID uint64, listingID uint64, req UpdateRequest)
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (r *Repository) SubmitReview(ownerID uint64, listingID uint64) (*ListingDTO, error) {
|
||||
func (r *Repository) SubmitReview(ownerID uint64, listingID uint64, reviewRequired bool) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
|
||||
@@ -126,11 +138,12 @@ func (r *Repository) SubmitReview(ownerID uint64, listingID uint64) (*ListingDTO
|
||||
if listing.Status == "rented" {
|
||||
return ErrListingLocked
|
||||
}
|
||||
listing.Status = "draft"
|
||||
listing.ReviewStatus = "pending"
|
||||
listingStatus, reviewStatus, publishedAt := initialPublishState(reviewRequired)
|
||||
listing.Status = listingStatus
|
||||
listing.ReviewStatus = reviewStatus
|
||||
listing.ReviewReason = ""
|
||||
listing.PublishedAt = nil
|
||||
account.Status = "draft"
|
||||
listing.PublishedAt = publishedAt
|
||||
account.Status = listingStatus
|
||||
if err := tx.Save(account).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package listing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -9,14 +10,27 @@ var (
|
||||
ErrDependencyUnavailable = errors.New("dependency unavailable")
|
||||
ErrInvalidInput = errors.New("invalid listing input")
|
||||
ErrListingLocked = errors.New("listing locked")
|
||||
ErrMissingTitle = errors.New("missing listing title")
|
||||
ErrMissingServerRegion = errors.New("missing server region")
|
||||
ErrInvalidPrice = errors.New("invalid listing price")
|
||||
ErrInvalidDeposit = errors.New("invalid listing deposit")
|
||||
ErrInvalidHafCoin = errors.New("invalid haf coin amount")
|
||||
ErrMissingScreenshot = errors.New("missing screenshot")
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
repo *Repository
|
||||
repo *Repository
|
||||
config ConfigReader
|
||||
}
|
||||
|
||||
func NewService(repo *Repository) *Service {
|
||||
return &Service{repo: repo}
|
||||
type ConfigReader interface {
|
||||
FindValue(key string) (string, error)
|
||||
}
|
||||
|
||||
const reviewRequiredConfigKey = "listing.review_required"
|
||||
|
||||
func NewService(repo *Repository, config ConfigReader) *Service {
|
||||
return &Service{repo: repo, config: config}
|
||||
}
|
||||
|
||||
func (s *Service) Create(ownerID uint64, req CreateRequest) (*ListingDTO, error) {
|
||||
@@ -26,7 +40,11 @@ func (s *Service) Create(ownerID uint64, req CreateRequest) (*ListingDTO, error)
|
||||
if err := validateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.Create(ownerID, req)
|
||||
reviewRequired, err := s.reviewRequired()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.Create(ownerID, req, reviewRequired)
|
||||
}
|
||||
|
||||
func (s *Service) Update(ownerID uint64, id uint64, req UpdateRequest) (*ListingDTO, error) {
|
||||
@@ -36,14 +54,22 @@ func (s *Service) Update(ownerID uint64, id uint64, req UpdateRequest) (*Listing
|
||||
if err := validateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.Update(ownerID, id, req)
|
||||
reviewRequired, err := s.reviewRequired()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.Update(ownerID, id, req, reviewRequired)
|
||||
}
|
||||
|
||||
func (s *Service) SubmitReview(ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.SubmitReview(ownerID, id)
|
||||
reviewRequired, err := s.reviewRequired()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.SubmitReview(ownerID, id, reviewRequired)
|
||||
}
|
||||
|
||||
func (s *Service) ListPendingReview() ([]ListingDTO, error) {
|
||||
@@ -140,17 +166,23 @@ func (s *Service) FindMine(ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
}
|
||||
|
||||
func validateRequest(req CreateRequest) error {
|
||||
if req.Title == "" || req.ServerRegion == "" {
|
||||
return ErrInvalidInput
|
||||
if strings.TrimSpace(req.Title) == "" {
|
||||
return ErrMissingTitle
|
||||
}
|
||||
if req.PriceHourly <= 0 || req.DepositAmount < 0 {
|
||||
return ErrInvalidInput
|
||||
if strings.TrimSpace(req.ServerRegion) == "" {
|
||||
return ErrMissingServerRegion
|
||||
}
|
||||
if req.PriceHourly <= 0 {
|
||||
return ErrInvalidPrice
|
||||
}
|
||||
if req.DepositAmount < 0 {
|
||||
return ErrInvalidDeposit
|
||||
}
|
||||
if req.HafCoinAmount < 0 {
|
||||
return ErrInvalidInput
|
||||
return ErrInvalidHafCoin
|
||||
}
|
||||
if !hasScreenshotURL(req.ScreenshotURLS) {
|
||||
return ErrInvalidInput
|
||||
return ErrMissingScreenshot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -163,3 +195,18 @@ func hasScreenshotURL(urls []string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Service) reviewRequired() (bool, error) {
|
||||
if s.config == nil {
|
||||
return false, nil
|
||||
}
|
||||
value, err := s.config.FindValue(reviewRequiredConfigKey)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
required, err := strconv.ParseBool(strings.TrimSpace(value))
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return required, nil
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ var defaultConfigs = []defaultConfig{
|
||||
{Key: "handoff.owner_return_confirm_timeout_minutes", Value: "120", Description: "号主待确认归还超时分钟数"},
|
||||
{Key: "order.return_overdue_grace_minutes", Value: "10", Description: "预计截止后归还宽限分钟数"},
|
||||
{Key: "deposit.min_amount", Value: "50", Description: "发布租号最低押金"},
|
||||
{Key: "listing.review_required", Value: "false", Description: "发布账号是否需要后台人工审核"},
|
||||
{Key: "risk.sms_limit_per_phone_hour", Value: "5", Description: "单手机号每小时短信验证码次数"},
|
||||
{Key: "risk.sms_limit_per_ip_hour", Value: "20", Description: "单 IP 每小时短信验证码次数"},
|
||||
{Key: "realname.required_for_order", Value: "false", Description: "下单是否必须完成实名认证"},
|
||||
|
||||
Reference in New Issue
Block a user