优化提号详情
This commit is contained in:
@@ -3,6 +3,8 @@ package pickup
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
// 提号状态
|
||||
@@ -18,39 +20,49 @@ var (
|
||||
ErrPickupNotFound = errors.New("pickup not found")
|
||||
ErrPickupNotPickingUp = errors.New("pickup not in picking_up status")
|
||||
ErrInvalidAmount = errors.New("invalid amount")
|
||||
ErrInvalidProfit = errors.New("invalid profit amount")
|
||||
ErrDuplicatePickup = errors.New("duplicate pickup in progress")
|
||||
)
|
||||
|
||||
type PickupDTO struct {
|
||||
ID uint64 `json:"id"`
|
||||
PickupNo string `json:"pickup_no"`
|
||||
ListingID uint64 `json:"listing_id"`
|
||||
ListingNo string `json:"listing_no"`
|
||||
AccountID uint64 `json:"account_id"`
|
||||
AccountTitle string `json:"account_title"`
|
||||
ServerRegion string `json:"server_region"`
|
||||
LoginPlatform string `json:"login_platform"`
|
||||
OwnerID uint64 `json:"owner_id"`
|
||||
OwnerPhone string `json:"owner_phone"`
|
||||
AdminID uint64 `json:"admin_id"`
|
||||
Platform string `json:"platform"`
|
||||
SettleAmountCent int64 `json:"settle_amount_cent"`
|
||||
Status string `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
CompleteRemark string `json:"complete_remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
CancelledAt *time.Time `json:"cancelled_at,omitempty"`
|
||||
ID uint64 `json:"id"`
|
||||
PickupNo string `json:"pickup_no"`
|
||||
ListingID uint64 `json:"listing_id"`
|
||||
ListingNo string `json:"listing_no"`
|
||||
AccountID uint64 `json:"account_id"`
|
||||
AccountTitle string `json:"account_title"`
|
||||
ServerRegion string `json:"server_region"`
|
||||
LoginPlatform string `json:"login_platform"`
|
||||
OwnerID uint64 `json:"owner_id"`
|
||||
OwnerPhone string `json:"owner_phone"`
|
||||
AdminID uint64 `json:"admin_id"`
|
||||
Platform string `json:"platform"`
|
||||
ListingPriceCent int64 `json:"listing_price_cent"`
|
||||
OwnerPriceCent int64 `json:"owner_price_cent"`
|
||||
WebsiteProfitCent int64 `json:"website_profit_cent"`
|
||||
ProfitAmountCent int64 `json:"profit_amount_cent"`
|
||||
SellerRatio float64 `json:"seller_ratio"`
|
||||
BuyerRatio float64 `json:"buyer_ratio"`
|
||||
AccountSnapshot datatypes.JSON `json:"account_snapshot,omitempty"`
|
||||
SettleAmountCent int64 `json:"settle_amount_cent"`
|
||||
Status string `json:"status"`
|
||||
Remark string `json:"remark"`
|
||||
CompleteRemark string `json:"complete_remark"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
||||
CancelledAt *time.Time `json:"cancelled_at,omitempty"`
|
||||
}
|
||||
|
||||
type CreateRequest struct {
|
||||
ListingID uint64 `json:"listing_id" binding:"required"`
|
||||
Platform string `json:"platform"`
|
||||
Remark string `json:"remark"`
|
||||
ListingID uint64 `json:"listing_id" binding:"required"`
|
||||
Platform string `json:"platform"`
|
||||
ProfitAmountCent int64 `json:"profit_amount_cent"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type CompleteRequest struct {
|
||||
SettleAmountCent int64 `json:"settle_amount_cent" binding:"required,min=1"`
|
||||
ProfitAmountCent *int64 `json:"profit_amount_cent"`
|
||||
CompleteRemark string `json:"complete_remark"`
|
||||
}
|
||||
|
||||
@@ -72,14 +84,19 @@ type SellerPickupQuery struct {
|
||||
}
|
||||
|
||||
type AvailableListingDTO struct {
|
||||
ID uint64 `json:"id"`
|
||||
ListingNo string `json:"listing_no"`
|
||||
AccountID uint64 `json:"account_id"`
|
||||
AccountTitle string `json:"account_title"`
|
||||
ServerRegion string `json:"server_region"`
|
||||
LoginPlatform string `json:"login_platform"`
|
||||
OwnerID uint64 `json:"owner_id"`
|
||||
OwnerPhone string `json:"owner_phone"`
|
||||
ID uint64 `json:"id"`
|
||||
ListingNo string `json:"listing_no"`
|
||||
AccountID uint64 `json:"account_id"`
|
||||
AccountTitle string `json:"account_title"`
|
||||
ServerRegion string `json:"server_region"`
|
||||
LoginPlatform string `json:"login_platform"`
|
||||
OwnerID uint64 `json:"owner_id"`
|
||||
OwnerPhone string `json:"owner_phone"`
|
||||
ListingPriceCent int64 `json:"listing_price_cent"`
|
||||
OwnerPriceCent int64 `json:"owner_price_cent"`
|
||||
WebsiteProfitCent int64 `json:"website_profit_cent"`
|
||||
SellerRatio float64 `json:"seller_ratio"`
|
||||
BuyerRatio float64 `json:"buyer_ratio"`
|
||||
}
|
||||
|
||||
type PaginatedResult struct {
|
||||
|
||||
@@ -154,6 +154,8 @@ func writePickupError(c *gin.Context, err error) {
|
||||
response.BadRequest(c, "提号订单状态不允许该操作")
|
||||
case errors.Is(err, ErrInvalidAmount):
|
||||
response.BadRequest(c, "结算金额不正确")
|
||||
case errors.Is(err, ErrInvalidProfit):
|
||||
response.BadRequest(c, "利润金额不正确")
|
||||
default:
|
||||
response.Error(c, http.StatusInternalServerError, "pickup_error", err.Error())
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package pickup
|
||||
import (
|
||||
"context"
|
||||
crand "crypto/rand"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -16,6 +18,7 @@ import (
|
||||
"hfb_sys/backend/internal/timeutil"
|
||||
"hfb_sys/backend/pkg/money"
|
||||
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
@@ -62,20 +65,32 @@ func (r *Repository) Create(ctx context.Context, req CreateRequest, adminID uint
|
||||
if err := tx.First(&account, listing.AccountID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
priceSnapshot := buildPickupPriceSnapshot(listing, account)
|
||||
accountSnapshot, err := makePickupAccountSnapshot(account, listing)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pickupNo, err := newPickupNo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pickup := model.AdminPickup{
|
||||
PickupNo: pickupNo,
|
||||
ListingID: listing.ID,
|
||||
AccountID: listing.AccountID,
|
||||
OwnerID: listing.OwnerID,
|
||||
AdminID: adminID,
|
||||
Platform: strings.TrimSpace(req.Platform),
|
||||
Status: StatusPickingUp,
|
||||
Remark: strings.TrimSpace(req.Remark),
|
||||
PickupNo: pickupNo,
|
||||
ListingID: listing.ID,
|
||||
AccountID: listing.AccountID,
|
||||
OwnerID: listing.OwnerID,
|
||||
AdminID: adminID,
|
||||
Platform: strings.TrimSpace(req.Platform),
|
||||
ListingPriceCent: priceSnapshot.ListingPriceCent,
|
||||
OwnerPriceCent: priceSnapshot.OwnerPriceCent,
|
||||
WebsiteProfitCent: priceSnapshot.WebsiteProfitCent,
|
||||
ProfitAmountCent: req.ProfitAmountCent,
|
||||
SellerRatio: priceSnapshot.SellerRatio,
|
||||
BuyerRatio: priceSnapshot.BuyerRatio,
|
||||
AccountSnapshot: accountSnapshot,
|
||||
Status: StatusPickingUp,
|
||||
Remark: strings.TrimSpace(req.Remark),
|
||||
}
|
||||
if err := tx.Create(&pickup).Error; err != nil {
|
||||
return err
|
||||
@@ -107,9 +122,10 @@ func (r *Repository) Create(ctx context.Context, req CreateRequest, adminID uint
|
||||
BizID: &bid,
|
||||
Meta: meta,
|
||||
Detail: map[string]any{
|
||||
"pickup_no": pickupNo,
|
||||
"listing_id": listing.ID,
|
||||
"platform": pickup.Platform,
|
||||
"pickup_no": pickupNo,
|
||||
"listing_id": listing.ID,
|
||||
"platform": pickup.Platform,
|
||||
"profit_cent": pickup.ProfitAmountCent,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
@@ -151,6 +167,9 @@ func (r *Repository) Complete(ctx context.Context, pickupID uint64, req Complete
|
||||
|
||||
now := time.Now()
|
||||
pickup.SettleAmountCent = req.SettleAmountCent
|
||||
if req.ProfitAmountCent != nil {
|
||||
pickup.ProfitAmountCent = *req.ProfitAmountCent
|
||||
}
|
||||
pickup.Status = StatusCompleted
|
||||
pickup.CompleteRemark = strings.TrimSpace(req.CompleteRemark)
|
||||
pickup.CompletedAt = &now
|
||||
@@ -199,6 +218,7 @@ func (r *Repository) Complete(ctx context.Context, pickupID uint64, req Complete
|
||||
Detail: map[string]any{
|
||||
"pickup_no": pickup.PickupNo,
|
||||
"settle_amount_cent": req.SettleAmountCent,
|
||||
"profit_amount_cent": pickup.ProfitAmountCent,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
@@ -275,6 +295,11 @@ func (r *Repository) FindByID(ctx context.Context, id uint64) (*PickupDTO, error
|
||||
return nil, err
|
||||
}
|
||||
dto := row.toDTO()
|
||||
if isEmptyPickupAccountSnapshot(dto.AccountSnapshot) {
|
||||
if snapshot, err := r.fallbackAccountSnapshot(ctx, row.ListingID, row.AccountID); err == nil {
|
||||
dto.AccountSnapshot = snapshot
|
||||
}
|
||||
}
|
||||
return &dto, nil
|
||||
}
|
||||
|
||||
@@ -290,7 +315,7 @@ func (r *Repository) ListAdmin(ctx context.Context, query AdminPickupQuery) (*Pa
|
||||
like := "%" + kw + "%"
|
||||
db = db.Where("p.pickup_no LIKE ? OR l.listing_no LIKE ? OR a.title LIKE ?", like, like, like)
|
||||
}
|
||||
return r.paginatePickups(db, query.Page, query.PageSize)
|
||||
return r.paginatePickups(db, query.Page, query.PageSize, false)
|
||||
}
|
||||
|
||||
func (r *Repository) ListForSeller(ctx context.Context, ownerID uint64, query SellerPickupQuery) (*PaginatedResult, error) {
|
||||
@@ -301,10 +326,10 @@ func (r *Repository) ListForSeller(ctx context.Context, ownerID uint64, query Se
|
||||
if status := strings.TrimSpace(query.Status); status != "" {
|
||||
db = db.Where("p.status = ?", status)
|
||||
}
|
||||
return r.paginatePickups(db, query.Page, query.PageSize)
|
||||
return r.paginatePickups(db, query.Page, query.PageSize, true)
|
||||
}
|
||||
|
||||
func (r *Repository) paginatePickups(db *gorm.DB, page, pageSize int) (*PaginatedResult, error) {
|
||||
func (r *Repository) paginatePickups(db *gorm.DB, page, pageSize int, sellerView bool) (*PaginatedResult, error) {
|
||||
page, pageSize = normalizePaging(page, pageSize)
|
||||
var total int64
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
@@ -316,7 +341,14 @@ func (r *Repository) paginatePickups(db *gorm.DB, page, pageSize int) (*Paginate
|
||||
}
|
||||
items := make([]PickupDTO, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
items = append(items, row.toDTO())
|
||||
item := row.toDTO()
|
||||
item.AccountSnapshot = nil
|
||||
if sellerView {
|
||||
item.WebsiteProfitCent = 0
|
||||
item.ProfitAmountCent = 0
|
||||
item.BuyerRatio = 0
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return &PaginatedResult{Items: items, Total: total, Page: page, PageSize: pageSize}, nil
|
||||
}
|
||||
@@ -327,7 +359,7 @@ func (r *Repository) ListAvailableListings(ctx context.Context, keyword string,
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
db := r.db.WithContext(ctx).Table("rental_listings AS l").
|
||||
Select("l.id, l.listing_no, l.account_id, a.title AS account_title, a.server_region, a.login_platform, l.owner_id, owner.phone AS owner_phone").
|
||||
Select("l.id, l.listing_no, l.account_id, l.owner_id, l.price_cent AS listing_price_cent, a.title AS account_title, a.server_region, a.login_platform, a.haf_coin_amount, a.asset_summary, owner.phone AS owner_phone").
|
||||
Joins("JOIN game_accounts AS a ON a.id = l.account_id").
|
||||
Joins("JOIN users AS owner ON owner.id = l.owner_id").
|
||||
Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false)
|
||||
@@ -341,11 +373,15 @@ func (r *Repository) ListAvailableListings(ctx context.Context, keyword string,
|
||||
return nil, err
|
||||
}
|
||||
page, pageSize = normalizePaging(page, pageSize)
|
||||
var rows []AvailableListingDTO
|
||||
var rows []availableListingRow
|
||||
if err := db.Order("l.id DESC").Offset((page - 1) * pageSize).Limit(pageSize).Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AvailableListingsResult{Items: rows, Total: total, Page: page, PageSize: pageSize}, nil
|
||||
items := make([]AvailableListingDTO, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
items = append(items, row.toDTO())
|
||||
}
|
||||
return &AvailableListingsResult{Items: items, Total: total, Page: page, PageSize: pageSize}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) baseQuery(ctx context.Context) *gorm.DB {
|
||||
@@ -367,28 +403,233 @@ type pickupRow struct {
|
||||
|
||||
func (row pickupRow) toDTO() PickupDTO {
|
||||
return PickupDTO{
|
||||
ID: row.ID,
|
||||
PickupNo: row.PickupNo,
|
||||
ListingID: row.ListingID,
|
||||
ListingNo: row.ListingNo,
|
||||
AccountID: row.AccountID,
|
||||
AccountTitle: row.AccountTitle,
|
||||
ServerRegion: row.ServerRegion,
|
||||
LoginPlatform: row.LoginPlatform,
|
||||
OwnerID: row.OwnerID,
|
||||
OwnerPhone: row.OwnerPhone,
|
||||
AdminID: row.AdminID,
|
||||
Platform: row.Platform,
|
||||
SettleAmountCent: row.SettleAmountCent,
|
||||
Status: row.Status,
|
||||
Remark: row.Remark,
|
||||
CompleteRemark: row.CompleteRemark,
|
||||
CreatedAt: row.CreatedAt,
|
||||
CompletedAt: row.CompletedAt,
|
||||
CancelledAt: row.CancelledAt,
|
||||
ID: row.ID,
|
||||
PickupNo: row.PickupNo,
|
||||
ListingID: row.ListingID,
|
||||
ListingNo: row.ListingNo,
|
||||
AccountID: row.AccountID,
|
||||
AccountTitle: row.AccountTitle,
|
||||
ServerRegion: row.ServerRegion,
|
||||
LoginPlatform: row.LoginPlatform,
|
||||
OwnerID: row.OwnerID,
|
||||
OwnerPhone: row.OwnerPhone,
|
||||
AdminID: row.AdminID,
|
||||
Platform: row.Platform,
|
||||
ListingPriceCent: row.ListingPriceCent,
|
||||
OwnerPriceCent: row.OwnerPriceCent,
|
||||
WebsiteProfitCent: row.WebsiteProfitCent,
|
||||
ProfitAmountCent: row.ProfitAmountCent,
|
||||
SellerRatio: row.SellerRatio,
|
||||
BuyerRatio: row.BuyerRatio,
|
||||
AccountSnapshot: row.AccountSnapshot,
|
||||
SettleAmountCent: row.SettleAmountCent,
|
||||
Status: row.Status,
|
||||
Remark: row.Remark,
|
||||
CompleteRemark: row.CompleteRemark,
|
||||
CreatedAt: row.CreatedAt,
|
||||
CompletedAt: row.CompletedAt,
|
||||
CancelledAt: row.CancelledAt,
|
||||
}
|
||||
}
|
||||
|
||||
type availableListingRow struct {
|
||||
ID uint64
|
||||
ListingNo string
|
||||
AccountID uint64
|
||||
AccountTitle string
|
||||
ServerRegion string
|
||||
LoginPlatform string
|
||||
OwnerID uint64
|
||||
OwnerPhone string
|
||||
ListingPriceCent int64
|
||||
HafCoinAmount int64
|
||||
AssetSummary datatypes.JSON
|
||||
}
|
||||
|
||||
func (row availableListingRow) toDTO() AvailableListingDTO {
|
||||
snapshot := buildPickupPriceSnapshot(
|
||||
model.RentalListing{PriceCent: row.ListingPriceCent},
|
||||
model.GameAccount{HafCoinAmount: row.HafCoinAmount, AssetSummary: row.AssetSummary},
|
||||
)
|
||||
return AvailableListingDTO{
|
||||
ID: row.ID,
|
||||
ListingNo: row.ListingNo,
|
||||
AccountID: row.AccountID,
|
||||
AccountTitle: row.AccountTitle,
|
||||
ServerRegion: row.ServerRegion,
|
||||
LoginPlatform: row.LoginPlatform,
|
||||
OwnerID: row.OwnerID,
|
||||
OwnerPhone: row.OwnerPhone,
|
||||
ListingPriceCent: snapshot.ListingPriceCent,
|
||||
OwnerPriceCent: snapshot.OwnerPriceCent,
|
||||
WebsiteProfitCent: snapshot.WebsiteProfitCent,
|
||||
SellerRatio: snapshot.SellerRatio,
|
||||
BuyerRatio: snapshot.BuyerRatio,
|
||||
}
|
||||
}
|
||||
|
||||
func makePickupAccountSnapshot(account model.GameAccount, listing model.RentalListing) (datatypes.JSON, error) {
|
||||
payload := map[string]any{
|
||||
"listing_id": listing.ID,
|
||||
"listing_no": listing.ListingNo,
|
||||
"account_id": account.ID,
|
||||
"title": account.Title,
|
||||
"game_name": account.GameName,
|
||||
"server_region": account.ServerRegion,
|
||||
"login_platform": account.LoginPlatform,
|
||||
"rank_level": account.RankLevel,
|
||||
"haf_coin_amount": account.HafCoinAmount,
|
||||
"asset_summary": account.AssetSummary,
|
||||
"season_tags": account.SeasonTags,
|
||||
"screenshot_urls": account.ScreenshotURLS,
|
||||
"snapshot_version": 1,
|
||||
}
|
||||
raw, err := json.Marshal(payload)
|
||||
return datatypes.JSON(raw), err
|
||||
}
|
||||
|
||||
func (r *Repository) fallbackAccountSnapshot(ctx context.Context, listingID uint64, accountID uint64) (datatypes.JSON, error) {
|
||||
var listing model.RentalListing
|
||||
if err := r.db.WithContext(ctx).First(&listing, listingID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var account model.GameAccount
|
||||
if err := r.db.WithContext(ctx).First(&account, accountID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return makePickupAccountSnapshot(account, listing)
|
||||
}
|
||||
|
||||
func isEmptyPickupAccountSnapshot(raw datatypes.JSON) bool {
|
||||
trimmed := strings.TrimSpace(string(raw))
|
||||
return trimmed == "" || trimmed == "null" || trimmed == "{}"
|
||||
}
|
||||
|
||||
type pickupPriceSnapshot struct {
|
||||
ListingPriceCent int64
|
||||
OwnerPriceCent int64
|
||||
WebsiteProfitCent int64
|
||||
SellerRatio float64
|
||||
BuyerRatio float64
|
||||
}
|
||||
|
||||
func buildPickupPriceSnapshot(listing model.RentalListing, account model.GameAccount) pickupPriceSnapshot {
|
||||
summary := decodePickupAssetSummary(account.AssetSummary)
|
||||
breakdown := pickupPriceBreakdown(summary)
|
||||
listingPriceCent := maxPickupCent(listing.PriceCent, 0)
|
||||
ownerPriceCent := yuanToPickupCent(readPickupNumber(breakdown["seller_total_price"]))
|
||||
if ownerPriceCent <= 0 || (listingPriceCent > 0 && ownerPriceCent > listingPriceCent) {
|
||||
ownerPriceCent = listingPriceCent
|
||||
}
|
||||
websiteProfitCent := yuanToPickupCent(readPickupNumber(breakdown["platform_markup_amount"]))
|
||||
if websiteProfitCent <= 0 {
|
||||
websiteProfitCent = listingPriceCent - ownerPriceCent
|
||||
}
|
||||
if websiteProfitCent < 0 {
|
||||
websiteProfitCent = 0
|
||||
}
|
||||
sellerRatio := readPickupNumber(breakdown["seller_ratio"])
|
||||
if sellerRatio <= 0 {
|
||||
sellerRatio = ratioFromCoin(account.HafCoinAmount, readPickupNumber(breakdown["seller_coin_base_price"]))
|
||||
}
|
||||
buyerRatio := readPickupNumber(breakdown["buyer_ratio"])
|
||||
if buyerRatio <= 0 {
|
||||
buyerRatio = readPickupNumber(summary["publish_ratio"])
|
||||
}
|
||||
if buyerRatio <= 0 {
|
||||
buyerRatio = ratioFromCoin(account.HafCoinAmount, readPickupNumber(breakdown["buyer_coin_base_price"]))
|
||||
}
|
||||
return pickupPriceSnapshot{
|
||||
ListingPriceCent: listingPriceCent,
|
||||
OwnerPriceCent: ownerPriceCent,
|
||||
WebsiteProfitCent: websiteProfitCent,
|
||||
SellerRatio: roundPickupRatio(sellerRatio),
|
||||
BuyerRatio: roundPickupRatio(buyerRatio),
|
||||
}
|
||||
}
|
||||
|
||||
func decodePickupAssetSummary(raw datatypes.JSON) map[string]any {
|
||||
if len(raw) == 0 {
|
||||
return map[string]any{}
|
||||
}
|
||||
var summary map[string]any
|
||||
if err := json.Unmarshal(raw, &summary); err != nil {
|
||||
return map[string]any{}
|
||||
}
|
||||
return summary
|
||||
}
|
||||
|
||||
func pickupPriceBreakdown(summary map[string]any) map[string]any {
|
||||
if summary == nil {
|
||||
return map[string]any{}
|
||||
}
|
||||
if breakdown, ok := summary["price_breakdown"].(map[string]any); ok {
|
||||
return breakdown
|
||||
}
|
||||
if raw, ok := summary["price_breakdown"].(string); ok && strings.TrimSpace(raw) != "" {
|
||||
var breakdown map[string]any
|
||||
if err := json.Unmarshal([]byte(raw), &breakdown); err == nil {
|
||||
return breakdown
|
||||
}
|
||||
}
|
||||
return map[string]any{}
|
||||
}
|
||||
|
||||
func readPickupNumber(value any) float64 {
|
||||
switch typed := value.(type) {
|
||||
case float64:
|
||||
return typed
|
||||
case float32:
|
||||
return float64(typed)
|
||||
case int:
|
||||
return float64(typed)
|
||||
case int64:
|
||||
return float64(typed)
|
||||
case json.Number:
|
||||
number, err := typed.Float64()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return number
|
||||
case string:
|
||||
number, err := strconv.ParseFloat(strings.TrimSpace(typed), 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return number
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func yuanToPickupCent(value float64) int64 {
|
||||
if value <= 0 || math.IsNaN(value) || math.IsInf(value, 0) {
|
||||
return 0
|
||||
}
|
||||
return int64(math.Round(value * 100))
|
||||
}
|
||||
|
||||
func ratioFromCoin(coinAmount int64, basePrice float64) float64 {
|
||||
if coinAmount <= 0 || basePrice <= 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(coinAmount) / 10000 / basePrice
|
||||
}
|
||||
|
||||
func roundPickupRatio(value float64) float64 {
|
||||
if value <= 0 || math.IsNaN(value) || math.IsInf(value, 0) {
|
||||
return 0
|
||||
}
|
||||
return math.Round(value*100) / 100
|
||||
}
|
||||
|
||||
func maxPickupCent(a, b int64) int64 {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func normalizePaging(page, pageSize int) (int, int) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
@@ -411,5 +652,5 @@ func newPickupNo() (string, error) {
|
||||
return "", err
|
||||
}
|
||||
randomNum := (int(buf[0])<<8 | int(buf[1])) % 1000
|
||||
return "PK" + timeStr + strconv.Itoa(1000+randomNum)[1:], nil
|
||||
return "PK" + timeStr + strconv.Itoa(1000 + randomNum)[1:], nil
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ func (s *Service) Create(ctx context.Context, req CreateRequest, adminID uint64,
|
||||
if req.ListingID == 0 {
|
||||
return nil, ErrListingUnavailable
|
||||
}
|
||||
if req.ProfitAmountCent < 0 {
|
||||
return nil, ErrInvalidProfit
|
||||
}
|
||||
return s.repo.Create(ctx, req, adminID, meta)
|
||||
}
|
||||
|
||||
@@ -31,6 +34,9 @@ func (s *Service) Complete(ctx context.Context, pickupID uint64, req CompleteReq
|
||||
if pickupID == 0 || req.SettleAmountCent <= 0 {
|
||||
return nil, ErrInvalidAmount
|
||||
}
|
||||
if req.ProfitAmountCent != nil && *req.ProfitAmountCent < 0 {
|
||||
return nil, ErrInvalidProfit
|
||||
}
|
||||
return s.repo.Complete(ctx, pickupID, req, adminID, meta)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user