Files

145 lines
4.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}