修复线下提号后商品仍显示在「已上架」的问题
创建提号时将 listing/account 置为 rented 并记录状态事件,与正常订单锁定一致;完成提号对齐 completed/offline;取消提号恢复上架。补充 migration 修复历史数据。
This commit is contained in:
@@ -33,7 +33,8 @@ func NewRepository(db *gorm.DB) *Repository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create 创建提号订单(状态:提号中)。
|
// Create 创建提号订单(状态:提号中)。
|
||||||
// 锁定 listing.in_transaction,与正常订单互斥;不结算、不动钱包。
|
// 与正常订单支付成功一致:listing 置 rented + in_transaction,从商品管理「已上架」中移出。
|
||||||
|
// 不结算、不动钱包。
|
||||||
func (r *Repository) Create(ctx context.Context, req CreateRequest, adminID uint64, meta auditlog.Meta) (*PickupDTO, error) {
|
func (r *Repository) Create(ctx context.Context, req CreateRequest, adminID uint64, meta auditlog.Meta) (*PickupDTO, error) {
|
||||||
if r == nil || r.db == nil {
|
if r == nil || r.db == nil {
|
||||||
return nil, ErrDependencyUnavailable
|
return nil, ErrDependencyUnavailable
|
||||||
@@ -63,7 +64,7 @@ func (r *Repository) Create(ctx context.Context, req CreateRequest, adminID uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
var account model.GameAccount
|
var account model.GameAccount
|
||||||
if err := tx.First(&account, listing.AccountID).Error; err != nil {
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&account, listing.AccountID).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
priceSnapshot := buildPickupPriceSnapshot(listing, account)
|
priceSnapshot := buildPickupPriceSnapshot(listing, account)
|
||||||
@@ -98,10 +99,28 @@ func (r *Repository) Create(ctx context.Context, req CreateRequest, adminID uint
|
|||||||
}
|
}
|
||||||
createdID = pickup.ID
|
createdID = pickup.ID
|
||||||
|
|
||||||
|
// 锁定商品:status=rented,从「已上架」列表移除,进入「已锁定」
|
||||||
|
fromStatus := listing.Status
|
||||||
listing.InTransaction = true
|
listing.InTransaction = true
|
||||||
|
listing.Status = "rented"
|
||||||
|
account.Status = "rented"
|
||||||
if err := tx.Save(&listing).Error; err != nil {
|
if err := tx.Save(&listing).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := tx.Save(&account).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := listingstatus.AppendTransition(
|
||||||
|
tx,
|
||||||
|
&listing,
|
||||||
|
fromStatus,
|
||||||
|
listingstatus.SourcePickup,
|
||||||
|
listingstatus.ActorAdmin,
|
||||||
|
adminID,
|
||||||
|
"管理员线下提号锁定",
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
bid := pickup.ID
|
bid := pickup.ID
|
||||||
if err := notification.Append(tx, notification.Entry{
|
if err := notification.Append(tx, notification.Entry{
|
||||||
@@ -164,7 +183,9 @@ func (r *Repository) Complete(ctx context.Context, pickupID uint64, req Complete
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var account model.GameAccount
|
var account model.GameAccount
|
||||||
_ = tx.First(&account, pickup.AccountID).Error
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&account, pickup.AccountID).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
pickup.SettleAmountCent = req.SettleAmountCent
|
pickup.SettleAmountCent = req.SettleAmountCent
|
||||||
@@ -178,20 +199,25 @@ func (r *Repository) Complete(ctx context.Context, pickupID uint64, req Complete
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// listing 置 completed:listingLockedForOwnerMutation 已拦截此状态再上架。
|
// listing 置 completed(售出终态,不可再上架),与正常订单 completeAssets 对齐。
|
||||||
fromStatus := listing.Status
|
fromStatus := listing.Status
|
||||||
listing.InTransaction = true
|
listing.InTransaction = false
|
||||||
listing.Status = "completed"
|
listing.Status = "completed"
|
||||||
|
listing.PublishedAt = nil
|
||||||
|
account.Status = "offline"
|
||||||
if err := tx.Save(&listing).Error; err != nil {
|
if err := tx.Save(&listing).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := tx.Save(&account).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := listingstatus.AppendTransition(
|
if err := listingstatus.AppendTransition(
|
||||||
tx,
|
tx,
|
||||||
&listing,
|
&listing,
|
||||||
fromStatus,
|
fromStatus,
|
||||||
listingstatus.SourcePickup,
|
listingstatus.SourcePickup,
|
||||||
listingstatus.ActorAdmin,
|
listingstatus.ActorAdmin,
|
||||||
0,
|
adminID,
|
||||||
"管理员提号完成",
|
"管理员提号完成",
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -244,7 +270,7 @@ func (r *Repository) Complete(ctx context.Context, pickupID uint64, req Complete
|
|||||||
return r.FindByID(ctx, pickupID)
|
return r.FindByID(ctx, pickupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel 取消提号:解锁 listing.in_transaction,账号恢复可租。
|
// Cancel 取消提号:恢复 listing 为 published + 解锁 in_transaction,账号恢复可租。
|
||||||
func (r *Repository) Cancel(ctx context.Context, pickupID uint64, reason string, adminID uint64, meta auditlog.Meta) error {
|
func (r *Repository) Cancel(ctx context.Context, pickupID uint64, reason string, adminID uint64, meta auditlog.Meta) error {
|
||||||
if r == nil || r.db == nil {
|
if r == nil || r.db == nil {
|
||||||
return ErrDependencyUnavailable
|
return ErrDependencyUnavailable
|
||||||
@@ -264,16 +290,38 @@ func (r *Repository) Cancel(ctx context.Context, pickupID uint64, reason string,
|
|||||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&listing, pickup.ListingID).Error; err != nil {
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&listing, pickup.ListingID).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
var account model.GameAccount
|
||||||
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&account, pickup.AccountID).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
pickup.Status = StatusCancelled
|
pickup.Status = StatusCancelled
|
||||||
pickup.CancelledAt = &now
|
pickup.CancelledAt = &now
|
||||||
if err := tx.Save(&pickup).Error; err != nil {
|
if err := tx.Save(&pickup).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// 取消提号:恢复上架,重新出现在商品管理「已上架」
|
||||||
|
fromStatus := listing.Status
|
||||||
listing.InTransaction = false
|
listing.InTransaction = false
|
||||||
|
listing.Status = "published"
|
||||||
|
account.Status = "published"
|
||||||
if err := tx.Save(&listing).Error; err != nil {
|
if err := tx.Save(&listing).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := tx.Save(&account).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := listingstatus.AppendTransition(
|
||||||
|
tx,
|
||||||
|
&listing,
|
||||||
|
fromStatus,
|
||||||
|
listingstatus.SourcePickup,
|
||||||
|
listingstatus.ActorAdmin,
|
||||||
|
adminID,
|
||||||
|
"取消提号恢复上架",
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
bid := pickup.ID
|
bid := pickup.ID
|
||||||
_ = notification.Append(tx, notification.Entry{
|
_ = notification.Append(tx, notification.Entry{
|
||||||
UserID: pickup.OwnerID,
|
UserID: pickup.OwnerID,
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
|
||||||
|
-- 进行中的线下提号:listing 应从「已上架」移出,与正常订单锁定一致(status=rented)
|
||||||
|
UPDATE rental_listings l
|
||||||
|
JOIN admin_pickups p ON p.listing_id = l.id AND p.status = 'picking_up'
|
||||||
|
SET l.status = 'rented',
|
||||||
|
l.in_transaction = 1
|
||||||
|
WHERE l.status = 'published';
|
||||||
|
|
||||||
|
UPDATE game_accounts a
|
||||||
|
JOIN admin_pickups p ON p.account_id = a.id AND p.status = 'picking_up'
|
||||||
|
SET a.status = 'rented'
|
||||||
|
WHERE a.status IN ('published', 'draft', '');
|
||||||
|
|
||||||
|
-- 已完成的线下提号:listing 应为 completed,账号下线(兜底历史数据)
|
||||||
|
UPDATE rental_listings l
|
||||||
|
JOIN admin_pickups p ON p.listing_id = l.id AND p.status = 'completed'
|
||||||
|
SET l.status = 'completed',
|
||||||
|
l.in_transaction = 0,
|
||||||
|
l.published_at = NULL
|
||||||
|
WHERE l.status IN ('published', 'rented');
|
||||||
|
|
||||||
|
UPDATE game_accounts a
|
||||||
|
JOIN admin_pickups p ON p.account_id = a.id AND p.status = 'completed'
|
||||||
|
SET a.status = 'offline'
|
||||||
|
WHERE a.status NOT IN ('offline', 'sealed', 'abnormal');
|
||||||
|
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
|
||||||
|
-- 回滚仅恢复「进行中提号」的锁定(completed 终态不回滚,避免误恢复可租)
|
||||||
|
UPDATE rental_listings l
|
||||||
|
JOIN admin_pickups p ON p.listing_id = l.id AND p.status = 'picking_up'
|
||||||
|
SET l.status = 'published',
|
||||||
|
l.in_transaction = 1
|
||||||
|
WHERE l.status = 'rented';
|
||||||
|
|
||||||
|
UPDATE game_accounts a
|
||||||
|
JOIN admin_pickups p ON p.account_id = a.id AND p.status = 'picking_up'
|
||||||
|
SET a.status = 'published'
|
||||||
|
WHERE a.status = 'rented';
|
||||||
|
|
||||||
|
-- +goose StatementEnd
|
||||||
Reference in New Issue
Block a user