Files
hfb_sys/backend/internal/modules/order/checkout_finalize.go
T
ymlandClaude Opus 4.8 ad70b24fb6 提交2: 发布群核心功能实现
- 实现 EnsureListingConversation: 发布时建发布群,发欢迎语+二维码
- 实现 AddRenterToListingConversation: 付款后拉租客进群
- 实现 RemoveRenterFromListingConversation: 订单终态移出租客
- 实现消息可见性过滤: 租客只能看到 joined_at 之后的消息
- listing 模块注入 ListingChatCreator 接口,发布时建群
- ListingDTO 新增 listing_group_conversation_id 字段
- 付款流程改为拉租客进发布群(替代建订单群)
- 订单完成和取消时自动移出租客
- 创建 chat 适配器实现接口解耦

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 15:57:10 +08:00

143 lines
4.4 KiB
Go

package order
import (
"time"
"hfb_sys/backend/internal/model"
"hfb_sys/backend/internal/modules/chat"
"hfb_sys/backend/internal/modules/notification"
"hfb_sys/backend/internal/modules/wallet"
"gorm.io/gorm"
)
func (r *Repository) finalizeCheckout(tx *gorm.DB, order *model.RentalOrder, checkout *model.OrderCheckout, renterContent string) (*refundAction, error) {
listing, account, err := r.lockListingAccountForOrder(tx, order)
if err != nil {
return nil, err
}
now := time.Now()
order.Status = orderStatusCompleted
order.HandoffStatus = handoffStatusReturned
order.SettlementStatus = settlementStatusSettled
order.SettledAt = &now
order.OwnerSettledAt = &now
archiveAssets(listing, account)
settlement := buildCheckoutSettlement(*order, checkout)
if err := appendCheckoutOwnerIncome(tx, order, settlement); err != nil {
return nil, err
}
refund, err := r.prepareCheckoutRefund(order, settlement)
if err != nil {
return nil, err
}
applyCheckoutSettlement(checkout, settlement)
if err := appendCheckoutCompletedNotifications(tx, order, renterContent); err != nil {
return nil, err
}
if err := saveFinalizedCheckout(tx, order, checkout, listing, account); err != nil {
return nil, err
}
// 订单完成,移出租客
if err := chat.RemoveRenterFromListingConversation(tx, listing.ID, order.RenterID); err != nil {
// 移出失败不阻塞订单完成,仅记录日志
// TODO: 添加日志
}
return refund, nil
}
func appendCheckoutOwnerIncome(tx *gorm.DB, order *model.RentalOrder, settlement checkoutSettlement) error {
orderID := order.ID
var ownerEntries []wallet.Entry
if settlement.OwnerRentIncomeCent > 0 {
ownerEntries = append(ownerEntries, wallet.Entry{
UserID: order.OwnerID,
OrderID: &orderID,
Direction: "in",
AmountCent: settlement.OwnerRentIncomeCent,
BalanceType: "available",
BizType: walletBizOwnerIncome,
BizNo: order.OrderNo,
Remark: "订单结账租金收入",
})
}
if settlement.DepositCompensationCent > 0 {
ownerEntries = append(ownerEntries, wallet.Entry{
UserID: order.OwnerID,
OrderID: &orderID,
Direction: "in",
AmountCent: settlement.DepositCompensationCent,
BalanceType: "available",
BizType: walletBizDepositCompensation,
BizNo: order.OrderNo,
Remark: "订单结账押金赔付",
})
}
if len(ownerEntries) == 0 {
return nil
}
return wallet.AppendEntries(tx, ownerEntries...)
}
func (r *Repository) prepareCheckoutRefund(order *model.RentalOrder, settlement checkoutSettlement) (*refundAction, error) {
renterRefundTotalCent := settlement.RentRefundCent + settlement.DepositRefundCent
if renterRefundTotalCent <= 0 {
return nil, nil
}
return r.prepareRefund(order, renterRefundTotalCent, refundBizCheckout, "结账退款原路退还")
}
func applyCheckoutSettlement(checkout *model.OrderCheckout, settlement checkoutSettlement) {
checkout.RentAmountCent = settlement.ActualRentAmountCent
checkout.OwnerRentAmountCent = settlement.OwnerRentIncomeCent
checkout.PlatformFeeCent = settlement.PlatformFeeCent
checkout.RenterRefundAmountCent = settlement.RenterRefundCent
checkout.OwnerIncomeAmountCent = settlement.OwnerIncomeCent
}
func appendCheckoutCompletedNotifications(tx *gorm.DB, order *model.RentalOrder, renterContent string) error {
orderID := order.ID
return notification.Append(tx,
notification.Entry{
UserID: order.RenterID,
Type: "settlement",
Title: "订单已完成",
Content: renterContent,
BizType: "order",
BizID: &orderID,
},
notification.Entry{
UserID: order.OwnerID,
Type: "settlement",
Title: "订单已完成",
Content: "订单已完成,结账金额已入账。",
BizType: "order",
BizID: &orderID,
},
)
}
func saveFinalizedCheckout(tx *gorm.DB, order *model.RentalOrder, checkout *model.OrderCheckout, listing *model.RentalListing, account *model.GameAccount) error {
if err := tx.Save(order).Error; err != nil {
return err
}
if err := tx.Save(checkout).Error; err != nil {
return err
}
if err := tx.Save(listing).Error; err != nil {
return err
}
return tx.Save(account).Error
}
func hasOpenCheckout(tx *gorm.DB, orderID uint64) (bool, error) {
var count int64
err := tx.Model(&model.OrderCheckout{}).
Where("order_id = ? AND status IN ?", orderID, []string{checkoutStatusSubmitted, checkoutStatusCountered, checkoutStatusAccepted, checkoutStatusDisputed}).
Count(&count).Error
return count > 0, err
}