fix version 1

This commit is contained in:
2026-09-22 21:15:34 +08:00
parent 9f86366638
commit d63d7e8b3a
277 changed files with 9959 additions and 1514 deletions

View File

@@ -3,8 +3,11 @@ package models
import (
"errors"
"fmt"
"time"
"bsm/full/module/finance/wallet/internal/impl"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/types"
"git.apinb.com/bsm-sdk/core/utils"
"google.golang.org/grpc/codes"
@@ -12,6 +15,13 @@ import (
"gorm.io/gorm"
)
// FillRecordYmd 填充流水的年月日与年月,供当日/当月统计使用
func FillRecordYmd(record *WalletRecord) {
now := time.Now()
record.Ymd = int32(now.Year()*10000 + int(now.Month())*100 + now.Day())
record.Ym = int32(now.Year()*100 + int(now.Month()))
}
// InitData 初始化默认钱包数据
func InitData() error {
var (
@@ -115,10 +125,93 @@ func GetWalletPaymentByIdentity(passport_identity, identity string) (*WalletPaym
return fp, err
}
// UpsetWalletPaymentByIdentity 更新钱包支付记录状态
func UpsetWalletPaymentByIdentity(passport_identity, identity string, CallbackStatus int8, CallbackMsg string) error {
err := impl.DBService.Model(&WalletPayment{}).Where("passport_identity=? and identity=?", passport_identity, identity).Updates(map[string]interface{}{"status": CallbackStatus, "call_back_msg": CallbackMsg}).Error
return err
// MarkPaymentFailed 将钱包支付单标记为支付失败
// 仅允许从「创建(0)/支付中(1)」流转到「失败(-1)」,并限定支付单归属,保证幂等且不可越权。
func MarkPaymentFailed(passport_identity, identity, msg string) error {
res := impl.DBService.Model(&WalletPayment{}).
Where("passport_identity = ? AND identity = ? AND status IN (0,1)", passport_identity, identity).
UpdateColumns(map[string]interface{}{"status": -1, "callback_msg": msg})
if res.Error != nil {
return res.Error
}
if res.RowsAffected == 0 {
return errcode.ErrNotFound(404, "payment")
}
return nil
}
// SettlePaymentSuccess 渠道支付成功结算
// 按商户订单号反查支付单 → 核对渠道金额 → 条件更新为成功(幂等)→
// 同一事务内完成余额入账与流水写入。仅充值单type=1增加余额。
// 只有经过验签解密的渠道回调才允许调用本函数。
func SettlePaymentSuccess(outTradeNo, tradeNo string, amount int64) error {
if outTradeNo == "" {
return errors.New("out_trade_no is empty")
}
if amount <= 0 {
return errors.New("amount is invalid")
}
return impl.DBService.Transaction(func(tx *gorm.DB) error {
payment := new(WalletPayment)
if err := tx.Where("order_no = ?", outTradeNo).First(payment).Error; err != nil {
return err
}
// 金额核对:渠道返回金额必须与本地下单金额一致,防止金额篡改
if payment.Amount != amount {
return fmt.Errorf("payment amount mismatch: local=%d channel=%d", payment.Amount, amount)
}
// 条件更新:重复回调时 RowsAffected 为 0直接返回保证幂等
res := tx.Model(&WalletPayment{}).
Where("identity = ? AND status IN (0,1)", payment.Identity).
UpdateColumns(map[string]interface{}{
"status": 2, // 支付成功
"trade_no": tradeNo,
"callback_msg": "SUCCESS",
})
if res.Error != nil {
return res.Error
}
if res.RowsAffected == 0 {
return nil
}
// 仅充值单需要入账
if payment.Type != 1 {
return nil
}
res = tx.Model(&WalletBasic{}).
Where("identity = ? AND status <> -1", payment.WalletIdentity).
UpdateColumns(map[string]interface{}{
"balance": gorm.Expr("balance + ?", payment.Amount),
"withdrawal_balance": gorm.Expr("withdrawal_balance + ?", payment.Amount),
})
if res.Error != nil {
return res.Error
}
if res.RowsAffected == 0 {
return errors.New("wallet not found")
}
record := &WalletRecord{
WalletIdentity: payment.WalletIdentity,
TransType: 1, // 收入
InTradeNo: outTradeNo,
OutTradeNo: tradeNo,
Money: payment.Amount,
TradeType: 1, // 充值
PayChannel: payment.PayChannel,
PayType: payment.PayType,
}
record.Identity = utils.UUID()
record.PassportID = payment.PassportID
record.PassportIdentity = payment.PassportIdentity
FillRecordYmd(record)
return tx.Create(record).Error
})
}
// CreatePaymentRecord 创建支付记录
@@ -132,57 +225,74 @@ func CreateTradeRecord(fr *WalletRecord) error {
}
// ChargeWallet 为钱包充值
// 余额变更使用条件增量更新,流水写入与余额变更在同一事务内完成。
func ChargeWallet(passport_id uint, passport_identity, order_no string, paySource int32, amount int64) error {
if amount <= 0 {
return errors.New("amount is invalid")
}
wallet, err := GetWalletByPassportIdentity(passport_id, passport_identity)
if err != nil {
return err
}
// 更新钱包余额
data := map[string]interface{}{
"balance": wallet.Balance + amount,
"withdrawal_balance": wallet.WithdrawalBalance + amount,
}
err = impl.DBService.Model(&WalletBasic{}).Where("identity=?", wallet.Identity).UpdateColumns(data).Error
if err != nil {
return err
} else {
return impl.DBService.Transaction(func(tx *gorm.DB) error {
res := tx.Model(&WalletBasic{}).
Where("identity = ? AND status <> -1", wallet.Identity).
UpdateColumns(map[string]interface{}{
"balance": gorm.Expr("balance + ?", amount),
"withdrawal_balance": gorm.Expr("withdrawal_balance + ?", amount),
})
if res.Error != nil {
return res.Error
}
if res.RowsAffected == 0 {
return errors.New("wallet not found")
}
// 创建交易记录
record := &WalletRecord{
WalletIdentity: wallet.Identity,
TransType: 1, // 充值类型
TransType: 1, // 收入
InTradeNo: order_no,
Money: amount,
TradeType: 1, // 收入类型
TradeType: 1, // 充值
PayChannel: int8(paySource),
PayType: "CHARGE",
}
record.Identity = utils.UUID()
record.PassportID = passport_id
record.PassportIdentity = passport_identity
CreateTradeRecord(record)
}
return nil
FillRecordYmd(record)
return tx.Create(record).Error
})
}
// FindWalletRecords 查询钱包交易记录
func FindWalletRecords(passportIdentity, start, end string, transType, tradeType, page, pageSize int64) ([]*WalletRecord, int64, error) {
list := make([]*WalletRecord, 0)
tx := impl.DBService.Where("passport_identity = ?", passportIdentity)
tx := impl.DBService.Model(&WalletRecord{}).Where("passport_identity = ?", passportIdentity)
// 添加时间范围过滤
if start != "" {
tx.Where("created_at >= ?", start)
tx = tx.Where("created_at >= ?", start)
}
if end != "" {
tx.Where("created_at <= ?", end)
tx = tx.Where("created_at <= ?", end)
}
// 添加交易类型过滤
if transType != 0 {
tx.Where("trans_type = ?", transType)
tx = tx.Where("trans_type = ?", transType)
}
if tradeType != 0 {
tx.Where("trade_type = ?", tradeType)
tx = tx.Where("trade_type = ?", tradeType)
}
// 总数使用独立的 Count 查询,避免用当页条数充当总数
var total int64
if err := tx.Count(&total).Error; err != nil {
return nil, 0, err
}
// 分页查询
@@ -193,5 +303,5 @@ func FindWalletRecords(passportIdentity, start, end string, transType, tradeType
}
return nil, 0, nil
}
return list, res.RowsAffected, nil
return list, total, nil
}