Files
hfb_sys/backend/internal/modules/listing/dto.go
T
yml2213 5840b7da40 fix(listing): 外部上传按后台实时配置归类皮肤与折算消耗品,并补齐新增消耗品字段
- 皮肤归类:删除代码内硬编码的 externalSkinCatalog 快照,改为读取 listing.publish_options。
  修复运营在后台新增皮肤后,上传的商品被归入 other 的问题(详情页显示“其他皮肤”、
  按刀皮/金皮/红皮/武器皮分组筛选搜不到)
- 消耗品折算:externalResources 改为按 publish_options.quantity_items 生成条目与单价,
  修复 AWM 子弹按 0.6 元/发旧价折算等价格漂移(线上已调为 0.7 元/发)
- 上传契约:ExternalUploadInventory 新增 level6Ammo/barrettAmmo/gridCard9/redPacket45/
  coffeeBean/bulletPart 六个字段,字段名与消耗品 key 对齐;后续后台再新增消耗品,
  按同样 key 登记即可被上传链路识别
- 数量为 0 的消耗品不再写入资源清单,与网站自身发布流程(filter quantity > 0)保持一致
- 客服调整外部商品同样改用实时单价;Service 增加 externalPublishOptions 统一读配置并带默认值
- 验证:新增回归测试 TestExternalAccountToCreateRequestFollowsPublishOptions;
  并以线上 mobile-home-config 的真实配置 + 上传工具产出的 JSON 做跨仓库联调,
  确认“重塑→近战皮肤、骇爪-骇客少女→干员金皮”,9 项消耗品全部按线上单价生成
2026-10-11 17:44:27 +08:00

281 lines
11 KiB
Go

package listing
import (
"encoding/json"
"strings"
"time"
"hfb_sys/backend/internal/auditlog"
)
type ListingDTO struct {
ID uint64 `json:"id"`
ListingNo string `json:"listing_no"`
AccountID uint64 `json:"account_id"`
OwnerID uint64 `json:"owner_id"`
OwnerPhone string `json:"owner_phone,omitempty"`
OwnerNickname string `json:"owner_nickname,omitempty"`
SourceChannel string `json:"source_channel,omitempty"`
IsExternalUpload bool `json:"is_external_upload"`
Title string `json:"title"`
Description string `json:"description"`
GameName string `json:"game_name"`
ServerRegion string `json:"server_region"`
LoginPlatform string `json:"login_platform"`
RankLevel string `json:"rank_level"`
HafCoinAmount int64 `json:"haf_coin_amount"`
AssetSummary map[string]any `json:"asset_summary,omitempty"`
ScreenshotURLS []string `json:"screenshot_urls"`
CoverURL string `json:"cover_url"`
PriceCent int64 `json:"price_cent"`
DepositAmountCent int64 `json:"deposit_amount_cent"`
IsAccelerated bool `json:"is_accelerated_sale"`
InTransaction bool `json:"in_transaction"`
Status string `json:"status"`
ReviewStatus string `json:"review_status"`
HandoffMode string `json:"handoff_mode"`
SettlementMode string `json:"settlement_mode"`
ManagedAdminID *uint64 `json:"managed_admin_id,omitempty"`
ReviewReason string `json:"review_reason"`
PublishedAt *time.Time `json:"published_at"`
DistributionSiteID *uint64 `json:"distribution_site_id,omitempty"`
DistributionSiteCode string `json:"distribution_site_code,omitempty"`
DistributionSiteName string `json:"distribution_site_name,omitempty"`
ListingGroupConversationID uint64 `json:"listing_group_conversation_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type CreateRequest struct {
Title string `json:"title" binding:"required"`
Description string `json:"description"`
ServerRegion string `json:"server_region" binding:"required"`
LoginPlatform string `json:"login_platform"`
RankLevel string `json:"rank_level"`
HafCoinAmount int64 `json:"haf_coin_amount"`
AssetSummary map[string]any `json:"asset_summary"`
ScreenshotURLS []string `json:"screenshot_urls"`
PriceCent int64 `json:"price_cent"`
DepositAmountCent int64 `json:"deposit_amount_cent"`
AgreedVirtualAssetSale bool `json:"agreed_virtual_asset_sale"`
AgreedSellerAgreement bool `json:"agreed_seller_agreement"`
}
type UpdateRequest = CreateRequest
type ReviewRequest struct {
Reason string `json:"reason"`
}
type AdminPriceAdjustRequest struct {
BuyerRatio float64 `json:"buyer_ratio"`
BuyerTotalPriceCent int64 `json:"buyer_total_price_cent"`
Reason string `json:"reason"`
}
const (
ExternalAdjustModeDiscountAmount = "discount_amount"
ExternalAdjustModeSaleRatio = "sale_ratio"
)
type AdminExternalListingAdjustRequest struct {
HafCoinAmount int64 `json:"haf_coin_amount" binding:"required,min=1"`
AWMBullets int `json:"awm_bullets" binding:"min=0,max=1000000"`
Level6Helmets int `json:"level6_helmets" binding:"min=0,max=1000000"`
Level6Armor int `json:"level6_armor" binding:"min=0,max=1000000"`
DepositAmountCent int64 `json:"deposit_amount_cent" binding:"min=0"`
AdjustMode string `json:"adjust_mode" binding:"required,oneof=discount_amount sale_ratio"`
DiscountAmountCent int64 `json:"discount_amount_cent" binding:"min=0"`
SaleRatio float64 `json:"sale_ratio" binding:"min=0"`
Reason string `json:"reason" binding:"required,max=255"`
}
type TransferOwnerRequest struct {
TargetUserID uint64 `json:"target_user_id" binding:"required"`
Reason string `json:"reason"`
}
type AdminUpdateScreenshotsRequest struct {
ScreenshotURLs []string `json:"screenshot_urls" binding:"required,min=1"`
ScreenshotGroups map[string]any `json:"screenshot_groups"`
}
type AdminListQuery struct {
OwnerID uint64
Keyword string
Status string
ReviewStatus string
Insurance []string
Stamina []string
Load []string
SkinGroup []string
SkinName []string
MinCoin *float64
MaxCoin *float64
MinDeposit *float64
MaxDeposit *float64
Limit int
Page int
PageSize int
}
type AdminListResult struct {
Items []ListingDTO `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
type PublicListQuery struct {
Page int
PageSize int
Keyword string
Sort string
Zone string
Server []string
Region []string
LoginMethod []string
Rank []string
Insurance []string
Stamina []string
Load []string
SkinGroup []string
SkinName []string
MinCoin *float64
MaxCoin *float64
MinRatio *float64
MaxRatio *float64
MinPrice *float64
MaxPrice *float64
MinDeposit *float64
MaxDeposit *float64
MinTotal *float64
MaxTotal *float64
MinFireLevel *float64
MaxFireLevel *float64
MinDailyLoss *float64
MaxDailyLoss *float64
MinSecretKD *float64
MaxSecretKD *float64
ResourceRanges map[string]NumberRange
DistributionSiteCode string
}
type NumberRange struct {
Min *float64
Max *float64
}
type PublicListResult struct {
Items []ListingDTO `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
ZoneCounts map[string]int64 `json:"zone_counts"`
}
type AdminActionRequest struct {
Reason string `json:"reason" binding:"required"`
}
type AuditMeta = auditlog.Meta
type ExternalUploadRequest struct {
UploadTime int64 `json:"uploadTime"`
UploaderName string `json:"uploaderName"`
Uploaderame string `json:"uploaderame"`
SourceChannel string `json:"sourceChannel"`
SourceChannelSnake string `json:"source_channel"`
Data json.RawMessage `json:"data"`
}
func (r ExternalUploadRequest) normalizedUploaderName() string {
if name := strings.TrimSpace(r.UploaderName); name != "" {
return name
}
return strings.TrimSpace(r.Uploaderame)
}
func (r ExternalUploadRequest) normalizedSourceChannel() string {
if channel := normalizeExternalSourceChannel(r.SourceChannel); channel != "" {
return channel
}
return normalizeExternalSourceChannel(r.SourceChannelSnake)
}
type ExternalAccountData struct {
LoginMethod string `json:"loginMethod"`
Rank string `json:"rank"`
Level int `json:"level"`
SafeSlots int `json:"safeSlots"`
SecretKD float64 `json:"secretKD"`
DailyLossM float64 `json:"dailyLossM"`
Deposit float64 `json:"deposit"`
BanRecord string `json:"banRecord"`
CommonRegion string `json:"commonRegion"`
ContactPhone string `json:"contactPhone"`
OwnerOnlineTime string `json:"ownerOnlineTime"`
Remark string `json:"remark"`
Currency ExternalUploadCurrency `json:"currency"`
DailyConsumption ExternalDailyConsumption `json:"dailyConsumption"`
Inventory ExternalUploadInventory `json:"inventory"`
}
type ExternalUploadCurrency struct {
HafuCoin float64 `json:"hafuCoin"`
RecycleRatio float64 `json:"recycleRatio"`
RecycleRent float64 `json:"recycleRent"`
}
type ExternalDailyConsumption struct {
Stamina int `json:"stamina"`
Weight int `json:"weight"`
}
type ExternalUploadInventory struct {
AWMBullets int `json:"awmBullets"`
Level6Helmets int `json:"level6Helmets"`
Level6Armor int `json:"level6Armor"`
Skins []string `json:"skins"`
// 以下字段名与 publish_options.quantity_items 的 key 对齐:
// 后台新增消耗品时,只要按同样的 key 在此登记即可被上传链路识别并计入价格。
Level6Ammo int `json:"level6Ammo"`
BarrettAmmo int `json:"barrettAmmo"`
GridCard9 int `json:"gridCard9"`
RedPacket45 int `json:"redPacket45"`
CoffeeBean int `json:"coffeeBean"`
BulletPart int `json:"bulletPart"`
}
type ExternalUploadMeta struct {
IP string
UserAgent string
RawPayload []byte
}
type ExternalUploadResult struct {
Index int `json:"index"`
ListingID uint64 `json:"listing_id,omitempty"`
ListingNo string `json:"listing_no,omitempty"`
AccountID uint64 `json:"account_id,omitempty"`
ListingGroupConversationID uint64 `json:"listing_group_conversation_id,omitempty"`
Status string `json:"status,omitempty"`
ReviewStatus string `json:"review_status,omitempty"`
Error string `json:"error,omitempty"`
}
type ExternalUploadResponse struct {
ListingID uint64 `json:"listing_id,omitempty"`
ListingNo string `json:"listing_no,omitempty"`
AccountID uint64 `json:"account_id,omitempty"`
ListingGroupConversationID uint64 `json:"listing_group_conversation_id,omitempty"`
Status string `json:"status,omitempty"`
ReviewStatus string `json:"review_status,omitempty"`
Total int `json:"total"`
Success int `json:"success"`
Failed int `json:"failed"`
Items []ExternalUploadResult `json:"items,omitempty"`
}