chore(init): 初始化解忧商行项目并完成 Phase 0 基座
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
package order
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"hfb_sys/backend/internal/model"
|
||||
"hfb_sys/backend/internal/modules/distribution"
|
||||
"hfb_sys/backend/internal/modules/invitation"
|
||||
"hfb_sys/backend/internal/modules/notification"
|
||||
"hfb_sys/backend/internal/modules/rentergrowth"
|
||||
"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
|
||||
if !isPlatformSettlementOrder(*order) {
|
||||
order.OwnerSettledAt = &now
|
||||
}
|
||||
if err := completeAssets(tx, listing, account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
settlement := buildCheckoutSettlement(*order, checkout)
|
||||
if settlement.ShortfallCent > 0 {
|
||||
return nil, ErrCheckoutDepositShortfall
|
||||
}
|
||||
if err := appendCheckoutOwnerIncome(tx, order, listing, settlement); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refund, err := r.prepareCheckoutRefund(order, settlement)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
applyCheckoutSettlement(checkout, settlement)
|
||||
order.ActualCoinConsumedM = settlement.CoinConsumedM
|
||||
order.ActualPureCoinAmountCent = settlement.PureCoinAmountCent
|
||||
order.ActualPureCoinDiscountCent = settlement.PureCoinDiscountCent
|
||||
order.ActualExtraItemAmountCent = settlement.ExtraItemAmountCent
|
||||
if err := appendCheckoutCompletedNotifications(tx, order, renterContent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if order.DistributionSiteID == nil || *order.DistributionSiteID == 0 {
|
||||
if err := rentergrowth.AwardOrderCompleted(tx, order, settlement.PureCoinAmountCent, rentergrowth.SourceOrderCompleted); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := saveFinalizedCheckout(tx, order, checkout, listing, account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if order.DistributionSiteID != nil && *order.DistributionSiteID > 0 {
|
||||
if err := distribution.ApplyRefundTx(tx, order.ID, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// 返利必须按本次实际结算的平台利润计算,不能用订单创建时的预收毛利快照。
|
||||
actualPlatformProfitCent := settlement.PlatformFeeCent
|
||||
_ = invitation.ProcessOrderInvitationRebateWithOptionsTx(tx, order.ID, invitation.OrderRebateOptions{
|
||||
GrossProfitCent: &actualPlatformProfitCent,
|
||||
RateReferenceAt: now,
|
||||
SettledAt: now,
|
||||
})
|
||||
|
||||
return refund, nil
|
||||
}
|
||||
|
||||
func ownerWalletSiteID(listing *model.RentalListing) uint64 {
|
||||
if listing != nil && listing.DistributionSiteID != nil {
|
||||
return *listing.DistributionSiteID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func appendCheckoutOwnerIncome(tx *gorm.DB, order *model.RentalOrder, listing *model.RentalListing, settlement checkoutSettlement) error {
|
||||
if isPlatformSettlementOrder(*order) {
|
||||
amountCent := settlement.OwnerRentIncomeCent + settlement.DepositCompensationCent
|
||||
order.OfflineSettlementAmountCent = amountCent
|
||||
if amountCent > 0 {
|
||||
order.OfflineSettlementStatus = offlineSettlementStatusPending
|
||||
} else {
|
||||
order.OfflineSettlementStatus = offlineSettlementStatusNone
|
||||
}
|
||||
return nil
|
||||
}
|
||||
orderID := order.ID
|
||||
siteID := ownerWalletSiteID(listing)
|
||||
var ownerEntries []wallet.Entry
|
||||
if settlement.OwnerRentIncomeCent > 0 {
|
||||
ownerEntries = append(ownerEntries, wallet.Entry{
|
||||
UserID: order.OwnerID,
|
||||
DistributionSiteID: siteID,
|
||||
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,
|
||||
DistributionSiteID: siteID,
|
||||
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
|
||||
// 押金暂扣:若订单已暂扣,拦截本次结账中应退还租客的押金部分挂起不退,其余(租金退款)照常原路退。
|
||||
actualRefundCent := order.ApplyDepositHold(renterRefundTotalCent, settlement.DepositRefundCent)
|
||||
if actualRefundCent <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return r.prepareRefund(order, actualRefundCent, refundBizCheckout, "结账退款原路退还")
|
||||
}
|
||||
|
||||
func applyCheckoutSettlement(checkout *model.OrderCheckout, settlement checkoutSettlement) {
|
||||
checkout.RentAmountCent = settlement.ActualRentAmountCent
|
||||
checkout.OwnerRentAmountCent = settlement.OwnerRentIncomeCent
|
||||
checkout.PlatformFeeCent = settlement.PlatformFeeCent
|
||||
checkout.PureCoinAmountCent = settlement.PureCoinAmountCent
|
||||
checkout.PureCoinDiscountCent = settlement.PureCoinDiscountCent
|
||||
checkout.PureCoinPayableCent = settlement.PureCoinPayableCent
|
||||
checkout.RenterRefundAmountCent = settlement.RenterRefundCent
|
||||
checkout.OwnerIncomeAmountCent = settlement.OwnerIncomeCent
|
||||
checkout.ShortfallCent = settlement.ShortfallCent
|
||||
checkout.OvershootAmountCent = settlement.OvershootAmountCent
|
||||
}
|
||||
|
||||
func appendCheckoutCompletedNotifications(tx *gorm.DB, order *model.RentalOrder, renterContent string) error {
|
||||
orderID := order.ID
|
||||
if isPlatformSettlementOrder(*order) {
|
||||
if err := notification.Append(tx, notification.Entry{
|
||||
UserID: order.RenterID,
|
||||
Type: "settlement",
|
||||
Title: "订单已完成",
|
||||
Content: renterContent,
|
||||
BizType: "order",
|
||||
BizID: &orderID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if order.OfflineSettlementAmountCent > 0 {
|
||||
return appendManagedAdminNotification(tx, *order, "settlement", "代管订单待线下结算", "订单已完成,请按线下流程向卖家转出结算金额并回填状态。")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user