145 lines
4.9 KiB
Go
145 lines
4.9 KiB
Go
package distribution
|
||
|
||
import (
|
||
"strings"
|
||
"sync"
|
||
|
||
"hfb_jysh/backend/internal/model"
|
||
|
||
"gorm.io/gorm"
|
||
"gorm.io/gorm/clause"
|
||
)
|
||
|
||
// 货架下架来源标记:写入 distribution_site_accounts.display_disabled_by。
|
||
// 所有站点共用同一批已审核商品,因此「上架成功后自动铺货」只允许恢复系统下架的行,
|
||
// 站长人工下架(选品)必须保持不动;两个来源必须可区分,否则同步会覆盖人工操作。
|
||
const (
|
||
displayDisabledByOperator = "operator"
|
||
displayDisabledBySystem = "system"
|
||
)
|
||
|
||
// shelfDisabledBySystem 判断某条下架行是否允许被自动铺货恢复。
|
||
// 历史数据没有来源标记(空串),按系统下架处理,使存量被误下架的商品可自动恢复。
|
||
func shelfDisabledBySystem(disabledBy string) bool {
|
||
switch strings.TrimSpace(disabledBy) {
|
||
case "", displayDisabledBySystem:
|
||
return true
|
||
default:
|
||
return false
|
||
}
|
||
}
|
||
|
||
// explicitZeroSiteColumns 收集建站请求里「显式传了零值」但模型带 default 标签的列。
|
||
//
|
||
// GORM 对带 default 标签的字段会忽略零值(false/0)并回落数据库默认值,
|
||
// 例如 display_account_enabled 默认 true:站长建站时明确关闭自动展示,
|
||
// 若不显式补写就会出现「配置被静默改成开启」。
|
||
func explicitZeroSiteColumns(req CreateSiteRequest) map[string]any {
|
||
columns := map[string]any{}
|
||
if req.DisplayAccountEnabled != nil && !*req.DisplayAccountEnabled {
|
||
columns["display_account_enabled"] = false
|
||
}
|
||
if req.TaxEnabled != nil && !*req.TaxEnabled {
|
||
columns["tax_enabled"] = false
|
||
}
|
||
if req.SettlementHoldDays != nil && *req.SettlementHoldDays == 0 {
|
||
columns["settlement_hold_days"] = 0
|
||
}
|
||
return columns
|
||
}
|
||
|
||
// displayDisabledBy 把站长人工上下架动作翻译成下架来源标记:下架记 operator,恢复展示则清空标记。
|
||
func displayDisabledBy(status string) string {
|
||
if strings.TrimSpace(status) == "enabled" {
|
||
return ""
|
||
}
|
||
return displayDisabledByOperator
|
||
}
|
||
|
||
// autoDisplaySiteIDs 返回开启「自动展示账号」的可用站点 ID。
|
||
// display_account_enabled=false 表示该站不参与自动铺货,需要站长手动同步。
|
||
func autoDisplaySiteIDs(tx *gorm.DB) ([]uint64, error) {
|
||
var ids []uint64
|
||
err := tx.Model(&model.DistributionSite{}).
|
||
Where("status = ? AND display_account_enabled = ?", "active", true).
|
||
Pluck("id", &ids).Error
|
||
return ids, err
|
||
}
|
||
|
||
// shelfTablePresent 探测分销货架表是否存在,仅首次访问查询 information_schema,
|
||
// 兼容尚未执行分销迁移的旧环境,避免商品上架事务因缺表而失败。
|
||
var (
|
||
shelfTablePresent bool
|
||
shelfTableCheckOnce sync.Once
|
||
)
|
||
|
||
func shelfTablesPresent(tx *gorm.DB) bool {
|
||
shelfTableCheckOnce.Do(func() {
|
||
shelfTablePresent = tx.Migrator().HasTable(&model.DistributionSiteAccount{})
|
||
})
|
||
return shelfTablePresent
|
||
}
|
||
|
||
// EnsureListingShelvedTx 在商品审核通过(published + approved)后自动铺到各分站货架。
|
||
//
|
||
// 站点货架与主站共用同一套货源(rental_listings / game_accounts),因此无需站长手动
|
||
// 「一键同步」才能看到新商品:
|
||
// - 缺少货架行:新增 enabled 行,加价保持 0(等于主站原价,站长可再自主加价)
|
||
// - 已有系统下架/历史无标记的下架行:恢复为 enabled
|
||
// - 已有站长人工下架的行:保持 disabled,尊重人工选品
|
||
//
|
||
// 本方法在调用方事务内执行,幂等:重复调用不会产生重复货架行。
|
||
func (r *Repository) EnsureListingShelvedTx(tx *gorm.DB, listingID, accountID uint64) error {
|
||
if tx == nil || listingID == 0 || accountID == 0 {
|
||
return nil
|
||
}
|
||
if !shelfTablesPresent(tx) {
|
||
return nil
|
||
}
|
||
siteIDs, err := autoDisplaySiteIDs(tx)
|
||
if err != nil || len(siteIDs) == 0 {
|
||
return err
|
||
}
|
||
|
||
var existing []model.DistributionSiteAccount
|
||
if err := tx.Where("site_id IN ? AND account_id = ?", siteIDs, accountID).Find(&existing).Error; err != nil {
|
||
return err
|
||
}
|
||
bySite := make(map[uint64]model.DistributionSiteAccount, len(existing))
|
||
for _, row := range existing {
|
||
bySite[row.SiteID] = row
|
||
}
|
||
|
||
toInsert := make([]model.DistributionSiteAccount, 0, len(siteIDs))
|
||
for _, siteID := range siteIDs {
|
||
row, ok := bySite[siteID]
|
||
if !ok {
|
||
toInsert = append(toInsert, model.DistributionSiteAccount{
|
||
SiteID: siteID,
|
||
AccountID: accountID,
|
||
ListingID: listingID,
|
||
DisplayStatus: "enabled",
|
||
})
|
||
continue
|
||
}
|
||
updates := map[string]any{}
|
||
if row.ListingID != listingID {
|
||
updates["listing_id"] = listingID
|
||
}
|
||
if row.DisplayStatus != "enabled" && shelfDisabledBySystem(row.DisplayDisabledBy) {
|
||
updates["display_status"] = "enabled"
|
||
updates["display_disabled_by"] = ""
|
||
}
|
||
if len(updates) == 0 {
|
||
continue
|
||
}
|
||
if err := tx.Model(&model.DistributionSiteAccount{}).Where("id = ?", row.ID).Updates(updates).Error; err != nil {
|
||
return err
|
||
}
|
||
}
|
||
if len(toInsert) == 0 {
|
||
return nil
|
||
}
|
||
return tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&toInsert).Error
|
||
}
|